laitimes

Contract programming with Ping AOP

author:The zebra hadn't slept yet

First, the contract programming

Spring AOP is a programming of the convention process, we can first understand the concept of Spring AOP through the implementation of the dynamic proxy pattern.

The logic of the agent is very simple, for example, when you need to interview a child, you first need to get the consent of his parents, in some questions the parents may answer for him, and for other questions, maybe the parents feel that it is not suitable for the child will refuse, obviously then the parents are the child's proxy . Access to the child's target can be enhanced or controlled through a proxy.

Contract programming with Ping AOP

1. First implement the interceptor interface Interceptor (self-defined interface)

The following code uses spring boot 2.6.2

package com.springboot.chapter4.intercept;

import java.lang.reflect.InvocationTargetException;
import com.springboot.chapter4.invoke.Invocation;

public class MyInterceptor implements Interceptor {

    @Override
    public boolean before() {
        System.out.println("before ......");
        return true;
    }

    @Override
    public boolean useAround() {
        return true;
    }

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

    @Override
    public Object around(Invocation invocation) 
           throws InvocationTargetException, IllegalAccessException 
   {
        System.out.println("around before ......");
        Object obj = invocation.proceed();
        System.out.println("around after ......");
        return obj;
    }

    @Override
    public void afterReturning() {
        System.out.println("afterReturning......");

    }

    @Override
    public void afterThrowing() {
        System.out.println("afterThrowing......");
    }

}
           
Contract programming with Ping AOP

2 Create a proxy object

In Java's JDK, a static proxy-like method, newProxyInstance, is provided, giving us a proxy to generate:

public static Object  newProxyInstance(ClassLoader classLoader, Class<?>[] interfaces, 
    InvocationHandler invocationHandler) throws IllegalArgumentException
           

It has 3 parameters:

classLoader – class loader; (proxed class)

•interfaces – bound interfaces, i.e. which interfaces to bind the proxy object to, can be multiple; (list of interfaces implemented by the proxy class)

• invocationHandler – Binding proxy object logic implementation.

The invokationHandler here is an interface InvokationHandler object that defines an invoke method that implements the convention flow of the proxy object

package com.springboot.chapter4.proxy;
package com.springboot.chapter4.proxy;
/**** imports ****/
public class ProxyBean implements InvocationHandler {

     private Object target = null;//代理对象
     private Interceptor interceptor = null;//拦截器

     public ProxyBean (Object target,Interceptor interceptor){
       this.target=target;
       this.interceptor=interceptor;
     }

     /**
     * 处理代理对象方法逻辑
     * @param proxy 代理对象
     * @param method 当前方法
     * @param args  运行参数
     * @return 方法调用结果
     * @throws Throwable 异常
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)  {
        // 异常标识
        boolean exceptionFlag = false;
        Invocation invocation = new Invocation(target, method, args);
        Object retObj = null; 
        try {
            if (this.interceptor.before()) {
                retObj = this.interceptor.around(invocation);
            } else {
                retObj = method.invoke(target, args);
            }
        } catch (Exception ex) {
            // 产生异常
            exceptionFlag = true;
        }
        this.interceptor.after();
        if (exceptionFlag) {
            this.interceptor.afterThrowing();
        } else {
            this.interceptor.afterReturning();
            return retObj;
        }
        return null;
    }

}
           

As you can see from the above example: the proxy object proxy beastBean defines the weaving process, the invoke method, which wraps the interceptor MyInterceptor onto the method of the target object tagert.

3. Test the effect

/**
     * 绑定代理对象
     * @param target 被代理对象
     * @param interceptor 拦截器
     * @return 代理对象
     */
    public static Object getProxyObject(Object target, Interceptor interceptor) {
        ProxyBean proxyBean = new ProxyBean(target,interceptor);//proxyBean定义了织入流程即invoke方法
        // 生成代理对象
        Object proxy = Proxy.newProxyInstance(
                       target.getClass().getClassLoader(), 
                       target.getClass().getInterfaces(),
                       proxyBean);//proxyBean实现target对象的接口列表,实现方式为调用代理对象的方法时通过invoke(invoke可调用target对象的方法)调用target对象对应的方法。
        // 返回代理对象
        return proxy;
    }
           
private static void testProxy() {
    IHelloService helloService = new HelloServiceImpl();//被代理对象
    // 按约定获取proxy
    IHelloService proxy = (IHelloService) getProxyObject(
        helloService, new MyInterceptor());
    proxy.sayHello("zhangsan");
    System.out.println("\n###############name is null!!#############\n");

    proxy.sayHello(null);
}
           

Spring AOP concepts and terminology

Spring AOP is a method-based AOP that can only be applied to methods. There are several ways to configure AOP in Spring, and since Spring Boot is annotated, only @AspectJ annotations are described here. AOP can reduce a lot of repetitive work, and the most typical application scenario is the control of database transactions. For example, the opening and closing of the database and the commit and rollback of transactions have processes that you implement by default. In other words, you don't need to complete them, you need to complete the sql step, and then weave into the process.

Contract programming with Ping AOP
@Autowired
private UserDao = null;
......

@Transactional
public int inserUser(User user) {
    return userDao.insertUser(user);
}
           

Here you can see that only a note @Transactional is used, indicating that the method requires the transaction to run, without any code for the database to open and close, and without the code for transaction rollback and commit, but to open and close database resources, roll back and commit transactions.

1. AOP Terminology:

• join point: Corresponds to the specific intercepted object, because Spring can only support methods, so the intercepted object often refers to a specific method, for example, we mentioned earlier HelloServiceImpl's sayHello method is a connection point, AOP will weave it into the corresponding process through dynamic proxy technology.

Point cut: Sometimes, our tangent can be applied not only to a single method, but also to different methods of multiple classes, in which case the connection point can be defined by the rules of the regular and indicator. The cut point is the concept of providing such a function.

• Advice: According to the method of the agreed process, it is divided into pre-notification (beforeadvice), post-notification (after advice), surround notification (around advice), afterReturning advice and afterThrowing advice, which will be woven into the process according to the agreement. You need to understand the order in which they are in the process and the conditions under which they run.

Target: i.e. the proxyed object, for example, the HelloServiceImpl instance in contract programming is a target object, it is proxied.

• Introduction: Refers to the introduction of new classes and their methods to enhance the functionality of existing beans.

• Weaving: It is a process of generating a proxy object for the original service object through dynamic proxy technology, and then intercepting the connection point that matches the tangent point definition, and weaving various notifications into the convention process according to the convention.

• Aspect: Is a content that can define cut points, various types of notifications, and introductions, and Spring AOP will enhance the function of the Bean or weave the corresponding methods into the process through its information.

Contract programming with Ping AOP

2. Specific implementation

2.1. Add maven dependencies

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
           

2.2. Defining Slices

First of all, Spring declares it with @Aspect as a tangent, and when it takes @Aspect as a note, Spring will know that it is a tangent, and then we can define various types of notifications through various annotations (@Before, @After, @AfterReturning, and @AfterThrowing).

package com.springboot.chapter4.aspect;
/**** imports ****/
@Component //注入到Spring IOC
@Aspect //定义切面
public class MyAspect {

    //引入
    @DeclareParents(value= "co.springboot.chapter4.aspect.service.impl.UserServiceImpl",defaultImpl=UserValidatorImpl.class) 
    public UserValidator userValidator;

    //定义切点
    @Pointcut("execution(* com.springboot.chapter4.aspect.service.impl.UserServiceImpl.printUser(..))")
    public void pointCut() {
    }

    //无参模式
    @Before("pointCut()")
    public void before() {
        System.out.println("before ......");
    }

    //获取参数模式
    //方式1:切点处加入对应的正则式
    //方式2:对于非环绕通知还可以使用一个连接点(JoinPoint)类型的参数,通过它也可以获取参数
    //正则式pointCut() && args(user)中,pointCut()表示启用原来定义切点的规则,并且约定将连接点(目标对象方法)名称为user的参数传递进来。这里要注意,JoinPoint类型的参数对于非环绕通知而言,Spring AOP会自动地把它传递到通知中;对于环绕通知而言,可以使用ProceedingJoinPoint类型的参数。
    @Before("pointCut() && args(user)") 
    public void beforeParam(JoinPoint point, User user) {
        Object[] args = point.getArgs();
        System.out.println("before ......");
    }  

    //环绕通知
    //注意:用环绕通知注解测试的时候总是不按顺序执行,估计是Spring版本之间的差异留下的问题,这是在使用时需要注意的。所以在没有必要的时候,应尽量不要使用环绕通知,它很强大,但是也很危险。
   @Around("pointCut()")
   public void around(ProceedingJoinPoint jp) throws Throwable {
       System.out.println("around before......");
       jp.proceed();//回调目标对象的原有方法

      System.out.println("around after......");
   }

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

    @AfterReturning("pointCut()")
    public void afterReturning() {
        System.out.println("afterReturning ......");
    }

    @AfterThrowing("pointCut()")
    public void afterThrowing() {
        System.out.println("afterThrowing ......");
    }    
}
           

The base types used in the above examples are as follows

View Code           

2.3 Defining Tangent Points

execution(* com.springboot.chapter4.aspect.service.impl.UserServiceImpl.printUser(..))
           

• execution represents the method of intercepting the regular match inside at the time of execution;

•* represents a method of arbitrary return type;

•com.springboot.chapter4.aspect.service.impl.UserServiceImpl specifies the fully qualified name of the target object;

•printUser specifies the method of the target object;

•(..) Represents any parameter to match.

2.4 Use @DeclareParents definitions to introduce

@DeclareParents, its role is to enhance the original service on the premise of not invading the original business, it has two properties that must be configured, value and defaultImpl.

•value: Points to the target object you want to enhance, here to enhance the UserServiceImpl object, so you can see the configuration configured as com.springboot.chapter4.aspect.service.impl.UserServiceImpl+.

•defaultImpl: Introduces an enhanced class, configured here as UserValidatorImpl, to provide the ability to verify that the user is empty.

2.5 Use the slice indicator

The indicators that can be used in the slice are as follows, such as the arg() parameter value of the tangent point in the above example

Contract programming with Ping AOP

3. Test the effect

package com.springboot.chapter4.aspect.controller;
/**** imports ****/
// 定义控制器
@Controller
// 定义类请求路径
@RequestMapping("/user")
public class UserController {

    // 注入用户服务
    @Autowired
    private UserService userService = null;

    // 定义请求
    @RequestMapping("/print")
    // 转换为JSON
    @ResponseBody
    public User printUser(Long id, String userName, String note) {
        User user = new User();
        user.setId(id);
        user.setUsername(userName);
        user.setNote(note);
        userService.printUser(user);// 若user=null,则执行afterthrowing方法

        //测试引入
        UserValidator userValidator = (UserValidator)userService;
        if (userValidator.validate(user)) {
            userService.printUser(user);
        }
 
        return user;// 加入断点
    }
}
        
           

3.1 No effects are introduced

before ......

id =1 username =user_name_1 note =2323

after ......

afterReturning ......

Contract programming with Ping AOP

3.2 Introduce effects

Introduced a new interface: UserValidator

around before......

before ......

id =1 username =user_name_1 note =2323

around after......

after ......

afterReturning ......

Contract programming with Ping AOP

3.4 Principles of Introduction

The second argument of newProxyInstance here is an array of objects, which means that when the proxy object is produced here, Spring will pass in the two interfaces of UserService and UserValidator, so that the proxy object is hung under these two interfaces, so that the proxy object can be converted to each other and use their methods.

Object proxy = Proxy.newProxyInstance(
    target.getClass().getClassLoader(), 
    target.getClass().getInterfaces(),
    proxyBean);
           

3.5 Weaving Methods

Above we are all using the interface (such as UserService) + implementation class (such as UserServiceImpl) pattern, which is the recommended way of Spring, and this book also follows this way. However, whether to have an interface is not a mandatory requirement of Spring AOP, and there are many implementations of dynamic proxies, the JDK we talked about earlier is only one of them, and the industry is more popular with CGLIB, Javassist, ASM, etc. Spring uses the JDK and CGLIB, which for the JDK requires the target object to be proxied must have an interface, while for the CGLIB it does not. So by default, Spring will follow a rule that when you need to use an AOP class ownership interface, it will run as a JDK dynamic proxy, otherwise it will run as a CGLIB.

Interface examples are not used

......
// 定义控制器
@Controller
// 定义类请求路径
@RequestMapping("/user")
public class UserController {
    // 使用非接口注入
    @Autowired
    private UserServiceImpl userService = null;

    // 定义请求
    @RequestMapping("/print")
    // 返回JSON
    @ResponseBody
    public User printUser(Long id, String userName, String note) {
        User user = new User();
        user.setId(id);
        user.setUsername(userName);
        user.setNote(note);
        userService.printUser(user);
        return user;// 加入断点测试
     }
     ......
}
           
Contract programming with Ping AOP

At this point, Spring has used CGLIB to generate proxy objects for us, thus weaving the contents of the slice into the corresponding process. When using an interface, use the JDK to generate a proxy object for us.

4. Multiple facets

Spring provides an annotation @Order and an interface Ordered, and we can specify the order of the facets using any one of them.

......
@Aspect
@Order(1)
public class MyAspect1 {
......
}

//或者

......
@Aspect
public class MyAspect1 implements Ordered {
    // 指定顺序
    @Override
    public int getOrder() {
        return 1;
    }
    ....
}
           

The effect is as follows

MyAspect1 before ......
MyAspect2 before ......
MyAspect3 before ......
测试多个切面顺序
MyAspect3 after ......
MyAspect3 afterReturning ......
MyAspect2 after ......
MyAspect2 afterReturning ......
MyAspect1 after ......
MyAspect1 afterReturning ......           

If you feel that the small writing is good, please quality three consecutive: like + forward + follow. I will try to write better works to share with you.

Contract programming with Ping AOP

Original https://www.cnblogs.com/ImBit/p/16100459.html