AOP process source code analysis - configuration information analysis and proxy object creation

This article even starts to analyze the process source code of aop. Different from IOC, aop is relatively simple. Our follow-up main content is the analysis of aop-related configuration information, the creation of agents, and the invocation of agent processes. These three parts will briefly talk about the source code analysis of aop.

Briefly describe the past and present of AOP

First of all, the beginning of the series of articles is still an old routine. Let me talk about some conceptual knowledge of aop. The main source of information is the Internet and the official. Skip this section if you have a certain understanding of aop.

  1. AOP (Aspect Orient Programming): aspect-oriented programming;
  2. Purpose: Used for cross-cutting concerns in the system, such as log management and transaction management;
  3. Implementation: Use the proxy mode to add functions to the proxied object through the proxy object. Therefore, the key is that the AOP framework automatically creates AOP proxy objects, and the proxy mode is divided into static proxy and dynamic proxy;
  4. Framework:
    AspectJ uses static proxies, enhanced at compile time, and generates proxy objects at compile time;
    SpringAOP uses dynamic proxies, enhanced at runtime, and dynamically generates proxy objects at runtime;

At present, there are three configuration methods for Spring AOP. Spring is well backward compatible, so you can use it with confidence.

  • Spring 1.2 interface-based configuration: the earliest Spring AOP was based entirely on several interfaces
  • Spring 2.0 schema-based configuration: After Spring 2.0, use XML to configure, use namespace
  • Spring 2.0 @AspectJ configuration : use annotations to configure, this method feels the most convenient, and, although it is called @AspectJ here, it has nothing to do with AspectJ.

It should be noted that the Spring AOP introduced here is pure Spring code and has nothing to do with AspectJ, but Spring extends the concepts in AspectJ, including the use of annotations in the jar package provided by AspectJ, but does not depend on its implementation functions . Annotations such as @Aspect, @Pointcut, @Before, @After are all from AspectJ, but the implementation of the function is still implemented by Spring AOP itself.

There are currently two underlying implementation mechanisms of Spring AOP: JDK dynamic proxy and CGLIB dynamic bytecode generation. Having an understanding of the use of these two mechanisms before reading the source code is conducive to a better understanding of the source code.

Source code analysis

For the source code, use the source code used when analyzing the IOC before, and then create a test class.

public static void main(String[] args) {
    
    

  ClassPathXmlApplicationContext applicationContext =
    new ClassPathXmlApplicationContext("classpath:applicationContext-aop.xml");

  UserService userService = (UserService) applicationContext.getBean("userServiceImpl");
  userService.findAll();
}

Our main purpose of source code analysis today is to test the creation of applicationContext in the class, which is the content when the container starts, and we will talk about the invocation of specific methods later.

Because IOC has already analyzed the core method refresh method once before, friends who don’t know about IOC can start to read it from the second article of IOC: IOC Process Analysis-BeanFactory Creation . Now that we have seen it, let's go directly to the specific method.

AOP configuration information analysis

First of all, the tag setting of aop is a bit different from the bean tag, so if we want to look at it, we start with the parsing content of the tag, that is, the construction of BeanDefinition.

<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	   xmlns:context="http://www.springframework.org/schema/context"
	   xmlns:aop="http://www.springframework.org/schema/aop"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans"
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context.xsd
		http://www.springframework.org/schema/aop
		http://www.springframework.org/schema/aop/spring-aop.xsd>
		
	<context:component-scan base-package="com.itheima"/>
	
	<!--@EnableAspectJAutoProxy-->
	<aop:aspectj-autoproxy />
</beans>

The code is directly located here, the parseBeanDefinitions method (global search is enough here, how to find it or see the second article of IOC), what you can see here is that there will be two branches of the parsing method, which we have also explained before Spring's schema mechanism, so the aop tag should be the second method parseCustomElement method.

if (node instanceof Element) {
    
    
  Element ele = (Element) node;
  //下面有两个分支
  if (delegate.isDefaultNamespace(ele)) {
    
    
    // 1.1 默认命名空间节点的处理,例如: <bean id="test" class="" />
    //分支1:代表解析标准元素 <import />、<alias />、<bean />、<beans /> 这几个
    //标准节点
    parseDefaultElement(ele, delegate);
  }
  else {
    
    
    // 1.2 自定义命名空间节点的处理,例如:<context:component-scan/>、<aop:aspectj-autoproxy/>
    //分支2:代表解析 <mvc />、<task />、<context />、<aop /> 、<component-scan />等
    //特殊节点
    delegate.parseCustomElement(ele);
  }
}

Follow up method.

The first focus here is to obtain a namespace processor, and when we introduced the schema mechanism before, we have already said that this parser is provided by the corresponding label settings, so we need to find the corresponding "http:/ /www.springframework.org/schema/aop” logo, and then find the corresponding spring.handlers file according to the logo.

//解析命名空间,得到一个命名空间处理器
//重点
NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri);
//开始解析
//主线 重点
return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd));

If the global search is hard to find, because there may be many configuration files that have introduced aop, then you can directly click on the logo in the xml, jump to the corresponding jar package, and then find the corresponding spring.handlers file under the inf folder

image-20220428154603852

The parser for this aop tag is stored in this file.

http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler

Then follow up directly with the AopNamespaceHandler class.

You can see the generation of different parsers for each tag. What we configured above is the aspectj-autoproxy tag, the basic tag of aop, so directly find the corresponding AspectJAutoProxyBeanDefinitionParser class. As for when this init method is called, let's continue to look down.

public class AopNamespaceHandler extends NamespaceHandlerSupport {

	@Override
	public void init() {
		// In 2.0 XSD as well as in 2.5+ XSDs
		registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser());
		registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());
		registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());

		// Only in 2.0 XSD: moved to context namespace in 2.5+
		registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());
	}
}

Going back to the parseCustomElement method, follow up with its resolve method.

We can see that the init method is called at this point, which means that different parser objects are created here and then returned.

NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass);
namespaceHandler.init();
handlerMappings.put(namespaceUri, namespaceHandler);
return namespaceHandler;

Go back to the parseCustomElement method again.

Now that we know that the current parser is the AspectJAutoProxyBeanDefinitionParser object, we can continue to follow up its parse method.

Follow up method. What we should focus on is the first registered AspectJAnnotationAutoProxyCreator method.

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    
    
  // 1.注册AspectJAnnotationAutoProxyCreator
  AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
  // 2.对于注解中子节点的处理
  extendBeanDefinition(element, parserContext);
  return null;
}

Follow up.

The purpose here is to register the AnnotationAwareAspectJAutoProxyCreator object in the BeanFactory. Note that this is not registered in the singleton pool, specifically why, see below.

public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
  ParserContext parserContext, Element sourceElement) {
    
    

  // 1.注册AnnotationAwareAspectJAutoProxyCreator
  BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
    parserContext.getRegistry(), parserContext.extractSource(sourceElement));
  // 2.对于proxy-target-class以及expose-proxy属性的处理
  useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
  // 3.注册组件并通知,便于监听器做进一步处理
  registerComponentIfNecessary(beanDefinition, parserContext);
}

We can look at the structure of the AnnotationAwareAspectJAutoProxyCreator object.

image-20220428160623332

The top-level interface of this object is the BeanPostProcessor interface, which means that this object is a Bean post-processor object. So if you register in BeanFactory, it is not registered in the single force pool, but in the collection of BeanPostProcessor.

summary

The aop tag parsing here is like this, the purpose is to register the post processor of the first Bean in the BeanPostProcessor collection.

Execution of BeanPostProcessor corresponding to AOP

In the above we see that AOP-related BeanPostProcessor has been built, so the next step is to see the execution of these Bean post-processors, so we directly locate the doCreateBean method of the Bean's initialization content.

After locating the method, directly find the call of the initializeBean method of the corresponding BeanPostProcessor to execute the related content.

The pre-method parses all advisor-postProcessBeforeInitialization methods

Follow up the method. The call of the first pre-method continues to follow the applyBeanPostProcessorsBeforeInitialization method. However, we have seen this method many times before. It has nothing to do with it. It just loops out all BeanPostProcessors and executes the corresponding postProcessBeforeInitialization method.

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
    
    
  // 2.在初始化前应用BeanPostProcessor的postProcessBeforeInitialization方法,允许对bean实例进行包装
  wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

We directly follow up the Before method of the AnnotationAwareAspectJAutoProxyCreator object built above. Note that the follow-up here is the implementation of the parent class AbstractAutoProxyCreator object, because this object does not implement the Before method.

Follow up code. Find its key point and call the shouldSkip method. Pay attention to whether the Before method creates a proxy object, but it will load all Advisors.

if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
    
    
  this.advisedBeans.put(cacheKey, Boolean.FALSE);
  return null;
}

Follow up. Note that it is necessary to follow up and find the corresponding subclass rewriting. Then find the key method findCandidateAdvisors method call

List<Advisor> candidateAdvisors = findCandidateAdvisors();

Still follow up. Or find its corresponding subclass to rewrite.

Here, all the advisors found according to the rules of the parent class will be found first, but there are none here, that is, null, so what depends on the call of the buildAspectJAdvisors method.

@Override
protected List<Advisor> findCandidateAdvisors() {
    
    
  // Add all the Spring advisors found according to superclass rules.
  // 1.添加根据父类规则找到的所有advisor
  List<Advisor> advisors = super.findCandidateAdvisors();
  // Build Advisors for all AspectJ aspects in the bean factory.
  // 2.为bean工厂中的所有AspectJ方面构建advisor
  if (this.aspectJAdvisorsBuilder != null) {
    
    
    advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
  }
  return advisors;
}

Follow up method.

First, all the advisors object caches will be obtained once. If there is a value, it will be returned directly. If there is no value, it will be parsed. The purpose is to parse only once, because all bean initialization will use this BeanPostProcessor, so it is necessary to avoid repeated parsing.

List<String> aspectNames = this.aspectBeanNames;

// 1.如果aspectNames为空,则进行解析
if (aspectNames == null) {
    
    

Then get all the beanName, loop through.

// 1.1 获取所有的beanName
String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
  this.beanFactory, Object.class, true, false);

// 1.2 循环遍历所有的beanName,找出对应的增强方法
for (String beanName : beanNames) {
    
    

The first important point is that the interface that must be modified by the Aspect annotation will be built into an advisor object.

// 1.4 如果beanType存在Aspect注解则进行处理
if (this.advisorFactory.isAspect(beanType)) {
    
    

The second key point is to call the getAdvisors method to obtain the Advisor object collection encapsulated by all methods of the current object.

// 使用BeanFactory和beanName创建一个BeanFactoryAspectInstanceFactory,主要用来创建切面对象实例
MetadataAwareAspectInstanceFactory factory =
  new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);
// 1.5 解析标记AspectJ注解中的增强方法===》》》》
List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);

Follow up the getAdvisors method to see the specific package content.

Here it is encapsulated into different Advisor objects according to different annotations modified on the method.

// 2.获取Aspect()标注的类名
String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();

// 5.获取切面类中的方法(也就是我们用来进行逻辑增强的方法,被@Around、@After等注解修饰的方法,使用@Pointcut的方法不处理)==>
for (Method method : getAdvisorMethods(aspectClass)) {
    
    
  // 6.处理method,获取增强器==>
  Advisor advisor = getAdvisor(method, lazySingletonAspectInstanceFactory, 0, aspectName);
  if (advisor != null) {
    
    
    // 7.如果增强器不为空,则添加到advisors
    advisors.add(advisor);
  }
}

There is no need to continue to follow up, let's take a look at the general writing.

The first is the @Aspect annotation modification class, indicating that this class is an aspect class and can be found by aop

Then the @Pointcut annotation modifies the first method, all specific objects that need to be intercepted

Finally, the @Before annotation modifies the pre-method, the @After annotation modifies the post-method, and the @Around annotation modifies the surround method.

@Component
@Aspect // 切面
public class AopAspect {
    
    
  
	@Pointcut("execution(* com.itheima.service..*.*(..))")
	public void pointcut() {
    
    
	}

	@Before("pointcut()")
	public void before() {
    
    
		System.out.println("before");
	}

	@After("pointcut()")
	public void after() {
    
    
		System.out.println("after");
	}

	@Around("pointcut()")
	public Object around(ProceedingJoinPoint proceedingJoinPoint) throws InterruptedException {
    
    
		System.out.println("around advice start");
		try {
    
    
			Object result = proceedingJoinPoint.proceed();
			System.out.println("around advice end");
			return result;
		} catch (Throwable throwable) {
    
    
			throwable.printStackTrace();
			return null;
		}
	}
}

The post method generates a proxy object - postProcessAfterInitialization method

Or go back to the AbstractAutoProxyCreator object and find the postProcessAfterInitialization method directly.

Follow up code.

The first is to generate the key for caching, and then call the wrapIfNecessary method.

//2.如果beanName为空,使用Class对象作为缓存的key
Object cacheKey = getCacheKey(bean.getClass(), beanName);
// 1.判断当前bean是否需要被代理,如果需要则进行封装
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
    
    
  return wrapIfNecessary(bean, beanName, cacheKey);
}

Follow up.

Here we first get all the Advisors in the parsing cache above, we can first look at the getAdvicesAndAdvisorsForBean method.

// 4.获取当前bean的Advices和Advisors===》
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

After the follow-up, the key point is to call the findCandidateAdvisors method directly. We will not look at it here, and just follow up again.

As you can see in this part, the first step is to obtain all Advisor objects, and then filter out the Advisors that intercept this object. Note that we are still in the initialization process of the Bean object, so the object referred to here is the object being initialized. Finally, just sort the qualified Advisor objects in order. The sorting here is to sort the surrounding, pre-, and post-methods, so as to facilitate the calling order of the subsequent proxy objects when they are executed.

As for the findCandidateAdvisors method, it is exactly the same as the above call, the only difference is that this time it does not need to be parsed again, but directly returns the above cache collection.

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    
    
  // 1.查找所有的候选Advisor
  //将所有拥有@Aspect注解的类转换为advisors(aspectJAdvisorsBuilder.buildAspectJAdvisors)
  // 2.从所有候选的Advisor中找出符合条件的
  List<Advisor> candidateAdvisors = findCandidateAdvisors();
  List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
  // 3.扩展方法,留个子类实现
  extendAdvisors(eligibleAdvisors);
  if (!eligibleAdvisors.isEmpty()) {
    
    
    // 4.对符合条件的Advisor进行排序
    eligibleAdvisors = sortAdvisors(eligibleAdvisors);
  }
  return eligibleAdvisors;
}

Back to the wrapIfNecessary method.

The continuation is the part of creating the proxy. Finally, the created proxy will be stored in the cache and the result will be returned. We will not come back to this method later, so be optimistic.

// 5.如果存在增强器则创建代理
if (specificInterceptors != DO_NOT_PROXY) {
    
    
  this.advisedBeans.put(cacheKey, Boolean.TRUE);
  // 5.1 创建代理对象:这边SingletonTargetSource的target属性存放的就是我们原来的bean实例(也就是被代理对象),
  // 用于最后增加逻辑执行完毕后,通过反射执行我们真正的方法时使用(method.invoke(bean, args))
  Object proxy = createProxy(
    bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
  // 5.2 创建完代理后,将cacheKey -> 代理类的class放到缓存
  this.proxyTypes.put(cacheKey, proxy.getClass());
  // 返回代理对象
  return proxy;
}

Continue to follow the createProxy method.

There are a lot of codes here, so you don’t need to worry about them, just read the last sentence below.

proxyFactory.getProxy(getProxyClassLoader());

Follow up code.

Don't rush to look at getProxy, first look at the createAopProxy method.

public Object getProxy(@Nullable ClassLoader classLoader) {
    
    
  // 1.createAopProxy:创建AopProxy
  // 2.getProxy(classLoader):获取代理对象实例
  return createAopProxy().getProxy(classLoader);
}

The call here is relatively simple, so I won't follow it step by step, just look at the result.

Here it will be judged whether the Cglib agent or the JDK agent is used. The direct conclusion is that the JDK proxy is used by default, and the Cglib proxy is configured. JDK proxy corresponding object: JdkDynamicAopProxy, Cglib proxy corresponding object: ObjenesisCglibAopProxy. Here we must remember the objects corresponding to the two proxies. When we go through the process later, we will look at the proxies and directly look at the invoke method of the corresponding objects.

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    
    
  // 1.判断使用JDK动态代理还是Cglib代理
  // optimize:用于控制通过cglib创建的代理是否使用激进的优化策略。除非完全了解AOP如何处理代理优化,否则不推荐使用这个配置,目前这个属性仅用于cglib代理,对jdk动态代理无效
  // proxyTargetClass:默认为false,设置为true时,强制使用cglib代理,设置方式:<aop:aspectj-autoproxy proxy-target-class="true" />
  // hasNoUserSuppliedProxyInterfaces:config是否存在代理接口或者只有SpringProxy一个接口
  if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
    
    
    // 拿到要被代理的对象的类型
    Class<?> targetClass = config.getTargetClass();
    if (targetClass == null) {
    
    
      // TargetSource无法确定目标类:代理创建需要接口或目标。
      throw new AopConfigException("TargetSource cannot determine target class: " +
                                   "Either an interface or a target is required for proxy creation.");
    }
    // 要被代理的对象是接口 || targetClass是Proxy class
    // 当且仅当使用getProxyClass方法或newProxyInstance方法动态生成指定的类作为代理类时,才返回true。
    if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
    
    
      // JDK动态代理,这边的入参config(AdvisedSupport)实际上是ProxyFactory对象
      // 具体为:AbstractAutoProxyCreator中的proxyFactory.getProxy发起的调用,在ProxyCreatorSupport使用了this作为参数,
      // 调用了的本方法,这边的this就是发起调用的proxyFactory对象,而proxyFactory对象中包含了要执行的的拦截器
      return new JdkDynamicAopProxy(config);
    }
    // Cglib代理
    return new ObjenesisCglibAopProxy(config);
  }
  else {
    
    
    // JDK动态代理
    return new JdkDynamicAopProxy(config);
  }
}

It is easy to look back at the getProxy method, because we have no configuration, so here is the use of the jdk proxy.

It is to obtain the proxy object through classLoader, interface, and InvocationHandler implementation class.

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
    
    
  if (logger.isTraceEnabled()) {
    
    
    logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
  }
  // 1.拿到要被代理对象的所有接口
  Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
  findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
  // 2.通过classLoader、接口、InvocationHandler实现类,来获取到代理对象
  return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

summary

In this way, the content of generating the proxy is over. The main thing is to see whether to use cglib or jdk as the proxy, and the analysis of advisors.

Summarize

This time is to understand some concepts of aop. Of course, I must find more information on the Internet than I do, because I am looking for it too.

Appendix Spring Source Code Analysis Series Articles

IOC

time article
2022-03-09 A brief introduction to the basic concepts of Spring and the IOC process
2022-03-11 IOC process analysis - creation of BeanFactory
2022-03-14 IOC process analysis - BeanFactoyPostProcessor and BeanPostProcessor
2022-03-15 IOC process analysis - instantiation and initialization
2022-03-17 IOC process analysis - circular dependency

AOP

time article
2022-03-19 A brief introduction to the basic concepts of Spring and the IOC process
2022-03-20 AOP process source code analysis - request call the whole process

Guess you like

Origin blog.csdn.net/qq_39339965/article/details/124489133