001/*
002 * Copyright 2002-2016 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 *      https://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
017package org.springframework.aop.aspectj;
018
019import java.io.IOException;
020import java.io.ObjectInputStream;
021import java.lang.reflect.Method;
022import java.util.Arrays;
023import java.util.HashSet;
024import java.util.Map;
025import java.util.Set;
026import java.util.concurrent.ConcurrentHashMap;
027
028import org.aopalliance.intercept.MethodInvocation;
029import org.apache.commons.logging.Log;
030import org.apache.commons.logging.LogFactory;
031import org.aspectj.weaver.patterns.NamePattern;
032import org.aspectj.weaver.reflect.ReflectionWorld.ReflectionWorldException;
033import org.aspectj.weaver.reflect.ShadowMatchImpl;
034import org.aspectj.weaver.tools.ContextBasedMatcher;
035import org.aspectj.weaver.tools.FuzzyBoolean;
036import org.aspectj.weaver.tools.JoinPointMatch;
037import org.aspectj.weaver.tools.MatchingContext;
038import org.aspectj.weaver.tools.PointcutDesignatorHandler;
039import org.aspectj.weaver.tools.PointcutExpression;
040import org.aspectj.weaver.tools.PointcutParameter;
041import org.aspectj.weaver.tools.PointcutParser;
042import org.aspectj.weaver.tools.PointcutPrimitive;
043import org.aspectj.weaver.tools.ShadowMatch;
044
045import org.springframework.aop.ClassFilter;
046import org.springframework.aop.IntroductionAwareMethodMatcher;
047import org.springframework.aop.MethodMatcher;
048import org.springframework.aop.ProxyMethodInvocation;
049import org.springframework.aop.framework.autoproxy.ProxyCreationContext;
050import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
051import org.springframework.aop.support.AbstractExpressionPointcut;
052import org.springframework.aop.support.AopUtils;
053import org.springframework.beans.factory.BeanFactory;
054import org.springframework.beans.factory.BeanFactoryAware;
055import org.springframework.beans.factory.BeanFactoryUtils;
056import org.springframework.beans.factory.FactoryBean;
057import org.springframework.beans.factory.config.ConfigurableBeanFactory;
058import org.springframework.util.ClassUtils;
059import org.springframework.util.ObjectUtils;
060import org.springframework.util.StringUtils;
061
062/**
063 * Spring {@link org.springframework.aop.Pointcut} implementation
064 * that uses the AspectJ weaver to evaluate a pointcut expression.
065 *
066 * <p>The pointcut expression value is an AspectJ expression. This can
067 * reference other pointcuts and use composition and other operations.
068 *
069 * <p>Naturally, as this is to be processed by Spring AOP's proxy-based model,
070 * only method execution pointcuts are supported.
071 *
072 * @author Rob Harrop
073 * @author Adrian Colyer
074 * @author Rod Johnson
075 * @author Juergen Hoeller
076 * @author Ramnivas Laddad
077 * @author Dave Syer
078 * @since 2.0
079 */
080@SuppressWarnings("serial")
081public class AspectJExpressionPointcut extends AbstractExpressionPointcut
082                implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {
083
084        private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<PointcutPrimitive>();
085
086        static {
087                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
088                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
089                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
090                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
091                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
092                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
093                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
094                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
095                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
096                SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
097        }
098
099
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 = determinePointcutClassLoader();
190                        this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
191                }
192        }
193
194        /**
195         * Determine the ClassLoader to use for pointcut evaluation.
196         */
197        private ClassLoader determinePointcutClassLoader() {
198                if (this.beanFactory instanceof ConfigurableBeanFactory) {
199                        return ((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader();
200                }
201                if (this.pointcutDeclarationScope != null) {
202                        return this.pointcutDeclarationScope.getClassLoader();
203                }
204                return ClassUtils.getDefaultClassLoader();
205        }
206
207        /**
208         * Build the underlying AspectJ pointcut expression.
209         */
210        private PointcutExpression buildPointcutExpression(ClassLoader classLoader) {
211                PointcutParser parser = initializePointcutParser(classLoader);
212                PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
213                for (int i = 0; i < pointcutParameters.length; i++) {
214                        pointcutParameters[i] = parser.createPointcutParameter(
215                                        this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
216                }
217                return parser.parsePointcutExpression(replaceBooleanOperators(getExpression()),
218                                this.pointcutDeclarationScope, pointcutParameters);
219        }
220
221        /**
222         * Initialize the underlying AspectJ pointcut parser.
223         */
224        private PointcutParser initializePointcutParser(ClassLoader cl) {
225                PointcutParser parser = PointcutParser
226                                .getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
227                                                SUPPORTED_PRIMITIVES, cl);
228                parser.registerPointcutDesignatorHandler(new BeanNamePointcutDesignatorHandler());
229                return parser;
230        }
231
232
233        /**
234         * If a pointcut expression has been specified in XML, the user cannot
235         * write {@code and} as "&&" (though &amp;&amp; will work).
236         * We also allow {@code and} between two pointcut sub-expressions.
237         * <p>This method converts back to {@code &&} for the AspectJ pointcut parser.
238         */
239        private String replaceBooleanOperators(String pcExpr) {
240                String result = StringUtils.replace(pcExpr, " and ", " && ");
241                result = StringUtils.replace(result, " or ", " || ");
242                result = StringUtils.replace(result, " not ", " ! ");
243                return result;
244        }
245
246
247        /**
248         * Return the underlying AspectJ pointcut expression.
249         */
250        public PointcutExpression getPointcutExpression() {
251                checkReadyToMatch();
252                return this.pointcutExpression;
253        }
254
255        @Override
256        public boolean matches(Class<?> targetClass) {
257                checkReadyToMatch();
258                try {
259                        try {
260                                return this.pointcutExpression.couldMatchJoinPointsInType(targetClass);
261                        }
262                        catch (ReflectionWorldException ex) {
263                                logger.debug("PointcutExpression matching rejected target class - trying fallback expression", ex);
264                                // Actually this is still a "maybe" - treat the pointcut as dynamic if we don't know enough yet
265                                PointcutExpression fallbackExpression = getFallbackPointcutExpression(targetClass);
266                                if (fallbackExpression != null) {
267                                        return fallbackExpression.couldMatchJoinPointsInType(targetClass);
268                                }
269                        }
270                }
271                catch (Throwable ex) {
272                        logger.debug("PointcutExpression matching rejected target class", ex);
273                }
274                return false;
275        }
276
277        @Override
278        public boolean matches(Method method, Class<?> targetClass, boolean beanHasIntroductions) {
279                checkReadyToMatch();
280                Method targetMethod = AopUtils.getMostSpecificMethod(method, targetClass);
281                ShadowMatch shadowMatch = getShadowMatch(targetMethod, method);
282
283                // Special handling for this, target, @this, @target, @annotation
284                // in Spring - we can optimize since we know we have exactly this class,
285                // and there will never be matching subclass at runtime.
286                if (shadowMatch.alwaysMatches()) {
287                        return true;
288                }
289                else if (shadowMatch.neverMatches()) {
290                        return false;
291                }
292                else {
293                        // the maybe case
294                        if (beanHasIntroductions) {
295                                return true;
296                        }
297                        // A match test returned maybe - if there are any subtype sensitive variables
298                        // involved in the test (this, target, at_this, at_target, at_annotation) then
299                        // we say this is not a match as in Spring there will never be a different
300                        // runtime subtype.
301                        RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
302                        return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
303                }
304        }
305
306        @Override
307        public boolean matches(Method method, Class<?> targetClass) {
308                return matches(method, targetClass, false);
309        }
310
311        @Override
312        public boolean isRuntime() {
313                checkReadyToMatch();
314                return this.pointcutExpression.mayNeedDynamicTest();
315        }
316
317        @Override
318        public boolean matches(Method method, Class<?> targetClass, Object... args) {
319                checkReadyToMatch();
320                ShadowMatch shadowMatch = getShadowMatch(AopUtils.getMostSpecificMethod(method, targetClass), method);
321                ShadowMatch originalShadowMatch = getShadowMatch(method, method);
322
323                // Bind Spring AOP proxy to AspectJ "this" and Spring AOP target to AspectJ target,
324                // consistent with return of MethodInvocationProceedingJoinPoint
325                ProxyMethodInvocation pmi = null;
326                Object targetObject = null;
327                Object thisObject = null;
328                try {
329                        MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
330                        targetObject = mi.getThis();
331                        if (!(mi instanceof ProxyMethodInvocation)) {
332                                throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
333                        }
334                        pmi = (ProxyMethodInvocation) mi;
335                        thisObject = pmi.getProxy();
336                }
337                catch (IllegalStateException ex) {
338                        // No current invocation...
339                        if (logger.isDebugEnabled()) {
340                                logger.debug("Could not access current invocation - matching with limited context: " + ex);
341                        }
342                }
343
344                try {
345                        JoinPointMatch joinPointMatch = shadowMatch.matchesJoinPoint(thisObject, targetObject, args);
346
347                        /*
348                         * Do a final check to see if any this(TYPE) kind of residue match. For
349                         * this purpose, we use the original method's (proxy method's) shadow to
350                         * ensure that 'this' is correctly checked against. Without this check,
351                         * we get incorrect match on this(TYPE) where TYPE matches the target
352                         * type but not 'this' (as would be the case of JDK dynamic proxies).
353                         * <p>See SPR-2979 for the original bug.
354                         */
355                        if (pmi != null) {  // there is a current invocation
356                                RuntimeTestWalker originalMethodResidueTest = getRuntimeTestWalker(originalShadowMatch);
357                                if (!originalMethodResidueTest.testThisInstanceOfResidue(thisObject.getClass())) {
358                                        return false;
359                                }
360                                if (joinPointMatch.matches()) {
361                                        bindParameters(pmi, joinPointMatch);
362                                }
363                        }
364
365                        return joinPointMatch.matches();
366                }
367                catch (Throwable ex) {
368                        if (logger.isDebugEnabled()) {
369                                logger.debug("Failed to evaluate join point for arguments " + Arrays.asList(args) +
370                                                " - falling back to non-match", ex);
371                        }
372                        return false;
373                }
374        }
375
376        protected String getCurrentProxiedBeanName() {
377                return ProxyCreationContext.getCurrentProxiedBeanName();
378        }
379
380
381        /**
382         * Get a new pointcut expression based on a target class's loader rather than the default.
383         */
384        private PointcutExpression getFallbackPointcutExpression(Class<?> targetClass) {
385                try {
386                        ClassLoader classLoader = targetClass.getClassLoader();
387                        if (classLoader != null && classLoader != this.pointcutClassLoader) {
388                                return buildPointcutExpression(classLoader);
389                        }
390                }
391                catch (Throwable ex) {
392                        logger.debug("Failed to create fallback PointcutExpression", ex);
393                }
394                return null;
395        }
396
397        private RuntimeTestWalker getRuntimeTestWalker(ShadowMatch shadowMatch) {
398                if (shadowMatch instanceof DefensiveShadowMatch) {
399                        return new RuntimeTestWalker(((DefensiveShadowMatch) shadowMatch).primary);
400                }
401                return new RuntimeTestWalker(shadowMatch);
402        }
403
404        private void bindParameters(ProxyMethodInvocation invocation, JoinPointMatch jpm) {
405                // Note: Can't use JoinPointMatch.getClass().getName() as the key, since
406                // Spring AOP does all the matching at a join point, and then all the invocations
407                // under this scenario, if we just use JoinPointMatch as the key, then
408                // 'last man wins' which is not what we want at all.
409                // Using the expression is guaranteed to be safe, since 2 identical expressions
410                // are guaranteed to bind in exactly the same way.
411                invocation.setUserAttribute(getExpression(), jpm);
412        }
413
414        private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
415                // Avoid lock contention for known Methods through concurrent access...
416                ShadowMatch shadowMatch = this.shadowMatchCache.get(targetMethod);
417                if (shadowMatch == null) {
418                        synchronized (this.shadowMatchCache) {
419                                // Not found - now check again with full lock...
420                                PointcutExpression fallbackExpression = null;
421                                Method methodToMatch = targetMethod;
422                                shadowMatch = this.shadowMatchCache.get(targetMethod);
423                                if (shadowMatch == null) {
424                                        try {
425                                                try {
426                                                        shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
427                                                }
428                                                catch (ReflectionWorldException ex) {
429                                                        // Failed to introspect target method, probably because it has been loaded
430                                                        // in a special ClassLoader. Let's try the declaring ClassLoader instead...
431                                                        try {
432                                                                fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
433                                                                if (fallbackExpression != null) {
434                                                                        shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
435                                                                }
436                                                        }
437                                                        catch (ReflectionWorldException ex2) {
438                                                                fallbackExpression = null;
439                                                        }
440                                                }
441                                                if (shadowMatch == null && targetMethod != originalMethod) {
442                                                        methodToMatch = originalMethod;
443                                                        try {
444                                                                shadowMatch = this.pointcutExpression.matchesMethodExecution(methodToMatch);
445                                                        }
446                                                        catch (ReflectionWorldException ex3) {
447                                                                // Could neither introspect the target class nor the proxy class ->
448                                                                // let's try the original method's declaring class before we give up...
449                                                                try {
450                                                                        fallbackExpression = getFallbackPointcutExpression(methodToMatch.getDeclaringClass());
451                                                                        if (fallbackExpression != null) {
452                                                                                shadowMatch = fallbackExpression.matchesMethodExecution(methodToMatch);
453                                                                        }
454                                                                }
455                                                                catch (ReflectionWorldException ex4) {
456                                                                        fallbackExpression = null;
457                                                                }
458                                                        }
459                                                }
460                                        }
461                                        catch (Throwable ex) {
462                                                // Possibly AspectJ 1.8.10 encountering an invalid signature
463                                                logger.debug("PointcutExpression matching rejected target method", ex);
464                                                fallbackExpression = null;
465                                        }
466                                        if (shadowMatch == null) {
467                                                shadowMatch = new ShadowMatchImpl(org.aspectj.util.FuzzyBoolean.NO, null, null, null);
468                                        }
469                                        else if (shadowMatch.maybeMatches() && fallbackExpression != null) {
470                                                shadowMatch = new DefensiveShadowMatch(shadowMatch,
471                                                                fallbackExpression.matchesMethodExecution(methodToMatch));
472                                        }
473                                        this.shadowMatchCache.put(targetMethod, shadowMatch);
474                                }
475                        }
476                }
477                return shadowMatch;
478        }
479
480
481        @Override
482        public boolean equals(Object other) {
483                if (this == other) {
484                        return true;
485                }
486                if (!(other instanceof AspectJExpressionPointcut)) {
487                        return false;
488                }
489                AspectJExpressionPointcut otherPc = (AspectJExpressionPointcut) other;
490                return ObjectUtils.nullSafeEquals(this.getExpression(), otherPc.getExpression()) &&
491                                ObjectUtils.nullSafeEquals(this.pointcutDeclarationScope, otherPc.pointcutDeclarationScope) &&
492                                ObjectUtils.nullSafeEquals(this.pointcutParameterNames, otherPc.pointcutParameterNames) &&
493                                ObjectUtils.nullSafeEquals(this.pointcutParameterTypes, otherPc.pointcutParameterTypes);
494        }
495
496        @Override
497        public int hashCode() {
498                int hashCode = ObjectUtils.nullSafeHashCode(this.getExpression());
499                hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutDeclarationScope);
500                hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterNames);
501                hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.pointcutParameterTypes);
502                return hashCode;
503        }
504
505        @Override
506        public String toString() {
507                StringBuilder sb = new StringBuilder();
508                sb.append("AspectJExpressionPointcut: ");
509                if (this.pointcutParameterNames != null && this.pointcutParameterTypes != null) {
510                        sb.append("(");
511                        for (int i = 0; i < this.pointcutParameterTypes.length; i++) {
512                                sb.append(this.pointcutParameterTypes[i].getName());
513                                sb.append(" ");
514                                sb.append(this.pointcutParameterNames[i]);
515                                if ((i+1) < this.pointcutParameterTypes.length) {
516                                        sb.append(", ");
517                                }
518                        }
519                        sb.append(")");
520                }
521                sb.append(" ");
522                if (getExpression() != null) {
523                        sb.append(getExpression());
524                }
525                else {
526                        sb.append("<pointcut expression not set>");
527                }
528                return sb.toString();
529        }
530
531
532        /**
533         * Handler for the Spring-specific {@code bean()} pointcut designator
534         * extension to AspectJ.
535         * <p>This handler must be added to each pointcut object that needs to
536         * handle the {@code bean()} PCD. Matching context is obtained
537         * automatically by examining a thread local variable and therefore a matching
538         * context need not be set on the pointcut.
539         */
540        private class BeanNamePointcutDesignatorHandler implements PointcutDesignatorHandler {
541
542                private static final String BEAN_DESIGNATOR_NAME = "bean";
543
544                @Override
545                public String getDesignatorName() {
546                        return BEAN_DESIGNATOR_NAME;
547                }
548
549                @Override
550                public ContextBasedMatcher parse(String expression) {
551                        return new BeanNameContextMatcher(expression);
552                }
553        }
554
555
556        /**
557         * Matcher class for the BeanNamePointcutDesignatorHandler.
558         * <p>Dynamic match tests for this matcher always return true,
559         * since the matching decision is made at the proxy creation time.
560         * For static match tests, this matcher abstains to allow the overall
561         * pointcut to match even when negation is used with the bean() pointcut.
562         */
563        private class BeanNameContextMatcher implements ContextBasedMatcher {
564
565                private final NamePattern expressionPattern;
566
567                public BeanNameContextMatcher(String expression) {
568                        this.expressionPattern = new NamePattern(expression);
569                }
570
571                @Override
572                @SuppressWarnings("rawtypes")
573                @Deprecated
574                public boolean couldMatchJoinPointsInType(Class someClass) {
575                        return (contextMatch(someClass) == FuzzyBoolean.YES);
576                }
577
578                @Override
579                @SuppressWarnings("rawtypes")
580                @Deprecated
581                public boolean couldMatchJoinPointsInType(Class someClass, MatchingContext context) {
582                        return (contextMatch(someClass) == FuzzyBoolean.YES);
583                }
584
585                @Override
586                public boolean matchesDynamically(MatchingContext context) {
587                        return true;
588                }
589
590                @Override
591                public FuzzyBoolean matchesStatically(MatchingContext context) {
592                        return contextMatch(null);
593                }
594
595                @Override
596                public boolean mayNeedDynamicTest() {
597                        return false;
598                }
599
600                private FuzzyBoolean contextMatch(Class<?> targetType) {
601                        String advisedBeanName = getCurrentProxiedBeanName();
602                        if (advisedBeanName == null) {  // no proxy creation in progress
603                                // abstain; can't return YES, since that will make pointcut with negation fail
604                                return FuzzyBoolean.MAYBE;
605                        }
606                        if (BeanFactoryUtils.isGeneratedBeanName(advisedBeanName)) {
607                                return FuzzyBoolean.NO;
608                        }
609                        if (targetType != null) {
610                                boolean isFactory = FactoryBean.class.isAssignableFrom(targetType);
611                                return FuzzyBoolean.fromBoolean(
612                                                matchesBeanName(isFactory ? BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName : advisedBeanName));
613                        }
614                        else {
615                                return FuzzyBoolean.fromBoolean(matchesBeanName(advisedBeanName) ||
616                                                matchesBeanName(BeanFactory.FACTORY_BEAN_PREFIX + advisedBeanName));
617                        }
618                }
619
620                private boolean matchesBeanName(String advisedBeanName) {
621                        if (this.expressionPattern.matches(advisedBeanName)) {
622                                return true;
623                        }
624                        if (beanFactory != null) {
625                                String[] aliases = beanFactory.getAliases(advisedBeanName);
626                                for (String alias : aliases) {
627                                        if (this.expressionPattern.matches(alias)) {
628                                                return true;
629                                        }
630                                }
631                        }
632                        return false;
633                }
634        }
635
636
637        //---------------------------------------------------------------------
638        // Serialization support
639        //---------------------------------------------------------------------
640
641        private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
642                // Rely on default serialization, just initialize state after deserialization.
643                ois.defaultReadObject();
644
645                // Initialize transient fields.
646                // pointcutExpression will be initialized lazily by checkReadyToMatch()
647                this.shadowMatchCache = new ConcurrentHashMap<Method, ShadowMatch>(32);
648        }
649
650
651        private static class DefensiveShadowMatch implements ShadowMatch {
652
653                private final ShadowMatch primary;
654
655                private final ShadowMatch other;
656
657                public DefensiveShadowMatch(ShadowMatch primary, ShadowMatch other) {
658                        this.primary = primary;
659                        this.other = other;
660                }
661
662                @Override
663                public boolean alwaysMatches() {
664                        return this.primary.alwaysMatches();
665                }
666
667                @Override
668                public boolean maybeMatches() {
669                        return this.primary.maybeMatches();
670                }
671
672                @Override
673                public boolean neverMatches() {
674                        return this.primary.neverMatches();
675                }
676
677                @Override
678                public JoinPointMatch matchesJoinPoint(Object thisObject, Object targetObject, Object[] args) {
679                        try {
680                                return this.primary.matchesJoinPoint(thisObject, targetObject, args);
681                        }
682                        catch (ReflectionWorldException ex) {
683                                return this.other.matchesJoinPoint(thisObject, targetObject, args);
684                        }
685                }
686
687                @Override
688                public void setMatchingContext(MatchingContext aMatchContext) {
689                        this.primary.setMatchingContext(aMatchContext);
690                        this.other.setMatchingContext(aMatchContext);
691                }
692        }
693
694}