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