java静态代理和动态代理详解

你说的曾经没有我的故事 提交于 2020-01-18 14:28:26

目录

一、静态代理

二、动态代理

(一)JDK动态代理

(二)Cglib动态代理

(三)jdk动态代理和cglib动态代理的区别


代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类(被代理类)预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。

为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。通过代理类这中间一层,能有效控制对委托类对象的直接访问,也可以很好地隐藏和保护委托类对象,同时也为实施不同控制策略预留了空间,从而在设计上获得了更大的灵活性。Java 动态代理机制以巧妙的方式近乎完美地实践了代理模式的设计理念。

代理,简单来说就是,代理类和委托类都实现相同的接口,代理类代替委托类去执行从相同接口中实现的委托类中的方法,而代理类在代替委托类去执行方法之前和之后可以进行其他相应的处理。

一、静态代理

  • 特点:代理类和委托类在编译期间就已经确定好。
  • 静态代理举例
/**
 *      静态代理:
 *      特点:代理类和被代理类在编译期间就确定下来
 */
interface ClothFactory{
    void produceCloth();
}
//被代理类
class NikeClothFactory implements ClothFactory{
    public void produceCloth() {
        System.out.println("Nike工厂生产一批运动服。");
    }
}
//代理类
class ProxyClothFactory implements ClothFactory{
    //用被代理对象进行实例化
    private ClothFactory clothFactory = null;
    public ProxyClothFactory(ClothFactory clothFactory){
        this.clothFactory = clothFactory;
    }
    public void produceCloth() {
        System.out.println("代理工厂做一些准备工作");
        clothFactory.produceCloth();
        System.out.println("代理工厂做一些收尾工作");
    }
}
public class StaticProxyTest {
    public static void main(String[] args) {
        //创建被代理类的对象
        ClothFactory nikeClothFactory = new NikeClothFactory();
        //创建代理类的对象
        ProxyClothFactory proxyClothFactory = new ProxyClothFactory(nikeClothFactory);
        proxyClothFactory.produceCloth();
    }
}

二、动态代理

动态代理是指客户通过代理类来调用其它对象的方法,并且是在程序运行时 根据需要动态创建目标类的代理对象。

(一)JDK动态代理

  • jdk动态代理相关的类和接口

       java.lang.reflect.Proxy:这是 Java 动态代理机制的主类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

  • Proxy 的静态方法

         // 方法 1: 该方法用于获取指定代理对象所关联的调用处理器
         static InvocationHandler getInvocationHandler(Object proxy) 
 
        // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
         static Class getProxyClass(ClassLoader loader, Class[] interfaces) 
 
        // 方法 3:该方法用于判断指定类对象是否是一个动态代理类
         static boolean isProxyClass(Class cl) 
 
       // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
          static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h)

  • InvocationHandler 的核心方法

       //当我们通过代理类的对象,调用方法a时,就会自动的调用如下的方法:invoke()
       //将被代理类要执行的方法a的功能就声明在invoke()中

   Object invoke(Object proxy, Method method, Object[] args)

  • jdk实现动态代理需要解决的两个问题

问题一:如何根据加载到内存中的委托类,动态创建出一个代理类及其对象;

              解决办法:反射。通过反射返回代理类对象。

//代理工厂
class ProxyFactory{
    //调用此方法时,传入委托类对象返回代理类对象
    public static Object getProxyInstance(Object obj){  //obj为委托类对象
        MyInvocationHandler handler = new MyInvocationHandler(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}

问题二:当通过代理类的对象访问方法a时,如何动态地去调用委托类中的同名方法。

              解决办法:实现InvocationHandler接口,重写invoke方法。当通过代理类对象调用方法a时,就会自动invoke方             法,因此可以将委托类要执行的方法a的功能逻辑写在invoke方法中。

class MyInvocationHandler implements InvocationHandler{
    private Object obj;
    public MyInvocationHandler(Object obj){
        this.obj = obj;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理类调用委托类的方法之前的处理");
        Object returnValue = method.invoke(obj, args);
        System.out.println("代理类调用委托类的方法之后的处理");
        return returnValue;
    }
}

 

  • jdk动态代理完整代码实现
/**
 *   动态代理
 *   特点:代理类的要在运行时才能确定
 **/
interface Human{
    void getBrief(String brief);
    void eat(String food);
}
//委托类
class SuperMan implements Human{
    public void getBrief(String brief) {
        System.out.println("超人的信仰是"+brief);
    }
    public void eat(String food) {
        System.out.println("超人喜欢吃"+food);
    }
}
//代理工厂
class ProxyFactory{
    //调用此方法时,传入委托类对象返回代理类对象
    public static Object getProxyInstance(Object obj){  //obj为委托类对象
        MyInvocationHandler handler = new MyInvocationHandler(obj);
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
    }
}
class MyInvocationHandler implements InvocationHandler{
    private Object obj;
    public MyInvocationHandler(Object obj){
        this.obj = obj;
    }
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理类调用委托类的方法之前的处理");
        Object returnValue = method.invoke(obj, args);
        System.out.println("代理类调用委托类的方法之后的处理");
        return returnValue;
    }
}
public class DynamicProxyTest {
    public static void main(String[] args) {
        SuperMan man = new SuperMan();
        Human proxyInstance = (Human) ProxyFactory.getProxyInstance(man);
        proxyInstance.getBrief("维护世界和平");
        System.out.println("=============================");
        proxyInstance.eat("电芯");
    }
}

(二)Cglib动态代理

  • cglib可以在运行期间扩展java类与java接口。cglib比jdk动态代理更强大的地方在于,它不仅能接管接口类的方法,而且可以接管普通类的方法(jdk动态代理只能接管接口类的方法)。在Spring、Hibernate中也使用了cglib动态代理。

  • cglib底层使用的是java字节码操作框架——ASM。

  • CGLIB详细学习参照:https://blog.csdn.net/weixin_40391011/article/details/104025265

(三)jdk动态代理和cglib动态代理的区别

  • 原理区别:

jdk动态代理利用反射机制生成一个代理类接口的匿名类,在调用具体方法前调用InvokeHandler来处理,核心是实现InvocationHandler接口,使用invoke方法进行面向切面的处理,调用相应的通知。

而cglib动态代理是利用asm开源包,将代理对象的class文件加载进来,通过修改其字节码生成子类的方法来处理。核心是实现MethodInterceptor接口,使用intercep()方法进行面向切面的处理,调用相应的通知。

      1、如果目标对象实现了接口,默认情况下spring采用JDK动态代理来实现AOP;

      2、如果目标对象实现了接口,spring也可以强制使用CGLIB实现AOP;

      3、如果目标对象没有实现接口,必须使用cglib来实现动态代理,当然Spring可以在JDK动态代理和CGLIB动态代理之间切换。

  • 性能区别:

CGLIB底层使用字节码操作框架ASM,使用字节码技术生成代理类,在jdk6之前比JDK动态代理效率要高。需要注意的是,CGLIB不能对被final修饰的类和方法进行代理,原因是,CGLIB原理是动态生成被代理类的子类。

在JDK6,JDK7,JDK8逐步对JDK动态代理进行优化之后,在调用次数较少的情况下,JDK动态代理的效率高于CGLIB,只有在大量调用的情况下,JDK动态代理的效率才会低一些,但是到了JDK8之后,JDK动态代理效率要比CGLIB高。

  • 各自局限性:

        1、JDK的动态代理只能代理实现了接口的类,没有实现接口的类不能进行JDK动态代理。

        2、cglib的原理是对目标类生成一个子类,并且覆盖其方法实现增强,但因为使用的是继承,所以不能对final修饰的类和方法进               行代理。 

  • 各自优势

        1、JDK动态代理优势:最小化依赖关系,减少依赖意味着减少开发和维护,JDK本身的支持可能比cglib更可靠。平滑进行JDK               版本升级,而字节码类库通常需要进行更新以确保在新版本的java上能够使用。代码实现简单。

        2、cglib动态代理的优势:没有限定对接口的实现。高性能。

 

 

 

 

 

 

 

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!