java jdk dynamic proxy + cglib dynamic proxy / and @Async asynchronous annotation invalidation problem

What is the proxy model

The agent mode mainly enhances our method before and after execution

Agent mode application scenarios

1. Log collection
2. Permission control
3. Realize aop
4. Mybatis mapper
5. Spring transaction
6. Global catch exception
7. Rpc remote call interface
8. Proxy data source

The difference between Jdk and Cglib dynamic proxy

1. Jdk dynamic proxy uses reflection technology to generate anonymous proxy classes and uses the InvokeHandler callback method to implement enhancements. It is also an interface-based approach to proxy.

2. Cglib dynamic agent uses asm bytecode technology to generate a sub-class coverage method to achieve enhancement, while using fastClass mechanism to index the entire agent class is more efficient than reflection

3. If you need a proxy object in Spring

  • Jdk dynamic proxy used if the interface is implemented
  • Cglib dynamic proxy used if the interface is not implemented

1. JDK dynamic proxy

The general steps of JDK dynamic proxy are as follows:

1. Create a proxy interface and class;

2. Implement the InvocationHandler interface, and process all methods declared in the target interface in a unified manner;

3. Call the static method of Proxy, create a proxy class and generate the corresponding proxy object;

Implementation principle: The use of the interceptor mechanism must implement the invoke method in the InvocationHandler interface to
enhance our target method.

1. Agency method

package com.xijia.proxy.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
  * jdk 动态代理
  * @author wangsong
  * @mail  [email protected]
  * @date  2020/8/30 0030 12:35
  * @version 1.0.0
  */
public class JdkInvocationHandler implements InvocationHandler {

    /**
     * 目标对象(需要被代理对象)
     */
    private Object target;

    /**
     * 获取目标对象
     * @param target
     */
    public JdkInvocationHandler(Object target) {
        this.target = target;
    }


    /**
     * 代理执行方法
     * @author wangsong
     * @param proxy JavaJdk动态生成代理类对象$proxy0
     * @param method 目标方法 反射获取到的
     * @param args  目标方法需要传递的参数
     * @return java.lang.Object
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Jdk动态代理方式开始:");
        // 执行目标方法  根据接口的方法 反射技术执行目标对象的对应的方法
        Object result = method.invoke(target, args);
        System.out.println("Jdk动态代理方式结束:");
        /***
         * Method method 类型接口的方法 采用java反射机制执行我们的对应目标对象的方法
         */
        return result;
    }

    /**
     * 生成目标对象,通过Jdk动态代理反射技术生成代理对象 调用代理类对象的方法的时候会走InvocationHandlerinvoke方法
     *
     * @param <T>
     * @return
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                this
        );
    }
}

2. OrderService interface

public interface OrderService {

    String addOrder(String orderId, String orderName);
    //      String addOrder(String v1,String v2 );
}

3. Implementation of OrderServiceImpl

public class OrderServiceImpl implements OrderService {
    @Override
    public String addOrder(String orderId, String orderName) {
        System.out.println("业务方法 " + orderId);
        return orderId + "-" + orderName;
    }
}

4. Test and use

/**
 * jdk 动态代理
 */
public class Test {
    public static void main(String[] args) {
        useJdkInvocation();
    }
    
    /**
     * 使用jdk 的动态代理方法生成class文件并执行
     */
    public static void useJdkInvocation() {
        JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
        // 保存动态代理生成的 class 到本地
        // System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        // 获取代理类对象
        OrderService proxy = jdkInvocationHandler.getProxy();
        String res = proxy.addOrder("12345689", "测试");
        System.out.println("返回数据为:" + res);
    }
 }

Insert picture description here

Two, Cjlib dynamic proxy

Cglib relies on the ASM bytecode technology to directly generate class files, and
use fastclass to index the proxy class when it is read into the program by the class loader . The file does not need to rely on reflection to find the target method,
so the efficiency is more dynamic than Jdk The agent must be high.

1. Cjlib dynamic proxy method

/**
 *  cglib动态代理
 */
public class CglibMethodInterceptor implements MethodInterceptor {
    /**
     * @param obj 执行对象class(被代理类)
     * @param method 执行对象class的方法
     * @param args 执行对象class的方法的参数
     * @param proxy 代理类
     * @return
     * @throws Throwable
     */
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println(">> cglib动态代理执行开始");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println(">> cglib动态代理执行结束");
        return result;
    }
}

2. Interface

public interface MemberService {

    String addMember(String userName);
}

3. Realize

public class MemberServiceImpl  implements MemberService  {
    public String addMember(String userName) {
        System.out.println("执行方法-------");
        return userName;
    }
}

4. Test and use

public class Test2 {
    public static void main(String[] args) {
        // 保存class 文件到-->
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code");
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(MemberServiceImpl.class);
        enhancer.setCallback(new CglibMethodInterceptor());
        // 创建代理代理对象
        MemberService memberService = (MemberService) enhancer.create();
        String res = memberService.addMember("1720696548");
        System.out.println("----> cglib动态代理返回数据为:" + res);
    }
}

Insert picture description here

Three, @Async asynchronous annotation invalidation processing

Invalidation description

  • 1. As long as MemberServiceImpl inherits the interface (implements MemberService), the @RestController annotation and @Async annotation cannot exist at the same time, (jdk dynamic proxy bug)
  • 2. In the MemberServiceImpl implementation class, directly using this to call the method annotated with @Async in the current class will not take effect, because this directly calls the current class without passing through the proxy object. The creation of asynchrony is performed at the time of proxy, and all this does not Will perform asynchronous operations
  • 3. In the MemberServiceImpl implementation class, use @Async to first obtain the proxy object of the current class, and then use the proxy object of the current class to call the @Async annotated method, or the method that needs @Async annotation to perform abnormal operations Create a class separately, you can avoid this call without going through the proxy object (eg: aop)

Let's go through a case to prove

1. Create @ExtAsync annotation

/**
  * 异步注解
  * @author wangsong
  * @mail  [email protected]
  * @date  2020/8/30 0030 20:59
  * @version 1.0.0
  */
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ExtAsync {
}

2. Create @ExtAsyncAop to start asynchronous threads

package com.xijia.aop.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


/**
  * 判断是否需要异步执行方法的aop代理
  * @author wangsong
  * @mail  [email protected]
  * @date  2020/8/30 0030 20:58
  * @version 1.0.0
  */
@Component
@Aspect
@Slf4j
public class ExtAsyncAop {

    private ExecutorService executorService;

    public ExtAsyncAop() {
        executorService = Executors.newFixedThreadPool(10);
    }

    @Around(value = "@annotation(com.xijia.aop.ext.ExtAsync)")
    public void doBefore(ProceedingJoinPoint joinPoint) throws Throwable {
        // 直接获取到方法上有加上ExtAsync
        log.info(">>>拦截到我们方法上有加上ExtAsync");
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                // 执行我们的目标方法
                try {
                    joinPoint.proceed();
                } catch (Throwable throwable) {
                    throwable.printStackTrace();
                }
            }
        });

    }
}

3. SpringUtils tool class, get bend

package com.xijia.aop.utils;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

/**
 * SpringUtils 工具类
 */
@Component
public class SpringUtils implements ApplicationContextAware {


    private static ApplicationContext applicationContext = null;

    public static ApplicationContext getApplicationContext() {
        return applicationContext;
    }


    public static <T> T getBean(String beanId) {
        return (T) applicationContext.getBean(beanId);
    }

    public static <T> T getBean(Class<T> requiredType) {
        return (T) applicationContext.getBean(requiredType);
    }


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringUtils.applicationContext = applicationContext;
    }

}

4. Create an interface

public interface MemberService {
    String addUser();
}

5. Create an implementation

package com.xijia.aop.service.impl;

import com.xijia.aop.ext.ExtAsync;
import com.xijia.aop.service.MemberService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * 1、MemberServiceImpl 只要继承了接口(implements MemberService ),那么 @RestController 注解和 @Async 注解就不能同时存在, (jdk动态代理的bug)
 * 2、在 MemberServiceImpl 实现类中, 直接使用this 调用异步 @Async 将不生效, 因为this直接调用当前类,没有经过代理对象, 创建异步是在代理时执行的,所有this不会进行异步操作
 * 3、在 MemberServiceImpl 实现类中, 使用@Async, 直接获取当前类的代理对象, 使用代理对象调用带有 @Async 注解的方法就ok了
 * 4、在 MemberServiceImpl 实现类中, 使用@Async, 或者把 @Async异常操作方法单独建立一个类, 就能避免this调用了,不经过代理对象了(如: aop)
 * @author wangsong
 * @mail [email protected]
 * @date 2020/8/30 0030 21:16
 * @version 1.0.0
 */
@RestController
@Slf4j
public class MemberServiceImpl  implements MemberService {  // implements MemberService

    // 方法二: 使用新的类来处理异步操作
  //  @Autowired
  //   private MemberServiceImplAsync memberServiceImplAsync;

    @Override
    @GetMapping("/addUser")
    public String addUser() {
        log.info(">>>流程1");

        // 方法一: 先获得代理对象,在通过代理对象调用本类的 addUserLog方法
        MemberServiceImpl proxy = SpringUtils.getBean("memberServiceImpl");
        proxy.addUserLog();

        // 方法二: 使用新的类来处理异步操作
        //  memberServiceImplAsync.addUserLog();

        //  直接调用调用无效不会被动态代理,@Async注解自然也无效, 因为开启异步是在动态代理中开启的
       //  this.addUserLog();
        log.info(">>>流程3");
        return "success";
    }

    /**
     * 异步方法
     * @return
     */
    @ExtAsync // 自定义
    //@Async  // 该注解不能和@RestController 或 @Controller 同时存在
    public void addUserLog() {
        try {
            Thread.sleep(1000);
        } catch (Exception e) {
        }
        log.info(">>>流程2");
    }
}

Normal exception, if the test uses this, then asynchronous will be invalid
Insert picture description here

  • Some of the above content comes from the ant classroom
  • Personal open source project (universal background management system) –> https://gitee.com/wslxm/spring-boot-plus2 , you can check it out if you like
  • This is the end of this article. If you find it useful, please like or pay attention to it. We will continue to update more content from time to time... Thank you for watching!

Guess you like

Origin blog.csdn.net/qq_41463655/article/details/108311756