001: /*
002: * Copyright 2002-2007 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.springframework.aop.aspectj.annotation;
018:
019: import java.lang.reflect.Field;
020: import java.lang.reflect.Method;
021: import java.util.LinkedList;
022: import java.util.List;
023:
024: import org.aopalliance.aop.Advice;
025: import org.aspectj.lang.JoinPoint;
026: import org.aspectj.lang.ProceedingJoinPoint;
027: import org.aspectj.lang.annotation.AfterReturning;
028: import org.aspectj.lang.annotation.AfterThrowing;
029: import org.aspectj.lang.annotation.DeclareParents;
030: import org.aspectj.lang.annotation.Pointcut;
031:
032: import org.springframework.aop.Advisor;
033: import org.springframework.aop.MethodBeforeAdvice;
034: import org.springframework.aop.aspectj.AbstractAspectJAdvice;
035: import org.springframework.aop.aspectj.AspectJAfterAdvice;
036: import org.springframework.aop.aspectj.AspectJAfterReturningAdvice;
037: import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice;
038: import org.springframework.aop.aspectj.AspectJAroundAdvice;
039: import org.springframework.aop.aspectj.AspectJExpressionPointcut;
040: import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
041: import org.springframework.aop.aspectj.DeclareParentsAdvisor;
042: import org.springframework.aop.framework.AopConfigException;
043: import org.springframework.aop.support.DefaultPointcutAdvisor;
044: import org.springframework.core.annotation.AnnotationUtils;
045: import org.springframework.util.ReflectionUtils;
046: import org.springframework.util.StringUtils;
047:
048: /**
049: * Factory that can create Spring AOP Advisors given AspectJ classes from
050: * classes honoring the AspectJ 5 annotation syntax, using reflection to
051: * invoke the corresponding advice methods.
052: *
053: * @author Rod Johnson
054: * @author Adrian Colyer
055: * @author Juergen Hoeller
056: * @since 2.0
057: */
058: public class ReflectiveAspectJAdvisorFactory extends
059: AbstractAspectJAdvisorFactory {
060:
061: public List<Advisor> getAdvisors(
062: MetadataAwareAspectInstanceFactory maaif) {
063: final Class<?> aspectClass = maaif.getAspectMetadata()
064: .getAspectClass();
065: final String aspectName = maaif.getAspectMetadata()
066: .getAspectName();
067: validate(aspectClass);
068:
069: // We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
070: // so that it will only instantiate once.
071: final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(
072: maaif);
073:
074: final List<Advisor> advisors = new LinkedList<Advisor>();
075: ReflectionUtils.doWithMethods(aspectClass,
076: new ReflectionUtils.MethodCallback() {
077: public void doWith(Method method)
078: throws IllegalArgumentException {
079: // Exclude pointcuts
080: if (AnnotationUtils.getAnnotation(method,
081: Pointcut.class) == null) {
082: Advisor advisor = getAdvisor(method,
083: lazySingletonAspectInstanceFactory,
084: advisors.size(), aspectName);
085: if (advisor != null) {
086: advisors.add(advisor);
087: }
088: }
089: }
090: });
091:
092: // If it's a per target aspect, emit the dummy instantiating aspect.
093: if (!advisors.isEmpty()
094: && lazySingletonAspectInstanceFactory
095: .getAspectMetadata().isLazilyInstantiated()) {
096: Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(
097: lazySingletonAspectInstanceFactory);
098: advisors.add(0, instantiationAdvisor);
099: }
100:
101: // Find introduction fields.
102: for (Field field : aspectClass.getDeclaredFields()) {
103: Advisor advisor = getDeclareParentsAdvisor(field);
104: if (advisor != null) {
105: advisors.add(advisor);
106: }
107: }
108:
109: return advisors;
110: }
111:
112: /**
113: * Build a {@link org.springframework.aop.aspectj.DeclareParentsAdvisor}
114: * for the given introduction field.
115: * <p>Resulting Advisors will need to be evaluated for targets.
116: * @param introductionField the field to introspect
117: * @return <code>null</code> if not an Advisor
118: */
119: private Advisor getDeclareParentsAdvisor(Field introductionField) {
120: DeclareParents declareParents = (DeclareParents) introductionField
121: .getAnnotation(DeclareParents.class);
122: if (declareParents == null) {
123: // Not an introduction field
124: return null;
125: }
126:
127: if (DeclareParents.class.equals(declareParents.defaultImpl())) {
128: // This is what comes back if it wasn't set. This seems bizarre...
129: // TODO this restriction possibly should be relaxed
130: throw new IllegalStateException(
131: "defaultImpl must be set on DeclareParents");
132: }
133:
134: return new DeclareParentsAdvisor(introductionField.getType(),
135: declareParents.value(), declareParents.defaultImpl());
136: }
137:
138: public Advisor getAdvisor(Method candidateAdviceMethod,
139: MetadataAwareAspectInstanceFactory aif,
140: int declarationOrderInAspect, String aspectName) {
141:
142: validate(aif.getAspectMetadata().getAspectClass());
143:
144: AspectJExpressionPointcut ajexp = getPointcut(
145: candidateAdviceMethod, aif.getAspectMetadata()
146: .getAspectClass());
147: if (ajexp == null) {
148: return null;
149: }
150: return new InstantiationModelAwarePointcutAdvisorImpl(this ,
151: ajexp, aif, candidateAdviceMethod,
152: declarationOrderInAspect, aspectName);
153: }
154:
155: private AspectJExpressionPointcut getPointcut(
156: Method candidateAdviceMethod, Class<?> candidateAspectClass) {
157: AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory
158: .findAspectJAnnotationOnMethod(candidateAdviceMethod);
159: if (aspectJAnnotation == null) {
160: return null;
161: }
162: AspectJExpressionPointcut ajexp = new AspectJExpressionPointcut(
163: candidateAspectClass, new String[0], new Class[0]);
164: ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
165: return ajexp;
166: }
167:
168: public Advice getAdvice(Method candidateAdviceMethod,
169: AspectJExpressionPointcut ajexp,
170: MetadataAwareAspectInstanceFactory aif,
171: int declarationOrderInAspect, String aspectName) {
172:
173: Class<?> candidateAspectClass = aif.getAspectMetadata()
174: .getAspectClass();
175: validate(candidateAspectClass);
176:
177: AspectJAnnotation<?> aspectJAnnotation = AbstractAspectJAdvisorFactory
178: .findAspectJAnnotationOnMethod(candidateAdviceMethod);
179: if (aspectJAnnotation == null) {
180: return null;
181: }
182:
183: // If we get here, we know we have an AspectJ method.
184: // Check that it's an AspectJ-annotated class
185: if (!isAspect(candidateAspectClass)) {
186: throw new AopConfigException(
187: "Advice must be declared inside an aspect type: "
188: + "Offending method '"
189: + candidateAdviceMethod + "' in class ["
190: + candidateAspectClass.getName() + "]");
191: }
192:
193: if (logger.isDebugEnabled()) {
194: logger.debug("Found AspectJ method: "
195: + candidateAdviceMethod);
196: }
197:
198: AbstractAspectJAdvice springAdvice;
199:
200: switch (aspectJAnnotation.getAnnotationType()) {
201: case AtBefore:
202: springAdvice = new AspectJMethodBeforeAdvice(
203: candidateAdviceMethod, ajexp, aif);
204: break;
205: case AtAfter:
206: springAdvice = new AspectJAfterAdvice(
207: candidateAdviceMethod, ajexp, aif);
208: break;
209: case AtAfterReturning:
210: springAdvice = new AspectJAfterReturningAdvice(
211: candidateAdviceMethod, ajexp, aif);
212: AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation
213: .getAnnotation();
214: if (StringUtils.hasText(afterReturningAnnotation
215: .returning())) {
216: springAdvice.setReturningName(afterReturningAnnotation
217: .returning());
218: }
219: break;
220: case AtAfterThrowing:
221: springAdvice = new AspectJAfterThrowingAdvice(
222: candidateAdviceMethod, ajexp, aif);
223: AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation
224: .getAnnotation();
225: if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
226: springAdvice.setThrowingName(afterThrowingAnnotation
227: .throwing());
228: }
229: break;
230: case AtAround:
231: springAdvice = new AspectJAroundAdvice(
232: candidateAdviceMethod, ajexp, aif);
233: break;
234: case AtPointcut:
235: if (logger.isDebugEnabled()) {
236: logger.debug("Processing pointcut '"
237: + candidateAdviceMethod.getName() + "'");
238: }
239: return null;
240: default:
241: throw new UnsupportedOperationException(
242: "Unsupported advice type on method "
243: + candidateAdviceMethod);
244: }
245:
246: // Now to configure the advice...
247: springAdvice.setAspectName(aspectName);
248: springAdvice.setDeclarationOrder(declarationOrderInAspect);
249: String[] argNames = getArgumentNames(candidateAdviceMethod);
250: if (argNames != null) {
251: springAdvice.setArgumentNamesFromStringArray(argNames);
252: }
253: springAdvice.calculateArgumentBindings();
254: return springAdvice;
255: }
256:
257: private String[] getArgumentNames(Method forMethod) {
258: String[] argNames = this .parameterNameDiscoverer
259: .getParameterNames(forMethod);
260: if (argNames != null) {
261: if (forMethod.getParameterTypes().length == argNames.length + 1) {
262: // May need to add implicit join point arg name...
263: Class firstArgType = forMethod.getParameterTypes()[0];
264: if (firstArgType == JoinPoint.class
265: || firstArgType == ProceedingJoinPoint.class
266: || firstArgType == JoinPoint.StaticPart.class) {
267: String[] oldNames = argNames;
268: argNames = new String[oldNames.length + 1];
269: argNames[0] = "THIS_JOIN_POINT";
270: System.arraycopy(oldNames, 0, argNames, 1,
271: oldNames.length);
272: }
273: }
274: }
275: return argNames;
276: }
277:
278: /**
279: * Synthetic advisor that instantiates the aspect.
280: * Triggered by per-clause pointcut on non-singleton aspect.
281: * The advice has no effect.
282: */
283: protected static class SyntheticInstantiationAdvisor extends
284: DefaultPointcutAdvisor {
285:
286: public SyntheticInstantiationAdvisor(
287: final MetadataAwareAspectInstanceFactory aif) {
288: super (aif.getAspectMetadata().getPerClausePointcut(),
289: new MethodBeforeAdvice() {
290: public void before(Method method,
291: Object[] args, Object target) {
292: // Simply instantiate the aspect
293: aif.getAspectInstance();
294: }
295: });
296: }
297: }
298:
299: }
|