Java动态代理(JDK动态代理和cglib)

JDK动态代理

JDK的动态代理只允许代理接口

重要的类和接口Proxy和InvocationHandler
Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)方法创建目标对象的代理对象
获取到目标对象的代理对象之后,执行目标对象的方法实际上是通过调用代理对象的invoke方法实现的
我们可以在invoke方法中增加额外的通用的逻辑来对目标对象进行增强

/**
 * @Description 代理对象
 */
public class ProxyHandler implements InvocationHandler{
    private Object target;

    public Object getProxyHandler(Object target){
        this.target = target;
        Object o = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        return o;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object invoke = method.invoke(target, args);
        System.out.println("After");
        return invoke;
    }
}

获取到代理对象之后,像调用目标对象一样调用代理对象的方法,直观上和普通的调用没有任何区别,但是实际上调用的是代理对象的invoke方法,在invoke方法里面可以做的事情有很多,可以增强甚至重写某些方法,通过method.getName()来对指定方法进行处理

public class ProxyHandlerTest extends TestInit {
    @Autowired
    ISalesOrderFlowDomainService salesOrderFlowDomainService;
    @Test
    public void test(){
        ProxyHandler proxyHandler = new ProxyHandler();
        ISalesOrderFlowDomainService iSalesOrderFlowDomainService = (ISalesOrderFlowDomainService) proxyHandler.getProxyHandler(salesOrderFlowDomainService);
        SalesOrderFlowDTO salesOrderFlow = iSalesOrderFlowDomainService.getSalesOrderFlow("20210702000001", OrderFlowType.SIGNED);
        System.out.println(salesOrderFlow);
    }
}

使用Cglib实现动态代理

Cglib不是jdk自带的jar包,需要下载并加入到项目中
CGLIB代理相关的类:

  • net.sf.cglib.proxy.Enhancer 主要的增强类。
  • net.sf.cglib.proxy.MethodInterceptor 主要的方法拦截类,它是Callback接口的子接口,需要用户实现。
  • net.sf.cglib.proxy.MethodProxy JDK的java.lang.reflect.Method类的代理类,可以方便的实现对源对象方法的调用。

CGLIB动态代理的原理就是用Enhancer生成一个原有类的子类,并且设置好callback到proxy, 则原有类的每个方法调用都会转为调用实现了MethodInterceptor接口的proxy的intercept() 函数

实现MethodInterceptor接口

public class CglibProxy implements MethodInterceptor{
    //主要的增强类。
    private Enhancer enhancer = new Enhancer();
    @Override
    /**
     * 
     * @param o 是被代理对象
     * @param method 调用方法的Method对象
     * @param args 方法参数
     * @param methodProxy
     * @return cglib生成用来代替Method对象的一个对象,使用MethodProxy比调用JDK自身的Method直接执行方法效率会有提升
     * @throws Throwable
     */
    public Object intercept(Object o, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        System.out.println("before " + methodProxy.getSuperName());  
        System.out.println(method.getName());  
        Object o1 = methodProxy.invokeSuper(o, args);  
        //Object o2 = method.invoke(o, args); 使用这种方式会发生死循环,因为方法会被拦截
        System.out.println("after " + methodProxy.getSuperName());  
        return o1;  
    }
    
    public  Object newProxyInstance(Class<?> c) {
        //设置产生的代理对象的父类。
        enhancer.setSuperclass(c); 
        //设置CallBack接口的实例
        enhancer.setCallback(this);  
        //使用默认无参数的构造函数创建目标对象 
        return enhancer.create();  
    }
}

被代理对象和测试类

public class CglibDemo {
    public static void main(String[] args) {
        CglibProxy cglibProxy = new CglibProxy();  
        Do o = (Do)cglibProxy.newProxyInstance(Do.class);  
        System.out.println(o.doSomething(5));
    }
}
class Do{
    public int doSomething(int num){
        System.out.println("方法执行中。。。。。。");
        return num;
    }
}

Jdk和Cglib 的区别

java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

  1. 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP
  2. 如果目标对象实现了接口,可以强制使用CGLIB实现AOP
  3. 如果目标对象没有实现接口,必须采用CGLIB库.
    spring会自动在JDK动态代理和CGLIB之间转换如何强制使用CGLIB实现AOP?
  • 添加CGLIB库,SPRING_HOME/cglib/*.jar
  • 在spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class=“true”/>
    JDK动态代理和CGLIB字节码生成的区别?
  • JDK动态代理只能对实现了接口的类生成代理,而不能针对类
  • CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
    因为是继承,所以该类或方法最好不要声明成final

【参考文章】
【1】https://www.cnblogs.com/shijiaqi1066/p/3429691.html

上一篇:封装,继承,super关键字


下一篇:这才是Springboot事务创建流程的正确打开方式(附源码分析)