1 /*
2  * Copyright 2002-2007 the original author or authors.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package org.springframework.aop.aspectj.annotation;
18 
19 import java.lang.reflect.Field;
20 import java.lang.reflect.Method;
21 import java.util.LinkedList;
22 import java.util.List;
23 
24 import org.aopalliance.aop.Advice;
25 import org.aspectj.lang.annotation.AfterReturning;
26 import org.aspectj.lang.annotation.AfterThrowing;
27 import org.aspectj.lang.annotation.DeclareParents;
28 import org.aspectj.lang.annotation.Pointcut;
29 
30 import org.springframework.aop.Advisor;
31 import org.springframework.aop.MethodBeforeAdvice;
32 import org.springframework.aop.aspectj.AbstractAspectJAdvice;
33 import org.springframework.aop.aspectj.AspectJAfterAdvice;
34 import org.springframework.aop.aspectj.AspectJAfterReturningAdvice;
35 import org.springframework.aop.aspectj.AspectJAfterThrowingAdvice;
36 import org.springframework.aop.aspectj.AspectJAroundAdvice;
37 import org.springframework.aop.aspectj.AspectJExpressionPointcut;
38 import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
39 import org.springframework.aop.aspectj.DeclareParentsAdvisor;
40 import org.springframework.aop.framework.AopConfigException;
41 import org.springframework.aop.support.DefaultPointcutAdvisor;
42 import org.springframework.core.annotation.AnnotationUtils;
43 import org.springframework.util.ReflectionUtils;
44 import org.springframework.util.StringUtils;
45 
46 /**
47  * Factory that can create Spring AOP Advisors given AspectJ classes from
48  * classes honoring the AspectJ 5 annotation syntax, using reflection to
49  * invoke the corresponding advice methods.
50  *
51  * @author Rod Johnson
52  * @author Adrian Colyer
53  * @author Juergen Hoeller
54  * @author Ramnivas Laddad
55  * @since 2.0
56  */
57 public class ReflectiveAspectJAdvisorFactory extends AbstractAspectJAdvisorFactory {
58 
getAdvisors(MetadataAwareAspectInstanceFactory maaif)59 	public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory maaif) {
60 		final Class<?> aspectClass = maaif.getAspectMetadata().getAspectClass();
61 		final String aspectName = maaif.getAspectMetadata().getAspectName();
62 		validate(aspectClass);
63 
64 		// We need to wrap the MetadataAwareAspectInstanceFactory with a decorator
65 		// so that it will only instantiate once.
66 		final MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory =
67 				new LazySingletonAspectInstanceFactoryDecorator(maaif);
68 
69 		final List<Advisor> advisors = new LinkedList<Advisor>();
70 		ReflectionUtils.doWithMethods(aspectClass, new ReflectionUtils.MethodCallback() {
71 			public void doWith(Method method) throws IllegalArgumentException {
72 				// Exclude pointcuts
73 				if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
74 					Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
75 					if (advisor != null) {
76 						advisors.add(advisor);
77 					}
78 				}
79 			}
80 		});
81 
82 		// If it's a per target aspect, emit the dummy instantiating aspect.
83 		if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
84 			Advisor instantiationAdvisor = new SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
85 			advisors.add(0, instantiationAdvisor);
86 		}
87 
88 		// Find introduction fields.
89 		for (Field field : aspectClass.getDeclaredFields()) {
90 			Advisor advisor = getDeclareParentsAdvisor(field);
91 			if (advisor != null) {
92 				advisors.add(advisor);
93 			}
94 		}
95 
96 		return advisors;
97 	}
98 
99 	/**
100 	 * Build a {@link org.springframework.aop.aspectj.DeclareParentsAdvisor}
101 	 * for the given introduction field.
102 	 * <p>Resulting Advisors will need to be evaluated for targets.
103 	 * @param introductionField the field to introspect
104 	 * @return <code>null</code> if not an Advisor
105 	 */
getDeclareParentsAdvisor(Field introductionField)106 	private Advisor getDeclareParentsAdvisor(Field introductionField) {
107 		DeclareParents declareParents = (DeclareParents) introductionField.getAnnotation(DeclareParents.class);
108 		if (declareParents == null) {
109 			// Not an introduction field
110 			return null;
111 		}
112 
113 		if (DeclareParents.class.equals(declareParents.defaultImpl())) {
114 			// This is what comes back if it wasn't set. This seems bizarre...
115 			// TODO this restriction possibly should be relaxed
116 			throw new IllegalStateException("defaultImpl must be set on DeclareParents");
117 		}
118 
119 		return new DeclareParentsAdvisor(
120 				introductionField.getType(), declareParents.value(), declareParents.defaultImpl());
121 	}
122 
123 
getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName)124 	public Advisor getAdvisor(Method candidateAdviceMethod, MetadataAwareAspectInstanceFactory aif,
125 			int declarationOrderInAspect, String aspectName) {
126 
127 		validate(aif.getAspectMetadata().getAspectClass());
128 
129 		AspectJExpressionPointcut ajexp =
130 				getPointcut(candidateAdviceMethod, aif.getAspectMetadata().getAspectClass());
131 		if (ajexp == null) {
132 			return null;
133 		}
134 		return new InstantiationModelAwarePointcutAdvisorImpl(
135 				this, ajexp, aif, candidateAdviceMethod, declarationOrderInAspect, aspectName);
136 	}
137 
getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass)138 	private AspectJExpressionPointcut getPointcut(Method candidateAdviceMethod, Class<?> candidateAspectClass) {
139 		AspectJAnnotation<?> aspectJAnnotation =
140 				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
141 		if (aspectJAnnotation == null) {
142 			return null;
143 		}
144 		AspectJExpressionPointcut ajexp =
145 				new AspectJExpressionPointcut(candidateAspectClass, new String[0], new Class[0]);
146 		ajexp.setExpression(aspectJAnnotation.getPointcutExpression());
147 		return ajexp;
148 	}
149 
150 
getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName)151 	public Advice getAdvice(Method candidateAdviceMethod, AspectJExpressionPointcut ajexp,
152 			MetadataAwareAspectInstanceFactory aif, int declarationOrderInAspect, String aspectName) {
153 
154 		Class<?> candidateAspectClass = aif.getAspectMetadata().getAspectClass();
155 		validate(candidateAspectClass);
156 
157 		AspectJAnnotation<?> aspectJAnnotation =
158 				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(candidateAdviceMethod);
159 		if (aspectJAnnotation == null) {
160 			return null;
161 		}
162 
163 		// If we get here, we know we have an AspectJ method.
164 		// Check that it's an AspectJ-annotated class
165 		if (!isAspect(candidateAspectClass)) {
166 			throw new AopConfigException("Advice must be declared inside an aspect type: " +
167 					"Offending method '" + candidateAdviceMethod + "' in class [" +
168 					candidateAspectClass.getName() + "]");
169 		}
170 
171 		if (logger.isDebugEnabled()) {
172 			logger.debug("Found AspectJ method: " + candidateAdviceMethod);
173 		}
174 
175 		AbstractAspectJAdvice springAdvice;
176 
177 		switch (aspectJAnnotation.getAnnotationType()) {
178 			case AtBefore:
179 				springAdvice = new AspectJMethodBeforeAdvice(candidateAdviceMethod, ajexp, aif);
180 				break;
181 			case AtAfter:
182 				springAdvice = new AspectJAfterAdvice(candidateAdviceMethod, ajexp, aif);
183 				break;
184 			case AtAfterReturning:
185 				springAdvice = new AspectJAfterReturningAdvice(candidateAdviceMethod, ajexp, aif);
186 				AfterReturning afterReturningAnnotation = (AfterReturning) aspectJAnnotation.getAnnotation();
187 				if (StringUtils.hasText(afterReturningAnnotation.returning())) {
188 					springAdvice.setReturningName(afterReturningAnnotation.returning());
189 				}
190 				break;
191 			case AtAfterThrowing:
192 				springAdvice = new AspectJAfterThrowingAdvice(candidateAdviceMethod, ajexp, aif);
193 				AfterThrowing afterThrowingAnnotation = (AfterThrowing) aspectJAnnotation.getAnnotation();
194 				if (StringUtils.hasText(afterThrowingAnnotation.throwing())) {
195 					springAdvice.setThrowingName(afterThrowingAnnotation.throwing());
196 				}
197 				break;
198 			case AtAround:
199 				springAdvice = new AspectJAroundAdvice(candidateAdviceMethod, ajexp, aif);
200 				break;
201 			case AtPointcut:
202 				if (logger.isDebugEnabled()) {
203 					logger.debug("Processing pointcut '" + candidateAdviceMethod.getName() + "'");
204 				}
205 				return null;
206 			default:
207 				throw new UnsupportedOperationException(
208 						"Unsupported advice type on method " + candidateAdviceMethod);
209 		}
210 
211 		// Now to configure the advice...
212 		springAdvice.setAspectName(aspectName);
213 		springAdvice.setDeclarationOrder(declarationOrderInAspect);
214 		String[] argNames = this.parameterNameDiscoverer.getParameterNames(candidateAdviceMethod);
215 		if (argNames != null) {
216 			springAdvice.setArgumentNamesFromStringArray(argNames);
217 		}
218 		springAdvice.calculateArgumentBindings();
219 		return springAdvice;
220 	}
221 
222 	/**
223 	 * Synthetic advisor that instantiates the aspect.
224 	 * Triggered by per-clause pointcut on non-singleton aspect.
225 	 * The advice has no effect.
226 	 */
227 	protected static class SyntheticInstantiationAdvisor extends DefaultPointcutAdvisor {
228 
SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif)229 		public SyntheticInstantiationAdvisor(final MetadataAwareAspectInstanceFactory aif) {
230 			super(aif.getAspectMetadata().getPerClausePointcut(), new MethodBeforeAdvice() {
231 				public void before(Method method, Object[] args, Object target) {
232 					// Simply instantiate the aspect
233 					aif.getAspectInstance();
234 				}
235 			});
236 		}
237 	}
238 
239 }
240