Spring source code analysis the third bullet - AOP aspect programming analysis

Posted by mpharo on Wed, 29 Dec 2021 03:35:49 +0100

Finally to AOP, the front did not understand must follow the code to read more, otherwise this article is easy to get lost!
If you don't say much, you can't change without its sect. First, the picture above.

AOP sequence diagram

Source code analysis

1. Initialization phase - > method of matching tangent points

  • Go back to abstractautowirecapablebeanfactory in the previous article Docreatebean mainly focuses on the code behind dependency injection
//Find here
//Dependency injection
populateBean(beanName, mbd, instanceWrapper);
//Initialize the AOP aspect entry of the initial method in the bean. This article focuses on this
exposedObject = initializeBean(beanName, exposedObject, mbd);

//The Bean object created when the container is initialized, and add a BeanPostProcessor post processor to it
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
	if (System.getSecurityManager() != null) {
		AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
			invokeAwareMethods(beanName, bean);
			return null;
		}, getAccessControlContext());
	}
	else {
        //Call all callbacks that implement BeanNameAware, BeanClassLoaderAware, beanfactory aware interface methods
        //Wrap the Bean instance object with relevant attributes, such as name, class loader, container, etc
		invokeAwareMethods(beanName, bean);
	}

	Object wrappedBean = bean;
	if (mbd == null || !mbd.isSynthetic()) {
        //Call the callback method to do some processing for the Bean instance before initialization
		wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
	}

	try {
        //Specified in the file through the init method attribute or @ PostConstruct
        //Some implementations of InitializingBean call the post notification afterpropertieset
        //MVC mapping is explained here when the callback triggers the next MVC
		invokeInitMethods(beanName, wrappedBean, mbd);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(
				(mbd != null ? mbd.getResourceDescription() : null),
				beanName, "Invocation of init method failed", ex);
	}
	if (mbd == null || !mbd.isSynthetic()) {
        //Call the callback method to do some processing for the Bean instance after initialization. The AOP agent mainly does this
		wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
	}
  		//Return object
	return wrappedBean;
}



//Call the processing method after the initialization of the BeanPostProcessor post processor instance object
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
		throws BeansException {

	Object result = existingBean;
	//Traverse the container and call back all post processors that implement the BeanPostProcessor interface for the created Bean
	for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
		//Do some custom processing operations for the Bean instance object after initialization. Next, let's go
		Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
		if (current == null) {
			return result;
		}
		result = current;
	}
	return result;
}
  • Next, go to abstractautoproxycreator #postprocessafterinitialization - > wrapifnecessary under the spring AOP package
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.
	//If there is advice, create a proxy class. First look at obtaining the advice chain, and then look at creating a proxy object
	Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
	//If this array is not equal to null
	//protected static final Object[] DO_NOT_PROXY = null;
	if (specificInterceptors != DO_NOT_PROXY) {
		//This bean is identified as having advice
		this.advisedBeans.put(cacheKey, Boolean.TRUE);
		//Create proxy object
		Object proxy = createProxy(
				bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
		this.proxyTypes.put(cacheKey, proxy.getClass());
		//The proxy object is returned
		return proxy;
	}

	this.advisedBeans.put(cacheKey, Boolean.FALSE);
	return bean;
}
  • Next, let's look at the logic of obtaining interceptor chain advice, and go down to abstractadvisor autoproxycreator getAdvicesAndAdvisorsForBean
protected Object[] getAdvicesAndAdvisorsForBean(
		Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
	//Qualified interceptors found
	List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
	if (advisors.isEmpty()) {
		return DO_NOT_PROXY;
	}
	return advisors.toArray();
}

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    //Load the currently configured AOP list
	List<Advisor> candidateAdvisors = findCandidateAdvisors();
    //Match according to beanName. Let's briefly analyze this
	List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
	extendAdvisors(eligibleAdvisors);
	if (!eligibleAdvisors.isEmpty()) {
        //sort
		eligibleAdvisors = sortAdvisors(eligibleAdvisors);
	}
    //Return to execution responsibility chain
	return eligibleAdvisors;
}
  • Findadvisors that can apply all the way down to aoputils canApply
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) {
        //Get all methods
		Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
		for (Method method : methods) {
            //Breakpoint debugging go to introductionawaremethodmatcher Matches, next look here
			if (introductionAwareMethodMatcher != null ?
					introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
					methodMatcher.matches(method, targetClass)) {
				return true;
			}
		}
	}
	return false;
}
  • introductionAwareMethodMatcher.matches -> AspectJExpressionPointcut.getShadowMatch
//Find this line and verify whether the method meets the pointcut through AOP rules
shadowMatch = obtainPointcutExpression().matchesMethodExecution(methodToMatch);

If the currently configured pointcut is satisfied, create a proxy object in AbstractAutoProxyCreator#wrapIfNecessary and return. The initialization phase is completed here. Next, continue to analyze the proxy source code, that is, the code weaving phase

2. Code weaving phase - > create dynamic agent

  • Go back to abstractautoproxycreator wrapIfNecessary
//Find this paragraph
//Get interceptor chain
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//If it is not empty, a proxy object is created
if (specificInterceptors != DO_NOT_PROXY) {
    this.advisedBeans.put(cacheKey, Boolean.TRUE);
    //Create a proxy object. Let's go here!
    Object proxy = createProxy(
        bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
    this.proxyTypes.put(cacheKey, proxy.getClass());
    return proxy;
}

//Create proxy object
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);
    }
	//Create agent factory
    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
	//Packaging actuator chain
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    proxyFactory.addAdvisors(advisors);
    //The information of the original class object encapsulates a layer of SingletonTargetSource
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
	//Create proxy - > there are two methods to create proxy: CGLIB and JDK. Let's choose JdkDynamicAopProxy
    return proxyFactory.getProxy(getProxyClassLoader());
}
  • Next, go to ProxyFactory#getProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
	return createAopProxy().getProxy(classLoader);
}

//createAopProxy -> ProxyCreatorSupport#createAopProxy
protected final synchronized AopProxy createAopProxy() {
	if (!this.active) {
		activate();
	}
	//It's this
	return getAopProxyFactory().createAopProxy(this);
}

//Get jdk or Cglib object
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
	if (!NativeDetector.inNativeImage() &&
			(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
		//Original class information
		Class<?> targetClass = config.getTargetClass();
		if (targetClass == null) {
			throw new AopConfigException("TargetSource cannot determine target class: " +
					"Either an interface or a target is required for proxy creation.");
		}
		if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
			//Save advice information AdvisedSupport through construction method 
			return new JdkDynamicAopProxy(config);
		}
		return new ObjenesisCglibAopProxy(config);
	}
	else {
		return new JdkDynamicAopProxy(config);
	}
}

//The jdk agent has been used as an example
public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
	Assert.notNull(config, "AdvisedSupport must not be null");
	if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
		throw new AopConfigException("No advisors and no TargetSource specified");
	}
	this.advised = config;
	//Get the interface of the original class. The JKD dynamic proxy must have an interface
	this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
	findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
}

  • Next, analyze getproxy - > jdkdynamicaopproxy getProxy
public Object getProxy(@Nullable ClassLoader classLoader) {
	if (logger.isTraceEnabled()) {
		logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
	}
	Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
	findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
	//Very familiar with the way to create an agent. If you don't understand it, look at the dynamic agent first
	return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

At this point, the proxy object is created, and the proxy object is returned to the IOC container.

3. Runtime - > reflection call of dynamic agent

  • When you call a method, you get the proxy object and paste the JdkDynamicAopProxy proxy class
//As you can see above, if the execution callback method is this, it means that the proxy object will call the invoke method of the current class
Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);

//Next, look at the invoker method
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
	Object oldProxy = null;
	boolean setProxyContext = false;
	//Gets the SingletonTargetSource encapsulated by the original class
	TargetSource targetSource = this.advised.targetSource;
	Object target = null;

	try {
		//A series of judgments are deleted
		......

		//Get original class
		target = targetSource.getTarget();
		//Class information
		Class<?> targetClass = (target != null ? target.getClass() : null);

		//The meaning of chain / pipeline obtains the interceptor pipeline
		//All the way down is actually wrapping it and saving the cache interceptorlist add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
		//interceptor is registry getInterceptors(advisor); Initialize the pre and post exceptions through the constructor
		List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
		if (chain.isEmpty()) {
			//The pipe is empty
			Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
			//Call the original class directly through reflection
			retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
		}
		else {
			// We need to create a method invocation...
			MethodInvocation invocation =
					new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
			//Start calling the pipe. Let's go here
			retVal = invocation.proceed();
		}

		//After that, the return refers to quota processing, which is removed first
		. . . 
	}
	finally {
		if (target != null && !targetSource.isStatic()) {
			// Must have come from TargetSource.
			targetSource.releaseTarget(target);
		}
		if (setProxyContext) {
			// Restore old proxy.
			AopContext.setCurrentProxy(oldProxy);
		}
	}
}
  • Next, invoke proceed() ->ReflectiveMethodInvocation. proceed()
//Member variable
//The executed pipeline call chain is assigned by the construction method
protected final List<?> interceptorsAndDynamicMethodMatchers;
private int currentInterceptorIndex = -1;


//Start responsibility chain call
public Object proceed() throws Throwable {
    // We start with an index of -1 and increment early.
    //If the subscript of the current execution is equal to the size-1 of the execution, it indicates that the last execution has occurred, and the native calling method is executed
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        //This is the return method invoke(target, args);
        return invokeJoinpoint();
    }
	//Subscript + 1 starts from 0. The default value is - 1
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
    //If you want to dynamically match joinpoints
    if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
        // Evaluate dynamic method matcher here: static part will already have
        // been evaluated and found to match.
        InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
        Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
        //Dynamic matching: whether the runtime parameters meet the matching conditions
        if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
            //The parameter passed in is this 
            //For example, the method will be called again after the execution of the front section
            return dm.interceptor.invoke(this);
        }
        else {
            //Continue recursion
            return proceed();
        }
    }
    else {
       	//If the MethodInterceptor is not called directly
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}
  • dm. interceptor. invoke(this) -> AspectJAfterAdvice. Invoke calls the method of notification. Pick one and see
//After calling, you can understand others by yourself
public Object invoke(MethodInvocation mi) throws Throwable {
	try {
		//Reflection calls the original class
		return mi.proceed();
	}
	finally {
		//Call the facet method after return
		invokeAdviceMethod(getJoinPointMatch(), null, null);
	}
}

summary

After the container initializes the bean, it calls back to the post notification method. aop just implements the post notification method. If it is judged that the current configuration pointcut is met, the proxy class is generated and returned to the IOC container.
When creating a proxy class, the original class, call link and other information will be saved through the construction method. When creating a dynamic proxy, the callback handler class passed in when newInstance is also this, so the invoke method of the handler class will be called when calling the proxy class
In the invoke method, recursive and subscript adding methods are used to make the calling link execute reflection calls. If the corresponding Advice class cannot be matched normally, the original class will be directly reflected and called when the last one is reached and returned
If it can be matched normally, for example, the AspectJAfterAdvice above is to call the original class by reflection first, and then call the post aspect method, and the whole process ends

So far, the source code of the whole AOP aspect has been analyzed. After reading these codes, there should be no problem with handwriting!
The next section of MVC source code is based on IOC container, and the idea is similar to AOP.

That is the whole content of this chapter.

Previous: Spring source code analysis bullet 2 - DI dependency injection analysis
Next: Spring source code analysis fourth bullet - MVC analysis

Don't take it easy, white young head, empty sad

Topics: Java Spring Design Pattern Back-end