Spring Aop matching source code parsing

I. Introduction

AOP (Aspect Oriented Programming) is called aspect-oriented programming. It is mainly used to solve some system-level problems in program development, such as logs, transactions, and permission waiting. The interceptor design of Struts2 is based on the idea of ​​AOP, which is a classic one. example.

Related concepts of AOP:

(1) Cross-cutting concerns: Which methods are intercepted and how to deal with them after interception, these concerns are called cross-cutting concerns
(2) Aspect (aspect): usually a class, which can define entry points and notifications
( 3) JointPoint (connection point): A clear point in the program execution process, generally a method call. The intercepted point, because Spring only supports method-type connection points, so in Spring, the connection point refers to the intercepted method. In fact, the connection point can also be a field or a constructor (4) Advice (notification)
: The enhanced processing performed by AOP on a specific entry point includes before (pre), after (post), afterReturning (final), afterThrowing (exception), around (surround) (5) Pointcut (entry point): is to
bring The connection point with notification is mainly reflected in the writing pointcut expression in the program
(6) weave (weaving): the process of applying the aspect to the target object and causing the creation of the proxy object
(7) introduction (introduction): without modifying Under the premise of the code, the introduction can dynamically add some methods or fields to the class at runtime
(8) AOP Proxy (AOP Proxy): The object created by the AOP framework, the proxy is the enhancement of the target object. The AOP proxy in Spring can be a JDK dynamic proxy or a CGLIB proxy. The former is based on the interface, and the latter is based on the subclass
(9) Target Object (Target Object): An object that contains a connection point. Also known as the advised or proxied object. POJOs

2. Source code analysis

The following is a source code analysis for Spring Boot's AOP usage. Spring Boot adds pom import

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
			<version>2.0.2.RELEASE</version>
		</dependency>

Then we create the aspect class

@Aspect
@Component
@EnableAspectJAutoProxy
public class ServiceAdvice {
    @Pointcut("@annotation(aoptest.Monitor)")
    public void annotation() {
    }


    @Pointcut("execution(public * aoptest.*.*(..))")
    public void webLog(){}

    @Before("webLog()")
    public void beforeMethodAdviceA(JoinPoint joinPoint){
        System.out.println("beforeMethodAdviceA");
    }

    @Before("annotation()")
    public void beforeMethodAdviceAnnotation(JoinPoint joinPoint){
        System.out.println("AdviceAnnotation");
    }

    @Before("annotation()")
    public void beforeMethodAdviceAnnotation2(JoinPoint joinPoint){
        System.out.println("AdviceAnnotation2");
    }

    @After("webLog()")
    public void afterMethodAdviceB(JoinPoint joinPoint){
        System.out.println("afterMethodAdviceB");
    }
}

In this way, enhancement processing will be performed before the corresponding method is executed.

First enter the @EnableAspectJAutoProxy annotation.

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
       //导入了AspectJAutoProxyRegistrar
}

The AspectJAutoProxyRegistrar class is imported.

class AspectJAutoProxyRegistrar implements ImportBeanDefinitionRegistrar {


	public void registerBeanDefinitions(
			AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

		AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);
            .........................
        }
        
	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry) {
		return registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry, null);
	}
    	public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(
			BeanDefinitionRegistry registry, @Nullable Object source) {

		return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
	}

The AnnotationAwareAspectJAutoProxyCreator object is imported through the AspectJAutoProxyRegistrar class. The AnnotationAwareAspectJAutoProxyCreator object implements the BeanPostProcessor interface.

The AnnotationAwareAspectJAutoProxyCreator.postProcessAfterInitialization method is executed when the Bean object is created.

	public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
		if (bean != null) {
			Object cacheKey = getCacheKey(bean.getClass(), beanName);
			if (this.earlyProxyReferences.remove(cacheKey) != bean) {
				return wrapIfNecessary(bean, beanName, cacheKey);
			}
		}
		return bean;
	}

	protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
		if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
			return bean;
		}
		if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
			return bean;
		}
		if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
			this.advisedBeans.put(cacheKey, Boolean.FALSE);
			return bean;
		}

		// Create proxy if we have advice.
		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

		this.advisedBeans.put(cacheKey, Boolean.FALSE);
		return bean;
	}

The getAdvicesAndAdvisorsForBean method is used to find the enhanced method and whether the Bean needs AOP processing.

	protected Object[] getAdvicesAndAdvisorsForBean(
			Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
                //找到增强集合
		List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
		if (advisors.isEmpty()) {
			return DO_NOT_PROXY;
		}
		return advisors.toArray();
	}
	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
                //找到实现了Advisor接口的类和标有@Aspect注解的类
		List<Advisor> candidateAdvisors = findCandidateAdvisors();
                //判断当前的Bean是否有方法适配AOP
		List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
		extendAdvisors(eligibleAdvisors);
		if (!eligibleAdvisors.isEmpty()) {
			eligibleAdvisors = sortAdvisors(eligibleAdvisors);
		}
		return eligibleAdvisors;
	}

	protected List<Advisor> findCandidateAdvisors() {
		// 找到所有实现Advisor接口的类
		List<Advisor> advisors = super.findCandidateAdvisors();
		// Build Advisors for all AspectJ aspects in the bean factory.
		if (this.aspectJAdvisorsBuilder != null) {
                //找到所有标有@Aspect的类			
                advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
		}
		return advisors;
	}
	public List<Advisor> buildAspectJAdvisors() {
            .......................
            	String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
							this.beanFactory, Object.class, true, false);
            ...............
            	if (this.advisorFactory.isAspect(beanType)) {
                    ...........................
                       //此方法将@Aspect类中的包含Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class的增强方法找到
                    	List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);
                    ...........
                }
        }

//Judge whether the current Bean has a method to adapt to AOP
List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);

	protected List<Advisor> findAdvisorsThatCanApply(
			List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {
		ProxyCreationContext.setCurrentProxiedBeanName(beanName);
		try {
			return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);
		}
		finally {
			ProxyCreationContext.setCurrentProxiedBeanName(null);
		}
	}

	public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
		if (candidateAdvisors.isEmpty()) {
			return candidateAdvisors;
		}
		List<Advisor> eligibleAdvisors = new ArrayList<>();
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
				eligibleAdvisors.add(candidate);
			}
		}
		boolean hasIntroductions = !eligibleAdvisors.isEmpty();
		for (Advisor candidate : candidateAdvisors) {
			if (candidate instanceof IntroductionAdvisor) {
				// already processed
				continue;
			}
                        //判断是否适配
			if (canApply(candidate, clazz, hasIntroductions)) {
				eligibleAdvisors.add(candidate);
			}
		}
		return eligibleAdvisors;
	}
	public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
		if (advisor instanceof IntroductionAdvisor) {
			return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
		}
		else if (advisor instanceof PointcutAdvisor) {
			PointcutAdvisor pca = (PointcutAdvisor) advisor;
			return canApply(pca.getPointcut(), targetClass, hasIntroductions);
		}
		else {
			// It doesn't have a pointcut so we assume it applies.
			return true;
		}
	}

	public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
		Assert.notNull(pc, "Pointcut must not be null");
		if (!pc.getClassFilter().matches(targetClass)) {
			return false;
		}

		MethodMatcher methodMatcher = pc.getMethodMatcher();
		if (methodMatcher == MethodMatcher.TRUE) {
			// No need to iterate the methods if we're matching any method anyway...
			return true;
		}

		IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
		if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
			introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
		}

		Set<Class<?>> classes = new LinkedHashSet<>();
		if (!Proxy.isProxyClass(targetClass)) {
			classes.add(ClassUtils.getUserClass(targetClass));
		}
		classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

		for (Class<?> clazz : classes) {
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method method : methods) {
				if (introductionAwareMethodMatcher != null ?
					//判断当前类是否有方法适配当前adviors
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}

In the canApply method, the pc.getClassFilter().matches(targetClass) method will perform a quick match (only to see if the class matches). The couldMatchJoinPointsInType method of the PointcutExpressionImpl class will be called.

	public boolean couldMatchJoinPointsInType(Class aClass) {
		ResolvedType matchType = world.resolve(aClass.getName());
		if (matchType.isMissing() && (world instanceof ReflectionWorld)) {
			// Class is a generated class that cannot be 'looked up' via getResource.
			// For example a proxy or lambda.
			// Use the class itself in this case
			matchType = ((ReflectionWorld)world).resolveUsingClass(aClass);
		}
		ReflectionFastMatchInfo info = new ReflectionFastMatchInfo(matchType, null, this.matchContext, world);
		boolean couldMatch = pointcut.fastMatch(info).maybeTrue();
		if (MATCH_INFO) {
			System.out.println("MATCHINFO: fast match for '" + this.expression + "' against '" + aClass.getName() + "': "
					+ couldMatch);
		}
		return couldMatch;
	}

Call the fastMatch method through a specific pointcut. For example, AnnotationPointcut will judge whether the method has corresponding annotations, and usually the one that uses the most execution expressions will enter the fastMatch method of KindedPointcut.

The method will first parse @Pointcut to see if it is adapted to an interface or an entity class. Determine whether the Bean object is suitable. If the expression is not suitable, there are two situations:

1. If the point-cutting point is an entity class, first judge whether the Bean class is the same as the point-cutting class. If not, it will check whether the parent class is compatible.

2. If the cut point is an interface, first judge whether the Bean class is the same as the cut point class, if not, then check whether the interface class is a class adapted to the aspect.

That is to say, if a subclass A, parent class SuperA, if the pointcut defines the parent class A @Pointcut("execution(public * aoptest.SuperA.*(..))"), then the pointcut call is any method of subclass A, then AOP enhancement will also be performed.

Enter the matching method

	public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
		obtainPointcutExpression();
		ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);

		if (shadowMatch.alwaysMatches()) {
			return true;
		}
		else if (shadowMatch.neverMatches()) {
			return false;
		}
		else {
			// the maybe case
			if (hasIntroductions) {
				return true;
			}
			RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
			return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
		}
	}

The obtainPointcutExpression method is used to obtain the parsing class corresponding to the cut point.

	private PointcutExpression obtainPointcutExpression() {
		if (getExpression() == null) {
			throw new IllegalStateException("Must set property 'expression' before attempting to match");
		}
		if (this.pointcutExpression == null) {
			this.pointcutClassLoader = determinePointcutClassLoader();
			this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
		}
		return this.pointcutExpression;
	}
	private PointcutExpression buildPointcutExpression(@Nullable ClassLoader classLoader) {
		PointcutParser parser = initializePointcutParser(classLoader);
		PointcutParameter[] pointcutParameters = new PointcutParameter[this.pointcutParameterNames.length];
		for (int i = 0; i < pointcutParameters.length; i++) {
			pointcutParameters[i] = parser.createPointcutParameter(
					this.pointcutParameterNames[i], this.pointcutParameterTypes[i]);
		}
		return parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()),
				this.pointcutDeclarationScope, pointcutParameters);
	}

initializePointcutParser initializes the pointcut class parsing object

	private PointcutParser initializePointcutParser(@Nullable ClassLoader classLoader) {
		PointcutParser parser = PointcutParser
				.getPointcutParserSupportingSpecifiedPrimitivesAndUsingSpecifiedClassLoaderForResolution(
						SUPPORTED_PRIMITIVES, classLoader);
		parser.registerPointcutDesignatorHandler(new BeanPointcutDesignatorHandler());
		return parser;
	}
        //其中SUPPORTED_PRIMITIVES包含各种pointcut类型,如EXECUTION、annotation等
    	private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = new HashSet<>();

	static {
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.EXECUTION);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.ARGS);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.REFERENCE);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.THIS);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.TARGET);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.WITHIN);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ANNOTATION);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_WITHIN);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_ARGS);
		SUPPORTED_PRIMITIVES.add(PointcutPrimitive.AT_TARGET);
	}

parser.parsePointcutExpression(replaceBooleanOperators(resolveExpression()), this.pointcutDeclarationScope, pointcutParameters) is used to create the PointCut parsing class corresponding to the current advisors.

	public PointcutExpression parsePointcutExpression(String expression, Class<?> inScope, PointcutParameter[] formalParameters)
			throws UnsupportedPointcutPrimitiveException, IllegalArgumentException {
		PointcutExpressionImpl pcExpr = null;
		try {
                        //解析切点表达式,用于创建对应的PointCut解析类
			Pointcut pc = resolvePointcutExpression(expression, inScope, formalParameters);
			pc = concretizePointcutExpression(pc, inScope, formalParameters);
			validateAgainstSupportedPrimitives(pc, expression); // again, because we have now followed any ref'd pcuts
			pcExpr = new PointcutExpressionImpl(pc, expression, formalParameters, getWorld());
		} catch (ParserException pEx) {
			throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
		} catch (ReflectionWorld.ReflectionWorldException rwEx) {
			throw new IllegalArgumentException(rwEx.getMessage());
		}
		return pcExpr;
	}
	protected Pointcut resolvePointcutExpression(String expression, Class<?> inScope, PointcutParameter[] formalParameters) {
		try {
			PatternParser parser = new PatternParser(expression);
			parser.setPointcutDesignatorHandlers(pointcutDesignators, world);
                        //创建PointCut实现类
			Pointcut pc = parser.parsePointcut(); // more correctly: parsePointcut(true)
			validateAgainstSupportedPrimitives(pc, expression);
			IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters);
			pc = pc.resolve(resolutionScope);
			return pc;
		} catch (ParserException pEx) {
			throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
		}
	}

Create a PointCut implementation class through parser.parsePointcut()

	public Pointcut parsePointcut() {
		Pointcut p = parseAtomicPointcut();
		if (maybeEat("&&")) {
			p = new AndPointcut(p, parseNotOrPointcut());
		}

		if (maybeEat("||")) {
			p = new OrPointcut(p, parsePointcut());
		}

		return p;
	}

    	private Pointcut parseAtomicPointcut() {
		if (maybeEat("!")) {
			int startPos = tokenSource.peek(-1).getStart();
			Pointcut p = new NotPointcut(parseAtomicPointcut(), startPos);
			return p;
		}
		if (maybeEat("(")) {
			Pointcut p = parsePointcut();
			eat(")");
			return p;
		}
                //如果Adviors的表达式是@开头,则创建AnnotationPointcut类
		if (maybeEat("@")) {
			int startPos = tokenSource.peek().getStart();
			Pointcut p = parseAnnotationPointcut();
			int endPos = tokenSource.peek(-1).getEnd();
			p.setLocation(sourceContext, startPos, endPos);
			return p;
		}
		int startPos = tokenSource.peek().getStart();
                //之前实例类创建的是  @Before("annotation()") 和   @Before("webLog()")会进入parseSinglePointcut方法寻找
		Pointcut p = parseSinglePointcut();
		int endPos = tokenSource.peek(-1).getEnd();
		p.setLocation(sourceContext, startPos, endPos);
		return p;
	}
        
	public Pointcut parseSinglePointcut() {
		int start = tokenSource.getIndex();
		IToken t = tokenSource.peek();
		Pointcut p = t.maybeGetParsedPointcut();
		if (p != null) {
			tokenSource.next();
			return p;
		}

		String kind = parseIdentifier();
	        //这里也没有适配的,我们传入的是annotation() 和 webLog()
		if (kind.equals("execution") || kind.equals("call") || kind.equals("get") || kind.equals("set")) {
			p = parseKindedPointcut(kind);
		} else if (kind.equals("args")) {
			p = parseArgsPointcut();
		} else if (kind.equals("this")) {
			p = parseThisOrTargetPointcut(kind);
		} else if (kind.equals("target")) {
			p = parseThisOrTargetPointcut(kind);
		} else if (kind.equals("within")) {
			p = parseWithinPointcut();
		} else if (kind.equals("withincode")) {
			p = parseWithinCodePointcut();
		} else if (kind.equals("cflow")) {
			p = parseCflowPointcut(false);
		} else if (kind.equals("cflowbelow")) {
			p = parseCflowPointcut(true);
		} else if (kind.equals("adviceexecution")) {
			eat("(");
			eat(")");
			p = new KindedPointcut(Shadow.AdviceExecution, new SignaturePattern(Member.ADVICE, ModifiersPattern.ANY,
					TypePattern.ANY, TypePattern.ANY, NamePattern.ANY, TypePatternList.ANY, ThrowsPattern.ANY,
					AnnotationTypePattern.ANY));
		} else if (kind.equals("handler")) {
			eat("(");
			TypePattern typePat = parseTypePattern(false, false);
			eat(")");
			p = new HandlerPointcut(typePat);
		} else if (kind.equals("lock") || kind.equals("unlock")) {
			p = parseMonitorPointcut(kind);
		} else if (kind.equals("initialization")) {
			eat("(");
			SignaturePattern sig = parseConstructorSignaturePattern();
			eat(")");
			p = new KindedPointcut(Shadow.Initialization, sig);
		} else if (kind.equals("staticinitialization")) {
			eat("(");
			TypePattern typePat = parseTypePattern(false, false);
			eat(")");
			p = new KindedPointcut(Shadow.StaticInitialization, new SignaturePattern(Member.STATIC_INITIALIZATION,
					ModifiersPattern.ANY, TypePattern.ANY, typePat, NamePattern.ANY, TypePatternList.EMPTY, ThrowsPattern.ANY,
					AnnotationTypePattern.ANY));
		} else if (kind.equals("preinitialization")) {
			eat("(");
			SignaturePattern sig = parseConstructorSignaturePattern();
			eat(")");
			p = new KindedPointcut(Shadow.PreInitialization, sig);
		} else if (kind.equals("if")) {
			// - annotation style only allows if(), if(true) or if(false)
			// - if() means the body of the annotated method represents the if expression
			// - anything else is an error because code cannot be put into the if()
			// - code style will already have been processed and the call to maybeGetParsedPointcut()
			// at the top of this method will have succeeded.
			eat("(");
			if (maybeEatIdentifier("true")) {
				eat(")");
				p = new IfPointcut.IfTruePointcut();
			} else if (maybeEatIdentifier("false")) {
				eat(")");
				p = new IfPointcut.IfFalsePointcut();
			} else {
				if (!maybeEat(")")) {
					throw new ParserException(
							"in annotation style, if(...) pointcuts cannot contain code. Use if() and put the code in the annotated method",
							t);
				}
				// TODO - Alex has some token stuff going on here to get a readable name in place of ""...
				p = new IfPointcut("");
			}
		} else {
			boolean matchedByExtensionDesignator = false;
			// see if a registered handler wants to parse it, otherwise
			// treat as a reference pointcut
			for (PointcutDesignatorHandler pcd : pointcutDesignatorHandlers) {
				if (pcd.getDesignatorName().equals(kind)) {
					p = parseDesignatorPointcut(pcd);
					matchedByExtensionDesignator = true;
				}

			}
			if (!matchedByExtensionDesignator) {
				tokenSource.setIndex(start);
                                //annotation() 和  webLog() 会进入这里
				p = parseReferencePointcut();
			}
		}
		return p;
	}
        

Since our Advisors create @Before("webLog()") and @Before("annotation()"), it will go to the end parseReferencePointcut to create a PointCut of reference type.

Continue to return to the previous resolvePointcutExpression method

	protected Pointcut resolvePointcutExpression(String expression, Class<?> inScope, PointcutParameter[] formalParameters) {
		try {
			PatternParser parser = new PatternParser(expression);
			parser.setPointcutDesignatorHandlers(pointcutDesignators, world);
                        //创建完ReferencePointcut类后
			Pointcut pc = parser.parsePointcut(); // more correctly: parsePointcut(true)
			validateAgainstSupportedPrimitives(pc, expression);
			IScope resolutionScope = buildResolutionScope((inScope == null ? Object.class : inScope), formalParameters);
                        //这里会解析ReferencePointcut这正的对应@PointCut的方法,找到真正的Pointcut解析类
			pc = pc.resolve(resolutionScope);
			return pc;
		} catch (ParserException pEx) {
			throw new IllegalArgumentException(buildUserMessageFromParserException(expression, pEx));
		}
	}

Here we will analyze the method of ReferencePointcut which corresponds to @PointCut, and find the real Pointcut analysis class.

	public final Pointcut resolve(IScope scope) {
		assertState(SYMBOLIC);
		Bindings bindingTable = new Bindings(scope.getFormalCount());
		IScope bindingResolutionScope = scope;
		if (typeVariablesInScope.length > 0) {
			bindingResolutionScope = new ScopeWithTypeVariables(typeVariablesInScope, scope);
		}
                //根据引用切点方法名称,找到对应的point里真正的要创建的PointCut
		this.resolveBindings(bindingResolutionScope, bindingTable);
		bindingTable.checkAllBound(bindingResolutionScope);
		this.state = RESOLVED;
		return this;
	}
        
	public void resolveBindings(IScope scope, Bindings bindings) {
		//找到对应名字的pointCut
		ResolvedPointcutDefinition pointcutDef = searchType.findPointcut(name);
                ............
        }

	public ResolvedPointcutDefinition findPointcut(String name) {
		for (Iterator<ResolvedMember> i = getPointcuts(); i.hasNext();) {
			ResolvedPointcutDefinition f = (ResolvedPointcutDefinition) i.next();
			// the ResolvedPointcutDefinition can be null if there are other problems that prevented its resolution
			if (f != null && name.equals(f.getName())) {
				return f;
			}
		}
        }
        
        //getPointcuts()方法会调用PointcutGetter.get 方法,会调用getDeclaredPointcuts()
	private static class PointcutGetter implements Iterators.Getter<ResolvedType, ResolvedMember> {
		@Override
		public Iterator<ResolvedMember> get(ResolvedType o) {
			return Iterators.array(o.getDeclaredPointcuts());
		}
	}

The Java15ReflectionBasedReferenceTypeDelegate.getDeclaredPointcuts method will be called.

	public ResolvedMember[] getDeclaredPointcuts() {
		if (pointcuts == null) {
			Pointcut[] pcs = this.myType.getDeclaredPointcuts();
                    ......
                }
			for (int i = 0; i < pcs.length; i++) {
				String pcExpr = pcs[i].getPointcutExpression().toString();
				org.aspectj.weaver.patterns.Pointcut pc = parser.resolvePointcutExpression(pcExpr, getBaseClass(), parameters[i]);
				((ResolvedPointcutDefinition) pointcuts[i]).setParameterNames(pnames);
				((ResolvedPointcutDefinition) pointcuts[i]).setPointcut(pc);
			}
        }

	public Pointcut[] getDeclaredPointcuts() {
		if (declaredPointcuts != null) return declaredPointcuts;
		List<Pointcut> pointcuts = new ArrayList<Pointcut>();
		Method[] methods = clazz.getDeclaredMethods();
		for (Method method : methods) {
			Pointcut pc = asPointcut(method);
			if (pc != null) pointcuts.add(pc);
		}
		Pointcut[] ret = new Pointcut[pointcuts.size()];
		pointcuts.toArray(ret);
		declaredPointcuts = ret;
		return ret;
	}

	private Pointcut asPointcut(Method method) {
		org.aspectj.lang.annotation.Pointcut pcAnn = method.getAnnotation(org.aspectj.lang.annotation.Pointcut.class);
		if (pcAnn != null) {
			String name = method.getName();
			if (name.startsWith(ajcMagic)) {
				// extract real name
				int nameStart = name.indexOf("$$");
				name = name.substring(nameStart +2,name.length());
				int nextDollar = name.indexOf("$");
				if (nextDollar != -1) name = name.substring(0,nextDollar);
			}
			return new PointcutImpl(name,pcAnn.value(),method,AjTypeSystem.getAjType(method.getDeclaringClass()),pcAnn.argNames());
		} else {
			return null;
		}
	}

Use Java15ReflectionBasedReferenceTypeDelegate to find the methods annotated with @Pointcut in the Aspect class corresponding to the Advisors method. Then execute parser.resolvePointcutExpression(pcExpr, getBaseClass(), parameters[i]) on these methods again for parsing. Create the corresponding PointCut class.

continue back to matching method

	public boolean matches(Method method, Class<?> targetClass, boolean hasIntroductions) {
		obtainPointcutExpression();
		ShadowMatch shadowMatch = getTargetShadowMatch(method, targetClass);

		if (shadowMatch.alwaysMatches()) {
			return true;
		}
		else if (shadowMatch.neverMatches()) {
			return false;
		}
		else {
			// the maybe case
			if (hasIntroductions) {
				return true;
			}
			RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch);
			return (!walker.testsSubtypeSensitiveVars() || walker.testTargetInstanceOfResidue(targetClass));
		}
	}

Execute the getTargetShadowMatch method. The function of this method is mainly to judge whether the Bean has a method to match these Advisors.

	private ShadowMatch getShadowMatch(Method targetMethod, Method originalMethod) {
		..............
		try {
		    shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);
                }
        }
	public ShadowMatch matchesMethodExecution(Method aMethod) {
		ShadowMatch match = matchesExecution(aMethod);
		if (MATCH_INFO && match.maybeMatches()) {
			System.out.println("MATCHINFO: method execution match on '" + aMethod + "' for '" + this.expression + "': "
					+ (match.alwaysMatches() ? "YES" : "MAYBE"));
		}
		return match;
	}
	private ShadowMatchImpl getShadowMatch(Shadow forShadow) {
                //之前创建的对应PointCut进行匹配判断
		org.aspectj.util.FuzzyBoolean match = pointcut.match(forShadow);
		Test residueTest = Literal.TRUE;
		ExposedState state = getExposedState();
		if (match.maybeTrue()) {
			residueTest = pointcut.findResidue(forShadow, state);
		}
		ShadowMatchImpl sm = new ShadowMatchImpl(match, residueTest, state, parameters);
		sm.setMatchingContext(this.matchContext);
		return sm;
	}
            

Enter the matching method matchInternal

	public final FuzzyBoolean match(Shadow shadow) {
		if (shadow.shadowId == lastMatchedShadowId) {
			return lastMatchedShadowResult;
		}
		FuzzyBoolean ret;
		// this next test will prevent a lot of un-needed matching going on....
		if (shadow.getKind().isSet(couldMatchKinds())) {
			ret = matchInternal(shadow);
		} else {
			ret = FuzzyBoolean.NO;
		}
		lastMatchedShadowId = shadow.shadowId;
		lastMatchedShadowResult = ret;
		return ret;
	}

Here we take the @annotation annotation as an example, and the rest are similar.

        protected FuzzyBoolean matchInternal(Shadow shadow) {
		AnnotatedElement toMatchAgainst = null;
		Member member = shadow.getSignature();
		ResolvedMember rMember = member.resolve(shadow.getIWorld());

		...................

		annotationTypePattern.resolve(shadow.getIWorld());
		return annotationTypePattern.matches(toMatchAgainst);
	}

	public FuzzyBoolean matches(AnnotatedElement annotated, ResolvedType[] parameterAnnotations) {
		if (!isForParameterAnnotationMatch()) {
			boolean checkSupers = false;
			if (getResolvedAnnotationType().isInheritedAnnotation()) {
				if (annotated instanceof ResolvedType) {
					checkSupers = true;
				}
			}

			if (annotated.hasAnnotation(annotationType)) {
				if (annotationType instanceof ReferenceType) {
					ReferenceType rt = (ReferenceType) annotationType;
					if (rt.getRetentionPolicy() != null && rt.getRetentionPolicy().equals("SOURCE")) {
						rt.getWorld()
								.getMessageHandler()
								.handleMessage(
										MessageUtil.warn(WeaverMessages.format(WeaverMessages.NO_MATCH_BECAUSE_SOURCE_RETENTION,
												annotationType, annotated), getSourceLocation()));
						return FuzzyBoolean.NO;
					}
				}
                ................
        }

The ExactAnnotationTypePattern.matches method will be called to determine whether the annotation is consistent with the annotation on the method. If they are the same, the method needs to be enhanced with AOP.

	public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {

		for (Class<?> clazz : classes) {
			Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
			for (Method method : methods) {
				if (introductionAwareMethodMatcher != null ?
						introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
						methodMatcher.matches(method, targetClass)) {
					return true;
				}
			}
		}

		return false;
	}

It can be seen from canApply that if any method in a Bean satisfies the Advoirs, then return true directly. This is because even if one method is successfully matched, this class needs to create a dynamic proxy, so other methods in this bean do not need to be judged in a loop for the time being.

In the postProcessAfterInitialization method

		Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
		if (specificInterceptors != DO_NOT_PROXY) {
			this.advisedBeans.put(cacheKey, Boolean.TRUE);
			Object proxy = createProxy(
					bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
			this.proxyTypes.put(cacheKey, proxy.getClass());
			return proxy;
		}

The dynamic proxy created by createProxy will again judge whether the specific method is suitable for Advisors, and put it in the cache.

	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
			@Nullable Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}

		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);

		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}

		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		proxyFactory.addAdvisors(advisors);
		proxyFactory.setTargetSource(targetSource);
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}

		return proxyFactory.getProxy(getProxyClassLoader());
	}

There are mainly two types of agents created: JdkDynamicAopProxy and CglibAopProxy. Take the invoke method of JdkDynamicAopProxy as an example.

	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            ..........................
	    List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
            ..........................
        }

	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
		MethodCacheKey cacheKey = new MethodCacheKey(method);
		List<Object> cached = this.methodCache.get(cacheKey);
		if (cached == null) {
			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
			this.methodCache.put(cacheKey, cached);
		}
		return cached;
	}

	public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		Advisor[] advisors = config.getAdvisors();
		List<Object> interceptorList = new ArrayList<>(advisors.length);
		Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
		Boolean hasIntroductions = null;

		for (Advisor advisor : advisors) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			else if (advisor instanceof IntroductionAdvisor) {
				IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
				if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
					Interceptor[] interceptors = registry.getInterceptors(advisor);
					interceptorList.addAll(Arrays.asList(interceptors));
				}
			}
			else {
				Interceptor[] interceptors = registry.getInterceptors(advisor);
				interceptorList.addAll(Arrays.asList(interceptors));
			}
		}

		return interceptorList;
	}

Because the previous verification is that if any method in this class conforms to @Apsect, the dynamic proxy will be created directly. What is judged here is whether the specific calling method needs to be enhanced. And put the enhancements and corresponding methods that need to increase AOP into the cache.

The above is the AOP matching source code analysis.

AOP matching source code parsing summary:

1. Through the @EnableAspectJAutoProxy annotation, import the AnnotationAwareAspectJAutoProxyCreator class, and the AnnotationAwareAspectJAutoProxyCreator class implements the BeanPostProcessor interface. In the postProcessAfterInitialization method, look for a class that contains @Apsect and implements the Advisors interface. Find all enhanced methods that contain @Around, @Before, @After, @AfterReturning, @AfterThrowing.

 

2. Match through the AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply method. The matching steps are as follows:

  1) Enter the AspectJExpressionPointcut.matches method for matching. Call the AspectJExpressionPointcut.buildPointcutExpression method to build the analytical expression. First, for pointcut method name analysis referenced by @Aound, @Before, @After, etc., such as @Before("webLog()") and @Before("annotation()"), a ReferencePointcut class will be created first.

  2) Then call the ReferencePointcut.resolveBindings method to find the @PointCut methods whose reference names are webLog() and annotation(). And by finally calling the Java15ReflectionBasedReferenceTypeDelegate.getDeclaredPointcuts method, the found @PointCut is parsed again to obtain the real Pointcut parsing class.

  3) The previously created ReferencePointcut will call the concreteize1 method again to find the real Pointcut analysis class. Then do the matching.

3. If any method in a Bean satisfies the Advoirs, then return true directly. This is because even if a method matches successfully, the class needs to create a dynamic proxy.

4. Finally, judge whether the specific calling method needs to be enhanced through the dynamic proxy. And put the enhancements and corresponding methods that need to increase AOP into the cache

 

Guess you like

Origin blog.csdn.net/yytree123/article/details/109317709