Spring principles (13) -- implementation of the concept of spring AOP;

Posted by Eng65 on Sun, 24 Oct 2021 23:57:05 +0200

@TOC #Spring collection
Record every step of the program___ auth:huf

OOP stands for object-oriented programming, which is a programming idea. AOP stands for aspect oriented programming, which is also a programming idea. There is a saying in the Spring official website;
Let us begin by defining some central AOP concepts and terminology. 
These terms are not Spring-specific. Unfortunately, 
AOP terminology is not particularly intuitive. However, 
it would be even more confusing if Spring used its own terminology

It means:
These concepts in AOP are not unique to Spring. Unfortunately, the concepts in AOP are not particularly intuitive, but if Spring redefines its own, it may lead to more confusion

We know; There are two kinds of proxies in Spring; One agent is JDK agent; One agent is CGLIB agent; What is the difference between the two agents? When will the JDK agent be used; Under what circumstances are post CGLIB agents used? This is something we must find out; We don't need to look at the source code in detail, no matter what is the cut-off point, what is the notification, etc; We follow the code step by step; Will understand;

CGLIB

Create a proxy class; The proxy class is the Service we often use;

To create a proxy class:

Last execution result:

All the objects obtained are CglibDemoService objects, but the effect of executing the test() method is different. This is the effect brought by the agent. The above is the creation of proxy objects through cglib, which is based on parent-child classes. The proxy class (CglibDemoService) is the parent class, the proxy class is the child class, the proxy object is the instance object of the proxy class, and the proxy class is created by cglib;

JDK agent;

JDK agent is an interface based agent; In other words, the proxy must be an interface; Then we will write an interface and an implementation;

realization:

JDK agent:

Final execution results:

The above is the main implementation of CGLIB and JDK agent;

The following code is not shown in pictures. The author hopes that readers can manually type it again. In this way, they can deepen their understanding of AOP; All the codes in this series of articles are typed out by the author one letter by one; All codes have been self tested;

ProxyFactory

In Spring; Encapsulate the above two agents; The class that encapsulates the export is called ProxyFactory, which means that it is a factory for creating proxy objects, which will be more convenient to use than the above;

package com.huf.aop;
import com.huf.aop.cglib.CglibDemoService;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;
/**
 * auth : huf
 */
public class ProxyFactoryDemo {
    public static void main(String[] args) {
        CglibDemoService target = new CglibDemoService();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        proxyFactory.addAdvice(new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation methodInvocation) throws Throwable {
                System.out.println("Before executing the method : before");
                Object result = methodInvocation.proceed();
                System.out.println("Before executing the method : after");
                return result;
            }
        });
        CglibDemoService cglibDemoService = (CglibDemoService) proxyFactory.getProxy();
        cglibDemoService.test();
    }
}

Through ProxyFactory, we can no longer decide whether to use cglib or jdk dynamic proxy. ProxyFactory will help us judge
If the class we put in setTarget() implements the interface, ProxyFactory will select JDK proxy. If it is not the interface, CGLIB proxy will be used; The above is the CGLIB agent used; If it is replaced with JdkDemoService, it is the JDK agent, so the converted JdkDemoInterface;

In the Proxy class above, we see that we have added an Advice, where Advice is our < < notification > >

Classification of Advice

  1. Before Advice: execute before method
  2. After returning advice: the method is executed after return
  3. After throwing advice: the method executes after throwing an exception
  4. After (finally) advice: the method is executed after it is finally executed. This is the last, which is later than return
  5. Around advice: This is the most powerful Advice. You can customize the execution order

Advisor

In my personal understanding, Advisor is an enhancement of Advice; An Advisor contains
An Advice – < < notification > >
A Pointcut – < tangent point > >
The logic to be represented can be specified through Pointcut;

A small case can clearly know what Pointcut is

/**
 * auth : huf
 */
public class ProxyFactoryDemo {
    public static void main(String[] args) {
        JdkDemoService jdkDemoService = new JdkDemoService();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(jdkDemoService);
        proxyFactory.addAdvisor(new PointcutAdvisor() {
        	Which method does the pointcut cut cut into;
            @Override
            public Pointcut getPointcut() { 
                return new StaticMethodMatcherPointcut() {
                    @Override
                    public boolean matches(Method method, Class<?> aClass) {
                        return method.getName().equals("test1");
                    }
                };
            }
            @Override
            public Advice getAdvice() {
                return new MethodInterceptor() {
                    @Override
                    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
                        System.out.println("Before method execution : before");
                        Object proceed = methodInvocation.proceed();
                        System.out.println("After method execution : after");
                        return proceed;
                    }
                };
            }
            @Override
            public boolean isPerInstance() {
                return false;
            }
        });
        JdkDemoInterface jdkDemoInterface = (JdkDemoInterface) proxyFactory.getProxy();
        In execution test Method of; Will not be represented; Because there's one
        jdkDemoInterface.test();
        System.out.println("------------------------------------------------------------");
        In execution test1 The method is represented;
        jdkDemoInterface.test1();
    }
}

How proxy objects are created

As described above, Spring provides [ProxyFactory, Advisor, Advice, PointCut] and other technologies to create proxy objects. However, when we use Spring, we do not directly use ProxyFactory. For example, we hope that the proxy object generated by ProxyFactory can be a Bean directly, and the proxy object of UserSerivce can be obtained directly from the Spring container, As developers, we must tell Spring which classes need to be proxied and what the proxy logic is

The following is an evolutionary process; We will gradually go deep into AOP;
We register beans through ProxyBean;

ProxyFactoryBean

    @Bean
    public ProxyFactoryBean proxyFactoryBean() {
        CglibDemoService cglibDemoService = new CglibDemoService();
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(cglibDemoService);
        proxyFactoryBean.addAdvice(new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation methodInvocation) throws Throwable {
                System.out.println("Before method execution : before");
                Object proceed = methodInvocation.proceed();
                System.out.println("After method execution : after");
                return proceed;
            }
        });
        return proxyFactoryBean;
    }


The results are:


Continue to evolve; We can register advice separately;

 @Bean
    public MethodInterceptor hufAroundAdvise() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("Before method execution : before");
                Object proceed = invocation.proceed();
                System.out.println("After method execution : after");
                return proceed;
            }
        };
    }
    @Bean
    public ProxyFactoryBean proxyFactoryBean() {
        CglibDemoService cglibDemoService = new CglibDemoService();
        ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
        proxyFactoryBean.setTarget(cglibDemoService);
        proxyFactoryBean.setInterceptorNames("hufAroundAdvise");
        return proxyFactoryBean;
    }

Their execution results are as like as two peas, only advice is registered as Bean.

Continue to dig deep; Since we can register one, can we register in batches??

BeanNameAutoProxyCreator

Two ordinary Service
@Component
public class CglibDemoService {
    public void test(){
        System.out.println("CGLIB Dynamic proxy demo;");
    }
}

@Component
public class CglibDemoService2 {
    public void test(){
        System.out.println("CGLIB2 Dynamic proxy demo;");
    }
}
@Configuration
public class AopConfig {
    @Bean
    public MethodInterceptor hufAroundAdvise() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("Before method execution : before");
                Object proceed = invocation.proceed();
                System.out.println("After method execution : after");
                return proceed;
            }
        };
    }
    @Bean
    public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        beanNameAutoProxyCreator.setBeanNames("cglibDemoServic*");
        beanNameAutoProxyCreator.setInterceptorNames("hufAroundAdvise");
        beanNameAutoProxyCreator.setProxyTargetClass(true);
        return beanNameAutoProxyCreator;
    }
}

Execution:

result:

Although this method is feasible, it can only be represented by Class Name to continue to dig deeper

DefaultAdvisorAutoProxyCreator

@Bean
    public MethodInterceptor hufAroundAdvise() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                System.out.println("Before method execution : before ....");
                Object proceed = invocation.proceed();
                System.out.println("After method execution : after....");
                return proceed;
            }
        };
    }

    @Bean
    public DefaultPointcutAdvisor defaultPointcutAdvisor(MethodInterceptor hufAroundAdvise){
        NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
        nameMatchMethodPointcut.addMethodName("test"); //Name of the method to cut in
        DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
        defaultPointcutAdvisor.setPointcut(nameMatchMethodPointcut);//Injection tangent
        defaultPointcutAdvisor.setAdvice(hufAroundAdvise);//Injection pointcut logic
        return defaultPointcutAdvisor;
    }

    @Bean
    public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
        DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        return defaultAdvisorAutoProxyCreator;
    }

The defaultadvisor autoproxycreator will directly find all Advisor type beans, and determine the beans to be proxied and the proxy logic according to the PointCut and Advice information in the Advisor

In this way, we register all beans in this chapter into the container; Bean s including JDK agents are also registered;

Execution:

Implementation results obtained:

In this way, we have a deeper evolution;

Summary:

This chapter explains

  1. Implementation of CGLIB
  2. Implementation of JDK dynamic agent;
    The of AOP is introduced
  3. Pointcut - tangent point
  4. Advice - Notification
  5. Advisor - combination of the first two;
    This paper introduces entity classes
  6. ProxyFactory - used by Spring
  7. ProxyFactoryBean -- register into the Spring container to generate a single bean
  8. BeanNameAutoProxyCreator – mass produce proxy objects by class name
  9. Defaultadvisor autoproxycreator – mass produce proxy objects by method name

The next chapter will explain the source code of AOP; AOP itself is not difficult, so its source code is simpler than the previous articles; And have a foreshadowing of this article; It can be said to take off directly;

seeyou

Topics: Java Spring Back-end