In depth analysis of spring 5 source code — use of AOP and AOP custom label

Time:2020-1-17

We know that there are some disadvantages in OO programming. When we need to introduce the same common behavior for multiple objects without inheritance relationship, such as log, security detection, etc., we only need to introduce the common behavior in each object, so a lot of repetitive code will be generated in the program, so we have the supplement of OO programming, AOP, AOP The direction of interest is horizontal, different from the OOP vertical. Next, we will analyze AOP in spring in detail. First we start with the use of dynamic AOP.

The use of AOP

Before you start, introduce aspect.

org.aspectj
    aspectjweaver
    ${aspectj.version}

Create beans for interception

public class TestBean {
    private String message = "test bean";

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public void test(){
        System.out.println(this.message);
    }
}

Create Advisor

Spring abandons the most original complicated configuration mode and uses @ AspectJ annotation to annotate POJO, which greatly simplifies the work of AOP. For example, in aspectjtest class, what we need to do is to be foretested on the console before the test methods of all classes are executed. After the test method of all classes is executed, the aftertest will be printed. At the same time, before and after the method of all classes is executed, the before1 and after1 will be printed respectively. The following is the code of aspectjtest:

@Aspect
public class AspectJTest {
    @Pointcut("execution(* *.test(..))")
    public void test(){
    }
    
    @Before("test()")
    public void beforeTest(){
        System.out.println("beforeTest");
    }
    
    @Around("test()")
    public Object aroundTest(ProceedingJoinPoint p){
        System.out.println("around.....before");
        Object o = null;
        try{
            o = p.proceed();
        }catch(Throwable e){
            e.printStackTrace();
        }
        System.out.println("around.....after");
        return o;
    }
    
    @After("test()")
    public void afterTest()
    {
        System.out.println("afterTest");
    }
 }

create profile

To enable AOP in spring, you also need to make the following declaration in the configuration file:

 

test

public class Test {
    public static void main(String[] args) {
        ApplicationContext bf = new ClassPathXmlApplicationContext("aspectTest.xml");
        TestBean bean = (TestBean)bf.getBean("test");
        bean.test();
    }
}

The output after execution is as follows:

 

Spring implements the enhancement of the test methods of all classes, which makes the auxiliary functions independent of the core business and convenient to extend and decouple the program.  
So, how does spring implement AOP? First of all, we know that whether spring supports annotation AOP is controlled by a configuration file, that is, when the configuration is declared in the configuration file, spring will support AOP of annotation, so our analysis starts from this annotation.

AOP custom label

I talked about the custom annotation in spring before. If the custom annotation is declared, the corresponding parser will be registered somewhere in the program. We searchaspectj-autoproxyThis code tries to find the registration place. After global search, we found that the aopnamespacehandler under org.springframework.aop.config package corresponds to such a function:

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

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

Here we will not discuss the custom annotation in spring. From this code, we can see that when parsing the configuration file, once the AspectJ AutoProxy annotation is encountered, the parser aspectjautoproxybean definitionparser will be used for parsing. Next, we will analyze its internal implementation in detail.

Register annotationawareaspectjautoproxycreator

All parsers implement the beandefinitionparser interface in a unified way. The entry starts from the parse function. The parse function of aspectjautoproxybeandefinitionparser is as follows:

@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
    //Register annotationawareaspectjautoproxycreator
    AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
    //Treatment of annotation subclasses
    extendBeanDefinition(element, parserContext);
    return null;
}

It can be seen from the code that the specific logic of the function is implemented in the registeraspectjannationautoproxycreatorifnecessary method and continues to enter the function body:

/**
 *Register annotationawareaspectjautoproxycreator
 * @param parserContext
 * @param sourceElement
 */
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        ParserContext parserContext, Element sourceElement) {
    //Register or upgrade the beandefinition of autoproxycreator definition with beanname of org.springframework.aop.config.internalautoproxycreator
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    //Handling of proxy target class and expose proxy properties
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    //Register components and notify for further processing by the listener
    registerComponentIfNecessary(beanDefinition, parserContext);
}

In the register aspect jannationautoproxycreatorifneccessary method, three things are mainly completed, basically every line of code is a complete logic. Next, we analyze each line of code in detail.

Register or upgrade annotationawareaspectjautoproxycreator

The implementation of AOP is basically accomplished by annotation aware aspectjautoproxycreator, which can automatically proxy matching beans according to the pointcuts defined by @ point annotation. However, for the convenience of configuration, spring uses a custom configuration to help us automatically register annotationawareaspectjautoproxycreator. The registration process is implemented here. We continue to follow up into the methodology:

public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry,
        @Nullable Object source) {
    return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source);
}

public static final String AUTO_PROXY_CREATOR_BEAN_NAME = "org.springframework.aop.config.internalAutoProxyCreator";

private static BeanDefinition registerOrEscalateApcAsRequired(Class> cls, BeanDefinitionRegistry registry,
        @Nullable Object source) {

    Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
    //If there is already an automatic agent creator and the existing one is inconsistent with the current one, it is necessary to determine which one to use according to the priority
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        if (!cls.getName().equals(apcDefinition.getBeanClassName())) {
            int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());
            int requiredPriority = findPriorityForClass(cls);
            if (currentPriority < requiredPriority) {
                //The most important thing to change a bean is to change its classname property  
                apcDefinition.setBeanClassName(cls.getName());
            }
        }
        return null;
    }
    //Register beandefinition, class is annotationawareaspectjautoproxycreator.class, beanname is internalautoproxycreator
    RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);
    beanDefinition.setSource(source);
    beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);
    beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
    registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);
    return beanDefinition;
}

The above code implements the function of automatically registering the annotationawareaspectjautoproxycreator class. At the same time, it also involves a priority problem. If there is already an automatic agent creator, and the existing automatic agent creator is inconsistent with the current one, then it is necessary to determine which one to use according to the priority.

Handling proxy target class and expose proxy properties

Useclassproxyingifnecessary implements the processing of the proxy target class attribute and the expose proxy attribute, and enters the method interior:

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {
    if (sourceElement != null) {
        //It implements the processing of proxy target class
        boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));
        if (proxyTargetClass) {
            AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
        }
        //Processing of expose proxy
        boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));
        if (exposeProxy) {
            AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
        }
    }
}

In the above code, two methods of forced use are used. In fact, the process of forced use is also a process of property setting. The methods of the two functions are as follows:

public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);
    }
}

public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {
    if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {
        BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);
        definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);
    }
}
  • Proxy target class: the spring AOP section uses the JDK dynamic proxy or cglib to create a proxy for the target object. (it is recommended to try to use the dynamic proxy of JDK). If the target object of the proxy implements at least one interface, the dynamic proxy of JDK will be used. All interfaces implemented by this target type will be proxied. If the target object does not implement any interfaces, a cglib proxy is created. If you want to enforce the use of cglib proxies (for example, you want to proxy all methods of the target object, not just methods that implement self interfaces), that’s fine. But there are two questions to consider.
  1. The (advise) final methods cannot be notified because they cannot be overridden.
  2. You need to put the cglib binary distribution under the classpath.

In contrast, JDK itself provides a dynamic proxy, which requiresThe proxy target class severity of is set to true:

...

When cglib agent and @ AspectJ auto agent support are needed, they can be set as follows:Proxy target class property of:

  • JDK dynamic proxy: its proxy object must be the implementation of an interface. It is the proxy to the target object by creating an implementation class of the interface during runtime.
  • Cgijb proxy: the implementation principle is similar to that of JDK dynamic proxy, except that the proxy object generated during its operation is a subclass extended for the target class. Cglib is an efficient code generation package. The bottom layer is implemented by ASM (open source Java bytecode editing Class Library) to operate bytecode. Its performance is better than JDK.
  • Expose proxy: sometimes the self call inside the target object will fail to implement the enhancement in the aspect, as shown in the following example:
public interface AService { 
    public void a(); 
    public void b();
}

@Service()
public class AServicelmpll implements AService {
    @Transactional(propagation = Propagation.REQUIRED) 
    public void a() { 
        this.b{);
    }
    
    @Transactional(propagation = Propagation.REQUIRES_NEW) 
    public void b() {
    }
}

This here points to the target object, so calling this. B() will not perform the B transaction facet, that is, it will not perform the transaction enhancement, so the transaction definition of B method “@ transactional (promotion = promotion. Requirements_new)” will not be implemented. To solve this problem, we can do this:

Then change “this. B();” in the above code to “((aservice) aopcontext. Currentproxy()). B();”. Through the above modifications, we can enhance the A and B methods at the same time.