View Javadoc
1   /*
2    * Copyright 2002-2013 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;
18  
19  import java.io.IOException;
20  import java.io.ObjectInputStream;
21  import java.lang.reflect.Method;
22  import java.util.HashSet;
23  import java.util.Map;
24  import java.util.Set;
25  import java.util.concurrent.ConcurrentHashMap;
26  
27  import org.aopalliance.intercept.MethodInvocation;
28  import org.apache.commons.logging.Log;
29  import org.apache.commons.logging.LogFactory;
30  import org.aspectj.weaver.BCException;
31  import org.aspectj.weaver.patterns.NamePattern;
32  import org.aspectj.weaver.reflect.ReflectionWorld.ReflectionWorldException;
33  import org.aspectj.weaver.reflect.ShadowMatchImpl;
34  import org.aspectj.weaver.tools.ContextBasedMatcher;
35  import org.aspectj.weaver.tools.FuzzyBoolean;
36  import org.aspectj.weaver.tools.JoinPointMatch;
37  import org.aspectj.weaver.tools.MatchingContext;
38  import org.aspectj.weaver.tools.PointcutDesignatorHandler;
39  import org.aspectj.weaver.tools.PointcutExpression;
40  import org.aspectj.weaver.tools.PointcutParameter;
41  import org.aspectj.weaver.tools.PointcutParser;
42  import org.aspectj.weaver.tools.PointcutPrimitive;
43  import org.aspectj.weaver.tools.ShadowMatch;
44  
45  import org.springframework.aop.ClassFilter;
46  import org.springframework.aop.IntroductionAwareMethodMatcher;
47  import org.springframework.aop.MethodMatcher;
48  import org.springframework.aop.ProxyMethodInvocation;
49  import org.springframework.aop.framework.autoproxy.ProxyCreationContext;
50  import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
51  import org.springframework.aop.support.AbstractExpressionPointcut;
52  import org.springframework.aop.support.AopUtils;
53  import org.springframework.beans.factory.BeanFactory;
54  import org.springframework.beans.factory.BeanFactoryAware;
55  import org.springframework.beans.factory.BeanFactoryUtils;
56  import org.springframework.beans.factory.FactoryBean;
57  import org.springframework.beans.factory.config.ConfigurableBeanFactory;
58  import org.springframework.util.ClassUtils;
59  import org.springframework.util.ObjectUtils;
60  import org.springframework.util.StringUtils;
61  
62  /**
63   * Spring {@link org.springframework.aop.Pointcut} implementation
64   * that uses the AspectJ weaver to evaluate a pointcut expression.
65   *
66   * <p>The pointcut expression value is an AspectJ expression. This can
67   * reference other pointcuts and use composition and other operations.
68   *
69   * <p>Naturally, as this is to be processed by Spring AOP's proxy-based model,
70   * only method execution pointcuts are supported.
71   *
72   * @author Rob Harrop
73   * @author Adrian Colyer
74   * @author Rod Johnson
75   * @author Juergen Hoeller
76   * @author Ramnivas Laddad
77   * @author Dave Syer
78   * @since 2.0
79   */
80  @SuppressWarnings("serial")
81  public class AspectJExpressionPointcut extends AbstractExpressionPointcut
82  		implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
83  
84  	private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>();
85  
86  	static {
87  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
88  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
89  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
90  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
91  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
92  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
93  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
94  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
95  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
96  		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
97  	}
98  
99  
100 	private static final Log logger = LogFactory.getLog(AspectJExpressionPointcut.class);
101 
102 	private Class<?> pointcutDeclarationScope;
103 
104 	private String[] pointcutParameterNames = new String[0];
105 
106 	private Class<?>[] pointcutParameterTypes = new Class<?>[0];
107 
108 	private BeanFactory beanFactory;
109 
110 	private transient ClassLoader pointcutClassLoader;
111 
112 	private transient PointcutExpression pointcutExpression;
113 
114 	private transient Map<Method, ShadowMatch> shadowMatchCache = new ConcurrentHashMap<Method, ShadowMatch>(32);
115 
116 
117 	/**
118 	 * Create a new default AspectJExpressionPointcut.
119 	 */
120 	public AspectJExpressionPointcut() {
121 	}
122 
123 	/**
124 	 * Create a new AspectJExpressionPointcut with the given settings.
125 	 * @param declarationScope the declaration scope for the pointcut
126 	 * @param paramNames the parameter names for the pointcut
127 	 * @param paramTypes the parameter types for the pointcut
128 	 */
129 	public AspectJExpressionPointcut(Class<?> declarationScope, String[] paramNames, Class<?>[] paramTypes) {
130 		this.pointcutDeclarationScope = declarationScope;
131 		if (paramNames.length != paramTypes.length) {
132 			throw new IllegalStateException(
133 					"Number of pointcut parameter names must match number of pointcut parameter types");
134 		}
135 		this.pointcutParameterNames = paramNames;
136 		this.pointcutParameterTypes = paramTypes;
137 	}
138 
139 
140 	/**
141 	 * Set the declaration scope for the pointcut.
142 	 */
143 	public void setPointcutDeclarationScope(Class<?> pointcutDeclarationScope) {
144 		this.pointcutDeclarationScope = pointcutDeclarationScope;
145 	}
146 
147 	/**
148 	 * Set the parameter names for the pointcut.
149 	 */
150 	public void setParameterNames(String... names) {
151 		this.pointcutParameterNames = names;
152 	}
153 
154 	/**
155 	 * Set the parameter types for the pointcut.
156 	 */
157 	public void setParameterTypes(Class<?>... types) {
158 		this.pointcutParameterTypes = types;
159 	}
160 
161 	@Override
162 	public void setBeanFactory(BeanFactory beanFactory) {
163 		this.beanFactory = beanFactory;
164 	}
165 
166 
167 	@Override
168 	public ClassFilter getClassFilter() {
169 		checkReadyToMatch();
170 		return this;
171 	}
172 
173 	@Override
174 	public MethodMatcher getMethodMatcher() {
175 		checkReadyToMatch();
176 		return this;
177 	}
178 
179 
180 	/**
181 	 * Check whether this pointcut is ready to match,
182 	 * lazily building the underlying AspectJ pointcut expression.
183 	 */
184 	private void checkReadyToMatch() {
185 		if (getExpression() == null) {
186 			throw new IllegalStateException("Must set property 'expression' before attempting to match");
187 		}
188 		if (this.pointcutExpression == null) {
189 			this.pointcutClassLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
190 					((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() :
191 					ClassUtils.getDefaultClassLoader());
192 			this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
193 		}
194 	}
195 
196 	/**
197 	 * Build the underlying AspectJ pointcut expression.
198 	 */
199 	private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {
200 		PointcutParser parser = initializePointcutParser(classLoader);
201 		PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
202 		for (int i = 0; i < pointcutParameters.length; i++) {
203 			pointcutParameters[i] = parser.createPointcutParameter(
204 					this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
205 		}
206 		return parser.parsePointcutExpression(
207 				replaceBooleanOperators(getExpression()),
208 				this.pointcutDeclarationScope, pointcutParameters);
209 	}
210 
211 	/**
212 	 * Initialize the underlying AspectJ pointcut parser.
213 	 */
214 	private PointcutParser initializePointcutParser(ClassLoader cl) {
215 		PointcutParser parser = PointcutParser
216 				.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
217 						SUPPORTED_PRIMITIVES, cl);
218 		parser.registerPointcutDesignatorHandler(new BeanNamePointcutDesignatorHandler());
219 		return parser;
220 	}
221 
222 
223 	/**
224 	 * If a pointcut expression has been specified in XML, the user cannot
225 	 * write {@code and} as "&&" (though &amp;&amp; will work).
226 	 * We also allow {@code and} between two pointcut sub-expressions.
227 	 * <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
228 	 */
229 	private String replaceBooleanOperators(String pcExpr) {
230 		String result = StringUtils.replace(pcExpr, " and ", " && ");
231 		result = StringUtils.replace(result, " or ", " || ");
232 		result = StringUtils.replace(result, " not ", " ! ");
233 		return result;
234 	}
235 
236 
237 	/**
238 	 * Return the underlying AspectJ pointcut expression.
239 	 */
240 	public PointcutExpression getPointcutExpression() {
241 		checkReadyToMatch();
242 		return this.pointcutExpression;
243 	}
244 
245 	@Override
246 	public boolean matches(Class<?> targetClass) {
247 		checkReadyToMatch();
248 		try {
249 			try {
250 				return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
251 			}
252 			catch (ReflectionWorldException ex) {
253 				logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
254 				// Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yet
255 				PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
256 				if (fallbackExpression != null) {
257 					return fallbackExpression.couldMatchJoinPointsInType(targetClass);
258 				}
259 			}
260 		}
261 		catch (BCException ex) {
262 			logger.debug("PointcutExpression matching rejected target class", ex);
263 		}
264 		return false;
265 	}
266 
267 	@Override
268 	public boolean matches(Method method, Class<?> targetClass, boolean beanHasIntroductions) {
269 		checkReadyToMatch();
270 		Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
271 		ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
272 
273 		// Special handling for this, target, @this, @target, @annotation
274 		// in Spring - we can optimize since we know we have exactly this class,
275 		// and there will never be matching subclass at runtime.
276 		if (shadowMatch.alwaysMatches()) {
277 			return true;
278 		}
279 		else if (shadowMatch.neverMatches()) {
280 			return false;
281 		}
282 		else {
283 			// the maybe case
284 			if (beanHasIntroductions) {
285 				return true;
286 			}
287 			// A match test returned maybe - if there are any subtype sensitive variables
288 			// involved in the test (this, target, at_this, at_target, at_annotation) then
289 			// we say this is not a match as in Spring there will never be a different
290 			// runtime subtype.
291 			RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
292 			return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
293 		}
294 	}
295 
296 	@Override
297 	public boolean matches(Method method, Class<?> targetClass) {
298 		return matches(method, targetClass, false);
299 	}
300 
301 	@Override
302 	public boolean isRuntime() {
303 		checkReadyToMatch();
304 		return this.pointcutExpression.mayNeedDynamicTest();
305 	}
306 
307 	@Override
308 	public boolean matches(Method method, Class<?> targetClass, Object[] args) {
309 		checkReadyToMatch();
310 		ShadowMatch shadowMatch = getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method);
311 		ShadowMatch originalShadowMatch = getShadowMatch(method, method);
312 
313 		// Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
314 		// consistent with return of MethodInvocationProceedingJoinPoint
315 		ProxyMethodInvocation pmi = null;
316 		Object targetObject = null;
317 		Object thisObject = null;
318 		try {
319 			MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
320 			targetObject = mi.getThis();
321 			if (!(mi instanceof ProxyMethodInvocation)) {
322 				throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
323 			}
324 			pmi = (ProxyMethodInvocation) mi;
325 			thisObject = pmi.getProxy();
326 		}
327 		catch (IllegalStateException ex) {
328 			// No current invocation...
329 			// TODO: Should we really proceed here?
330 			logger.debug("Couldn't access current invocation - matching with limited context: " + ex);
331 		}
332 
333 		JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);
334 
335 		/*
336 		 * Do a final check to see if any this(TYPE) kind of residue match. For
337 		 * this purpose, we use the original method's (proxy method's) shadow to
338 		 * ensure that 'this' is correctly checked against. Without this check,
339 		 * we get incorrect match on this(TYPE) where TYPE matches the target
340 		 * type but not 'this' (as would be the case of JDK dynamic proxies).
341 		 * <p>See SPR-2979 for the original bug.
342 		 */
343 		if (pmi != null) {  // there is a current invocation
344 			RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(originalShadowMatch);
345 			if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
346 				return false;
347 			}
348 			if (joinPointMatch.matches()) {
349 				bindParameters(pmi, joinPointMatch);
350 			}
351 		}
352 		return joinPointMatch.matches();
353 	}
354 
355 	protected String getCurrentProxiedBeanName() {
356 		return ProxyCreationContext.getCurrentProxiedBeanName();
357 	}
358 
359 
360 	/**
361 	 * Get a new pointcut expression based on a target class's loader rather than the default.
362 	 */
363 	private PointcutExpression getFallbackPointcutExpression(Class<?> targetClass) {
364 		try {
365 			ClassLoader classLoader = targetClass.getClassLoader();
366 			if (classLoader != null && classLoader != this.pointcutClassLoader) {
367 				return buildPointcutExpression(classLoader);
368 			}
369 		}
370 		catch (Throwable ex) {
371 			logger.debug("Failed to create fallback PointcutExpression", ex);
372 		}
373 		return null;
374 	}
375 
376 	private RuntimeTestWalker getRuntimeTestWalker(ShadowMatch shadowMatch) {
377 		if (shadowMatch instanceof DefensiveShadowMatch) {
378 			return new RuntimeTestWalker(((DefensiveShadowMatch) shadowMatch).primary);
379 		}
380 		return new RuntimeTestWalker(shadowMatch);
381 	}
382 
383 	private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm) {
384 		// Note: Can't use JoinPointMatch.getClass().getName() as the key, since
385 		// Spring AOP does all the matching at a join point, and then all the invocations
386 		// under this scenario, if we just use JoinPointMatch as the key, then
387 		// 'last man wins' which is not what we want at all.
388 		// Using the expression is guaranteed to be safe, since 2 identical expressions
389 		// are guaranteed to bind in exactly the same way.
390 		invocation.setUserAttribute(getExpression(), jpm);
391 	}
392 
393 	private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
394 		// Avoid lock contention for known Methods through concurrent access...
395 		ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
396 		if (shadowMatch == null) {
397 			synchronized (this.shadowMatchCache) {
398 				// Not found - now check again with full lock...
399 				PointcutExpression fallbackExpression = null;
400 				Method methodToMatch = targetMethod;
401 				shadowMatch = this.shadowMatchCache.get(targetMethod);
402 				if (shadowMatch == null) {
403 					try {
404 						shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
405 					}
406 					catch (ReflectionWorldException ex) {
407 						// Failed to introspect target method, probably because it has been loaded
408 						// in a special ClassLoader. Let's try the declaring ClassLoader instead...
409 						try {
410 							fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
411 							if (fallbackExpression != null) {
412 								shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
413 							}
414 						}
415 						catch (ReflectionWorldException ex2) {
416 							fallbackExpression = null;
417 						}
418 					}
419 					if (shadowMatch == null && targetMethod != originalMethod) {
420 						methodToMatch = originalMethod;
421 						try {
422 							shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
423 						}
424 						catch (ReflectionWorldException ex3) {
425 							// Could neither introspect the target class nor the proxy class ->
426 							// let's try the original method's declaring class before we give up...
427 							try {
428 								fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
429 								if (fallbackExpression != null) {
430 									shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
431 								}
432 							}
433 							catch (ReflectionWorldException ex4) {
434 								fallbackExpression = null;
435 							}
436 						}
437 					}
438 					if (shadowMatch == null) {
439 						shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
440 					}
441 					else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
442 						shadowMatch = new DefensiveShadowMatch(shadowMatch,
443 								fallbackExpression.matchesMethodExecution(methodToMatch));
444 					}
445 					this.shadowMatchCache.put(targetMethod, shadowMatch);
446 				}
447 			}
448 		}
449 		return shadowMatch;
450 	}
451 
452 
453 	@Override
454 	public boolean equals(Object other) {
455 		if (this == other) {
456 			return true;
457 		}
458 		if (!(other instanceof AspectJExpressionPointcut)) {
459 			return false;
460 		}
461 		AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
462 		return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
463 				ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
464 				ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
465 				ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
466 	}
467 
468 	@Override
469 	public int hashCode() {
470 		int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
471 		hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
472 		hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
473 		hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
474 		return hashCode;
475 	}
476 
477 	@Override
478 	public String toString() {
479 		StringBuilder sb = new StringBuilder();
480 		sb.append("AspectJExpressionPointcut: ");
481 		if (this.pointcutParameterNames != null && this.pointcutParameterTypes != null) {
482 			sb.append("(");
483 			for (int i = 0; i < this.pointcutParameterTypes.length; i++) {
484 				sb.append(this.pointcutParameterTypes[i].getName());
485 				sb.append(" ");
486 				sb.append(this.pointcutParameterNames[i]);
487 				if ((i+1) < this.pointcutParameterTypes.length) {
488 					sb.append(", ");
489 				}
490 			}
491 			sb.append(")");
492 		}
493 		sb.append(" ");
494 		if (getExpression() != null) {
495 			sb.append(getExpression());
496 		}
497 		else {
498 			sb.append("<pointcut expression not set>");
499 		}
500 		return sb.toString();
501 	}
502 
503 
504 	/**
505 	 * Handler for the Spring-specific {@code bean()} pointcut designator
506 	 * extension to AspectJ.
507 	 * <p>This handler must be added to each pointcut object that needs to
508 	 * handle the {@code bean()} PCD. Matching context is obtained
509 	 * automatically by examining a thread local variable and therefore a matching
510 	 * context need not be set on the pointcut.
511 	 */
512 	private class BeanNamePointcutDesignatorHandler implements PointcutDesignatorHandler {
513 
514 		private static final String BEAN_DESIGNATOR_NAME = "bean";
515 
516 		@Override
517 		public String getDesignatorName() {
518 			return BEAN_DESIGNATOR_NAME;
519 		}
520 
521 		@Override
522 		public ContextBasedMatcher parse(String expression) {
523 			return new BeanNameContextMatcher(expression);
524 		}
525 	}
526 
527 
528 	/**
529 	 * Matcher class for the BeanNamePointcutDesignatorHandler.
530 	 * <p>Dynamic match tests for this matcher always return true,
531 	 * since the matching decision is made at the proxy creation time.
532 	 * For static match tests, this matcher abstains to allow the overall
533 	 * pointcut to match even when negation is used with the bean() pointcut.
534 	 */
535 	private class BeanNameContextMatcher implements ContextBasedMatcher {
536 
537 		private final NamePattern expressionPattern;
538 
539 		public BeanNameContextMatcher(String expression) {
540 			this.expressionPattern = new NamePattern(expression);
541 		}
542 
543 		@Override
544 		@SuppressWarnings("rawtypes")
545 		@Deprecated
546 		public boolean couldMatchJoinPointsInType(Class someClass) {
547 			return (contextMatch(someClass) == FuzzyBoolean.YES);
548 		}
549 
550 		@Override
551 		@SuppressWarnings("rawtypes")
552 		@Deprecated
553 		public boolean couldMatchJoinPointsInType(Class someClass, MatchingContext context) {
554 			return (contextMatch(someClass) == FuzzyBoolean.YES);
555 		}
556 
557 		@Override
558 		public boolean matchesDynamically(MatchingContext context) {
559 			return true;
560 		}
561 
562 		@Override
563 		public FuzzyBoolean matchesStatically(MatchingContext context) {
564 			return contextMatch(null);
565 		}
566 
567 		@Override
568 		public boolean mayNeedDynamicTest() {
569 			return false;
570 		}
571 
572 		private FuzzyBoolean contextMatch(Class<?> targetType) {
573 			String advisedBeanName = getCurrentProxiedBeanName();
574 			if (advisedBeanName == null) {  // no proxy creation in progress
575 				// abstain; can't return YES, since that will make pointcut with negation fail
576 				return FuzzyBoolean.MAYBE;
577 			}
578 			if (BeanFactoryUtils.isGeneratedBeanName(advisedBeanName)) {
579 				return FuzzyBoolean.NO;
580 			}
581 			if (targetType != null) {
582 				boolean isFactory = FactoryBean.class.isAssignableFrom(targetType);
583 				return FuzzyBoolean.fromBoolean(
584 						matchesBeanName(isFactory ? BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName : advisedBeanName));
585 			}
586 			else {
587 				return FuzzyBoolean.fromBoolean(matchesBeanName(advisedBeanName) ||
588 						matchesBeanName(BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName));
589 			}
590 		}
591 
592 		private boolean matchesBeanName(String advisedBeanName) {
593 			if (this.expressionPattern.matches(advisedBeanName)) {
594 				return true;
595 			}
596 			if (beanFactory != null) {
597 				String[] aliases = beanFactory.getAliases(advisedBeanName);
598 				for (String alias : aliases) {
599 					if (this.expressionPattern.matches(alias)) {
600 						return true;
601 					}
602 				}
603 			}
604 			return false;
605 		}
606 	}
607 
608 
609 	//---------------------------------------------------------------------
610 	// Serialization support
611 	//---------------------------------------------------------------------
612 
613 	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
614 		// Rely on default serialization, just initialize state after deserialization.
615 		ois.defaultReadObject();
616 
617 		// Initialize transient fields.
618 		// pointcutExpression will be initialized lazily by checkReadyToMatch()
619 		this.shadowMatchCache = new ConcurrentHashMap<Method, ShadowMatch>(32);
620 	}
621 
622 
623 	private static class DefensiveShadowMatch implements ShadowMatch {
624 
625 		private final ShadowMatch primary;
626 
627 		private final ShadowMatch other;
628 
629 		public DefensiveShadowMatch(ShadowMatch primary, ShadowMatch other) {
630 			this.primary = primary;
631 			this.other = other;
632 		}
633 
634 		@Override
635 		public boolean alwaysMatches() {
636 			return this.primary.alwaysMatches();
637 		}
638 
639 		@Override
640 		public boolean maybeMatches() {
641 			return this.primary.maybeMatches();
642 		}
643 
644 		@Override
645 		public boolean neverMatches() {
646 			return this.primary.neverMatches();
647 		}
648 
649 		@Override
650 		public JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args) {
651 			try {
652 				return this.primary.matchesJoinPoint(thisObject, targetObject, args);
653 			}
654 			catch (ReflectionWorldException ex) {
655 				return this.other.matchesJoinPoint(thisObject, targetObject, args);
656 			}
657 		}
658 
659 		@Override
660 		public void setMatchingContext(MatchingContext aMatchContext) {
661 			this.primary.setMatchingContext(aMatchContext);
662 			this.other.setMatchingContext(aMatchContext);
663 		}
664 	}
665 
666 }