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 && 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}