1 /*
2  * Copyright 2002-2010 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.Method;
20 
21 import org.aopalliance.aop.Advice;
22 import org.aspectj.lang.reflect.PerClauseKind;
23 
24 import org.springframework.aop.Pointcut;
25 import org.springframework.aop.aspectj.AspectJExpressionPointcut;
26 import org.springframework.aop.aspectj.AspectJPrecedenceInformation;
27 import org.springframework.aop.aspectj.InstantiationModelAwarePointcutAdvisor;
28 import org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory.AspectJAnnotation;
29 import org.springframework.aop.support.DynamicMethodMatcherPointcut;
30 import org.springframework.aop.support.Pointcuts;
31 
32 /**
33  * Internal implementation of AspectJPointcutAdvisor.
34  * Note that there will be one instance of this advisor for each target method.
35  *
36  * @author Rod Johnson
37  * @author Juergen Hoeller
38  * @since 2.0
39  */
40 class InstantiationModelAwarePointcutAdvisorImpl
41 		implements InstantiationModelAwarePointcutAdvisor, AspectJPrecedenceInformation {
42 
43 	private final AspectJExpressionPointcut declaredPointcut;
44 
45 	private Pointcut pointcut;
46 
47 	private final MetadataAwareAspectInstanceFactory aspectInstanceFactory;
48 
49 	private final Method method;
50 
51 	private final boolean lazy;
52 
53 	private final AspectJAdvisorFactory atAspectJAdvisorFactory;
54 
55 	private Advice instantiatedAdvice;
56 
57 	private int declarationOrder;
58 
59 	private String aspectName;
60 
61 	private Boolean isBeforeAdvice;
62 
63 	private Boolean isAfterAdvice;
64 
65 
InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af, AspectJExpressionPointcut ajexp, MetadataAwareAspectInstanceFactory aif, Method method, int declarationOrderInAspect, String aspectName)66 	public InstantiationModelAwarePointcutAdvisorImpl(AspectJAdvisorFactory af,  AspectJExpressionPointcut ajexp,
67 			MetadataAwareAspectInstanceFactory aif,  Method method, int declarationOrderInAspect, String aspectName) {
68 
69 		this.declaredPointcut = ajexp;
70 		this.method = method;
71 		this.atAspectJAdvisorFactory = af;
72 		this.aspectInstanceFactory = aif;
73 		this.declarationOrder = declarationOrderInAspect;
74 		this.aspectName = aspectName;
75 
76 		if (aif.getAspectMetadata().isLazilyInstantiated()) {
77 			// Static part of the pointcut is a lazy type.
78 			Pointcut preInstantiationPointcut =
79 					Pointcuts.union(aif.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
80 
81 			// Make it dynamic: must mutate from pre-instantiation to post-instantiation state.
82 			// If it's not a dynamic pointcut, it may be optimized out
83 			// by the Spring AOP infrastructure after the first evaluation.
84 			this.pointcut = new PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aif);
85 			this.lazy = true;
86 		}
87 		else {
88 			// A singleton aspect.
89 			this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
90 			this.pointcut = declaredPointcut;
91 			this.lazy = false;
92 		}
93 	}
94 
95 
96 	/**
97 	 * The pointcut for Spring AOP to use. Actual behaviour of the pointcut will change
98 	 * depending on the state of the advice.
99 	 */
getPointcut()100 	public Pointcut getPointcut() {
101 		return this.pointcut;
102 	}
103 
104 	/**
105 	 * This is only of interest for Spring AOP: AspectJ instantiation semantics
106 	 * are much richer. In AspectJ terminology, all a return of <code>true</code>
107 	 * means here is that the aspect is not a SINGLETON.
108 	 */
isPerInstance()109 	public boolean isPerInstance() {
110 		return (getAspectMetadata().getAjType().getPerClause().getKind() != PerClauseKind.SINGLETON);
111 	}
112 
113 	/**
114 	 * Return the AspectJ AspectMetadata for this advisor.
115 	 */
getAspectMetadata()116 	public AspectMetadata getAspectMetadata() {
117 		return this.aspectInstanceFactory.getAspectMetadata();
118 	}
119 
120 	/**
121 	 * Lazily instantiate advice if necessary.
122 	 */
getAdvice()123 	public synchronized Advice getAdvice() {
124 		if (this.instantiatedAdvice == null) {
125 			this.instantiatedAdvice = instantiateAdvice(this.declaredPointcut);
126 		}
127 		return this.instantiatedAdvice;
128 	}
129 
isLazy()130 	public boolean isLazy() {
131 		return this.lazy;
132 	}
133 
isAdviceInstantiated()134 	public synchronized boolean isAdviceInstantiated() {
135 		return (this.instantiatedAdvice != null);
136 	}
137 
138 
instantiateAdvice(AspectJExpressionPointcut pcut)139 	private Advice instantiateAdvice(AspectJExpressionPointcut pcut) {
140 		return this.atAspectJAdvisorFactory.getAdvice(
141 				this.method, pcut, this.aspectInstanceFactory, this.declarationOrder, this.aspectName);
142 	}
143 
getAspectInstanceFactory()144 	public MetadataAwareAspectInstanceFactory getAspectInstanceFactory() {
145 		return this.aspectInstanceFactory;
146 	}
147 
getDeclaredPointcut()148 	public AspectJExpressionPointcut getDeclaredPointcut() {
149 		return this.declaredPointcut;
150 	}
151 
getOrder()152 	public int getOrder() {
153 		return this.aspectInstanceFactory.getOrder();
154 	}
155 
getAspectName()156 	public String getAspectName() {
157 		return this.aspectName;
158 	}
159 
getDeclarationOrder()160 	public int getDeclarationOrder() {
161 		return this.declarationOrder;
162 	}
163 
isBeforeAdvice()164 	public boolean isBeforeAdvice() {
165 		if (this.isBeforeAdvice == null) {
166 			determineAdviceType();
167 		}
168 		return this.isBeforeAdvice;
169 	}
170 
isAfterAdvice()171 	public boolean isAfterAdvice() {
172 		if (this.isAfterAdvice == null) {
173 			determineAdviceType();
174 		}
175 		return this.isAfterAdvice;
176 	}
177 
178 	/**
179 	 * Duplicates some logic from getAdvice, but importantly does not force
180 	 * creation of the advice.
181 	 */
determineAdviceType()182 	private void determineAdviceType() {
183 		AspectJAnnotation<?> aspectJAnnotation =
184 				AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(this.method);
185 		if (aspectJAnnotation == null) {
186 			this.isBeforeAdvice = false;
187 			this.isAfterAdvice = false;
188 		}
189 		else {
190 			switch (aspectJAnnotation.getAnnotationType()) {
191 				case AtAfter:
192 				case AtAfterReturning:
193 				case AtAfterThrowing:
194 					this.isAfterAdvice = true;
195 					this.isBeforeAdvice = false;
196 					break;
197 				case AtAround:
198 				case AtPointcut:
199 					this.isAfterAdvice = false;
200 					this.isBeforeAdvice = false;
201 					break;
202 				case AtBefore:
203 					this.isAfterAdvice = false;
204 					this.isBeforeAdvice = true;
205 			}
206 		}
207 	}
208 
209 
210 	@Override
toString()211 	public String toString() {
212 		return "InstantiationModelAwarePointcutAdvisor: expression [" + getDeclaredPointcut().getExpression() +
213 			"]; advice method [" + this.method + "]; perClauseKind=" +
214 			this.aspectInstanceFactory.getAspectMetadata().getAjType().getPerClause().getKind();
215 
216 	}
217 
218 
219 	/**
220 	 * Pointcut implementation that changes its behaviour when the advice is instantiated.
221 	 * Note that this is a <i>dynamic</i> pointcut. Otherwise it might
222 	 * be optimized out if it does not at first match statically.
223 	 */
224 	private class PerTargetInstantiationModelPointcut extends DynamicMethodMatcherPointcut {
225 
226 		private final AspectJExpressionPointcut declaredPointcut;
227 
228 		private final Pointcut preInstantiationPointcut;
229 
230 		private LazySingletonAspectInstanceFactoryDecorator aspectInstanceFactory;
231 
PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPointcut, Pointcut preInstantiationPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory)232 		private PerTargetInstantiationModelPointcut(AspectJExpressionPointcut declaredPointcut,
233 				Pointcut preInstantiationPointcut, MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
234 			this.declaredPointcut = declaredPointcut;
235 			this.preInstantiationPointcut = preInstantiationPointcut;
236 			if (aspectInstanceFactory instanceof LazySingletonAspectInstanceFactoryDecorator) {
237 				this.aspectInstanceFactory = (LazySingletonAspectInstanceFactoryDecorator) aspectInstanceFactory;
238 			}
239 		}
240 
241 		@Override
matches(Method method, Class targetClass)242 		public boolean matches(Method method, Class targetClass) {
243 			// We're either instantiated and matching on declared pointcut, or uninstantiated matching on either pointcut
244 			return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass)) ||
245 					this.preInstantiationPointcut.getMethodMatcher().matches(method, targetClass);
246 		}
247 
matches(Method method, Class targetClass, Object[] args)248 		public boolean matches(Method method, Class targetClass, Object[] args) {
249 			// This can match only on declared pointcut.
250 			return (isAspectMaterialized() && this.declaredPointcut.matches(method, targetClass));
251 		}
252 
isAspectMaterialized()253 		private boolean isAspectMaterialized() {
254 			return (this.aspectInstanceFactory == null || this.aspectInstanceFactory.isMaterialized());
255 		}
256 	}
257 
258 }
259