Spring source code analysis – use of AOP and AOP custom tags

Text

We know that there are some disadvantages in object-oriented OOP programming. When we need to introduce the same public behavior for multiple objects that do not have inheritance relationships, such as logging, security detection, etc., we can only introduce public behavior into each object, so that the program A large amount of duplicate code is generated, so there is a supplement to object-oriented programming, aspect-oriented programming (AOP). The direction that AOP focuses on is horizontal, which is different from the vertical direction of OOP. Next, we will analyze AOP in spring in detail. First we start with the use of dynamic AOP.

The most comprehensive Java interview website

Usage of AOP

Before we begin, let’s introduce aspects.

<!-- aspectjweaver -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>${aspectj.version}</version>
</dependency>

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 original complicated configuration method and uses @AspectJ annotation to annotate POJO, which greatly simplifies the work of AOP. For example, in the AspectJTest class, all we have to do is to run the test method on the console before the execution of all classes. beforeTest. After the test methods of all classes are executed, afterTest is printed, and at the same time, the wraparound method is used to print before1 and after1 respectively before and after the methods of all classes are executed. 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 configuration file

To enable the AOP function in Spring, you also need to make the following statement in the configuration file:

<?xml version="1.0" encoding="UTF-8" ?>
<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">

    <aop:aspectj-autoproxy/>
    <bean id="test" class="com.dabin.myspring.demo.aop.TestBean">
        <property name="message" value="This is a hard programmer"/>
    </bean>
    <bean id="aspect" class="com.dabin.myspring.demo.aop.AspectJTest"/>
</beans>

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 has implemented enhancements to the test methods of all classes, so that the auxiliary functions can be independent of the core business and facilitate the expansion and decoupling of the program.

So, how does Spring implement AOP? First of all, we know that whether SPring supports annotated AOP is controlled by a configuration file, that is, . When this configuration is declared in the configuration file, Spring Will support annotated AOP, then our analysis will start from this annotation.

Share a Big Factory Interview Manual carefully compiled by Da Bin, including computer basics, Java basics, multi-threading, JVM, database, Redis, Spring, Mybatis, SpringMVC, SpringBoot, distributed, Frequent interview questions such as microservices, design patterns, architecture, school recruitment and social recruitment sharingare very practical. Some friends got the Byte offer by relying on this manual~

Friends who need it can download themselves:

http://mp.weixin.qq.com/s?__biz=Mzg2OTY1NzY0MQ== & amp;mid=2247485445 & amp;idx=1 & amp;sn=1c6e224b9bb3da457f5ee03894493dbc & amp;chksm=ce98f543f9ef7c55325e3bf 336607a370935a6c78dbb68cf86e59f5d68f4c51d175365a189f8#rd

AOP custom tag

I have talked about custom annotations in Spring before. If a custom annotation is declared, the corresponding parser will be registered somewhere in the program. We searched for the aspectj-autoproxy code and tried to find the registration place. After a global search, we found that the AopNamespaceHandler under the 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 no longer discuss the custom annotation method in spring. From this code, we can know that when parsing the configuration file, once the aspectj-autoproxy annotation is encountered, the parser AspectJAutoProxyBeanDefinitionParser will be used for parsing. Next, we will analyze its internal implementation in detail.

Register AnnotationAwareAspectJAutoProxyCreator

All parsers, because they are unified implementations of the BeanDefinitionParser interface, start 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);
    // Processing of subclasses in annotations
    extendBeanDefinition(element, parserContext);
    return null;
}

Through the code, you can know that the specific logic of the function is implemented in the registerAspectJAnnotationAutoProxyCreatorIfecessary method, and continue to enter the function body:

/**
 * Register AnnotationAwareAspectJAutoProxyCreator
 * @param parserContext
 * @param sourceElement
 */
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(
        ParserContext parserContext, Element sourceElement) {<!-- -->
    // Register or upgrade AutoProxyCreator to define the beanName as the BeanDefinition of org.springframework.aop.config.internalAutoProxyCreator
    BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(
            parserContext.getRegistry(), parserContext.extractSource(sourceElement));
    // Processing of proxy-target-class and expose-proxy attributes
    useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);
    //Register the component and notify it to facilitate further processing by the listener
    registerComponentIfNecessary(beanDefinition, parserContext);
}

Three main things are done in the registerAspectJAnnotationAutoProxyCreatorIfNeccessary method. Basically, each 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 completed by AnnotationAwareAspectJAutoProxyCreator, which can automatically proxy matching beans based on the pointcut defined by the @Point annotation. But for the sake of easy configuration, Spring uses custom configuration to help us automatically register AnnotationAwareAspectJAutoProxyCreator, and its registration process is implemented here. We continue to follow up inside the method:

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 an automatic proxy creator already exists and the existing automatic proxy creator is inconsistent with the current one, you need to determine which one needs to be used based on 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 the bean is to change the className attribute corresponding to the bean
                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 issue. If an automatic proxy creator already exists, and the existing automatic proxy creator is inconsistent with the current one, then it needs to be judged according to the priority. Which one needs to be used?

Processing proxy-target-class and expose-proxy attributes

useClassProxyingIfNecessary implements the processing of proxy-target-class attributes and expose-proxy attributes, and enters the method:

private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {<!-- -->
    if (sourceElement != null) {<!-- -->
        //Implemented 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);
        }
    }
}

Two forced usage methods are used in the above code. The process of forced usage is actually a process of attribute 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 part uses JDK dynamic proxy or CGLIB to create a proxy for the target object. (It is recommended to use JDK’s dynamic proxy as much as possible). If the target object being proxied implements at least one interface, JDK dynamic proxy will be used. All interfaces implemented by this target type will be proxied. If the target object does not implement any interface, a CGLIB proxy is created. If you want to force the use of CGLIB proxies (for example, you want to proxy all methods of the target object, not just the methods that implement the self-interface), that is also possible. But there are two issues to consider.

  1. Final methods cannot be advised because they cannot be overridden.
  2. You need to put the CGLIB binary distribution package on the classpath.

In contrast, the JDK itself provides a dynamic proxy. To force the use of CGLIB proxy, you need to set the proxy-target-class property of aop:config to true:

<aop:config proxy-target-class = "true">...</aop:config>

When you need to use CGLIB proxy and @AspectJ autoproxy support, you can set the proxy-target-class attribute of as follows:

<aop:aspectj-autoproxy proxy-target-class = "true"/>
  • JDK dynamic proxy: Its proxy object must be the implementation of an interface. It completes the proxy for the target object by creating an implementation class of the interface during runtime.
  • CGIJB proxy: The implementation principle is similar to JDK dynamic proxy, except that the proxy object it generates during runtime is a subclass extended for the target class. CGLIB is an efficient code generation package. The bottom layer is implemented by relying on ASM (open source Java bytecode editing class library) to operate bytecode, and its performance is better than JDK.
  • expose-proxy: Sometimes the self-call inside the target object will not be able to implement the enhancements 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 execute the b transaction aspect, that is, transaction enhancement will not be executed, so the transaction definition “@Transactional(propagation = Propagation.REQUIRES_NEW)” of the b method will not Implementation, to solve this problem, we can do this:

<aop:aspectj-autoproxy expose-proxy = "true"/>

Then change “this.b();” in the above code to “((AService) AopContext.currentProxy()).b();”. Through the above modifications, the simultaneous enhancement of methods a and b can be completed.

Finally, I would like to share with you a Github repository, which contains more than 300 classic computer book PDFs organized by Da Bin, including C language, C++, Java, Python, front-end, database, operation Systems, computer networks, data structures and algorithms, machine learning, programming life, etc., you can star it. Next time you are looking for books, search directly on it. The warehouse is constantly being updated~

Github address

If you cannot access Github, you can access the code cloud address.

code cloud address

syntaxbug.com © 2021 All Rights Reserved.