Java动态代理

一、JDK动态代理

   基于接口实现,要求委托类要是接口的实现。

  Java的 java.lang.reflect 包下提供了 Proxy 类和一个InvocationHandler接口。

  Proxy定义了生成JDK动态代理类的方法 getProxyClass(ClassLoader loader,Class<?>... interfaces)生成动态代理类

  InvocationHandler 是被动态代理类回调的接口,接口中的invoke()方法调用委托类(被代理的类)的实际方法,我们所有需要的针对委托类的统一增强逻辑都放在invoke()方法中,在调用委托类接口方法之前或之后。

  生成JDK动态代理步骤:

    1、定义需要被代理的接口

public interface Sleep {
    void goToSleep();
}

    2、定义委托类,并实现1中的接口

public class SleepImpl implements Sleep {
    @Override
    public void goToSleep() {
        System.out.println("睡觉");
    }
}

    3、定义 InvocationHandler 接口的实现类,并重写 invoke 方法,在invoke方法中调用委托类的方法,并可在调用前后添加增强逻辑

public class MyInvocationHandler<T> implements InvocationHandler {

    /**
     * 被代理的目标对象
     */
    private T target;

    public MyInvocationHandler(T target) {
        this.target = target;
    }

    /**
     * 调用委托类的实际方法及执行增强逻辑
     *
     * @param proxy
     * @param method
     * @param args
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 增强逻辑
        System.out.println("前增强");

        // 调用委托类的实际方法
        Object result = method.invoke(target, args);

        // 增强逻辑
        System.out.println("后增强");

        // 结果返回
        return result;
    }

}

    4、实例化委托类,并通过Proxy.newProxyInstance 生成代理对象

     // 实例化委托类
        Sleep sleep = new SleepImpl();

        // 生成代理对象
        Sleep sleepProxy= (Sleep) Proxy.newProxyInstance(sleep.getClass().getClassLoader(),sleep.getClass().getInterfaces(),new MyInvocationHandler<Sleep>(sleep));

        // 执行代理类增强后的方法
        sleepProxy.goToSleep();
  建议整合步骤3和4,使用匿名内部类的方式,如:
     // 实例化委托类
        Sleep sleep = new SleepImpl();

        // 生成代理对象
        Sleep sleepProxy = (Sleep) Proxy.newProxyInstance(sleep.getClass().getClassLoader(), sleep.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 增强逻辑
                System.out.println("前增强");
                // 调用委托类的实际方法
                Object result = method.invoke(sleep, args);
                // 增强逻辑
                System.out.println("后增强");
                // 结果返回
                return result;
            }
        });

        // 执行代理类的方法
        sleepProxy.goToSleep();


 

  JDK动态代理特点

    1、生成的代理类:$Proxy0 extends Proxy implements Subject,我们看到代理类继承了Proxy类,Java的继承机制决定了JDK动态代理类们无法实现对 类 的动态代理。所以也就决定了JDK动态代理只能对接口进行代理。

    2、每个生成的动态代理实例都会关联一个调用处理器对象,可以通过 Proxy 提供的静态方法 getInvocationHandler去获得代理类实例的调用处理器对象。在代理类实例上调用其代理的接口中所声明的方法时,这些方法最终都会由调用处理器的 invoke 方法执行。

    3、代理类的根类 java.lang.Object 中有三个方法也同样会被分派到调用处理器的 invoke 方法执行,它们是 hashCode,equals 和 toString,可能的原因有:一是因为这些方法为 public 且非 final 类型,能够被代理类覆盖;二是因为这些方法往往呈现出一个类的某种特征属性,具有一定的区分度,所以为了保证代理类与委托类对外的一致性,这三个方法也应该被调用处理器分派到委托类执行。

  JDK动态代理的不足

  JDK动态代理的代理类字节码在创建时,需要实现业务实现类所实现的接口作为参数。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。(JDK动态代理重要特点是代理接口)并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。动态代理只能对接口产生代理,不能对类产生代理。

 

二、CGLib动态代理

  基于继承,CGlib是针对类来实现代理的,他的原理是对委托类(被代理的目标类)生成一个子类,并覆盖其中方法实现增强。

  因为底层是基于创建被代理类的一个子类,所以它避免了JDK动态代理的缺陷。

  但因为采用的继承,所以不能对final修饰的类进行代理。

  生成CGLib动态代理的步骤:

  1、定义委托类(需要被代理的类)

public class Sleep {
    public void goToSleep() {
        System.out.println("sleeping");
    }
}

  2、定义 MethodInterceptor 接口的实现类,并重写 intercept方法,在intercept方法中调用委托类的方法,并可在调用前后添加增强逻辑

public class CGLibDynamicProxy implements MethodInterceptor {

    /**
     * 调用委托类的实际方法及执行增强逻辑
     * @param obj
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        // 增强逻辑
        System.out.println("前增强");

        //调用委托类(父类中)的方法
        Object result = methodProxy.invokeSuper(obj, objects);

        // 增强逻辑
        System.out.println("后增强");

        // 返回执行结果
        return result;
    }
}

  3、通过 Enhancer.create 生成代理对象

     // 生成Sleep类的代理对象
        Sleep sleepProxy= (Sleep) Enhancer.create(Sleep.class,new CGLibDynamicProxy());
        
        // 执行代理对象增强后的方法
        sleepProxy.goToSleep();

  注意:Enhancer 、 MethodInterceptor  MethodProxy 均来自 org.springframework.cglib.proxy 包。

  建议整合步骤2和3,使用匿名内部类的方式,如:

        // 生成Sleep类的代理对象
        Sleep sleepProxy = (Sleep) Enhancer.create(Sleep.class, new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                // 增强逻辑
                System.out.println("前增强");
                //调用委托类(父类中)的方法
                Object result = methodProxy.invokeSuper(obj, objects);
                // 增强逻辑
                System.out.println("后增强");
                return result;
            }
        });

        // 执行代理类增强后的方法
        sleepProxy.goToSleep();

  

  CGLib动态代理特点:

    1、CGlib可以传入接口也可以传入普通的类,接口使用实现的方式,普通类使用会使用继承的方式生成代理类;

    2、由于是继承方式,如果是static方法,private方法,final方法等描述的方法是不能被代理的;

    3、做了方法访问优化,使用建立方法索引的方式避免了传统JDK动态代理需要通过Method方法反射调用;

    4、提供callback 和filter设计,可以灵活地给不同的方法绑定不同的callback。编码更方便灵活;

    5、CGLIB会默认代理Object中equals,toString,hashCode,clone等方法。比JDK代理多了clone

END.

原文地址:https://www.cnblogs.com/yangyongjie/p/14603633.html