动态代理概述

动态代理是相对静态代理而言。静态代理是代理类在代码运行前已经创建好,并生成class文件;动态代理类是指代理类在程序运行时创建的代理模式。动态代理类的代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成
相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。 在实际调用中,根本不关心代理类的创建与销毁,使用动态代理不仅可以屏蔽代理类的编写。
在说明代理前,先介绍几个基本概念:

委托类,指的是代理模式中的被代理对象
代理类,指的是生成的代表委托类的一个角色

静态代理

静态代理是代理类在编译期间就创建好了,注意,这里的代理类不是编译器生成的代理类,而是手动创建的类。在编译时就已经将接口,被代理类,代理类等确定下来。其实现主要分以下四个步骤:
(1) 定义需要代理的接口并声明接口方法;
(2) 定义上述接口的实现类,也即委托类;
(3) 定义代理类,实现上述接口,接口方法通过转调委托类实例的方法实现;
(4) 创建代理类实例,实现接口调用。
示例代码如下:
(1) 定义接口

public interface ICustomService {
    String sayHello(String str);
}

(2) 定义委托类

public class CustomService implements ICustomService {
    @Override
    public String sayHello(String str) {
        return "Hello " + str;
    }
}

(3) 定义代理类

public class CustomServiceStaticProxy implements ICustomService {
    private CustomService customService = new CustomService();

    @Override
    public String sayHello(String str) {
        return customService.sayHello(str);
    }
}

(4) 创建代理类实例并转调接口

public class CustomServiceStaticProxyTest {
    @Test
    public void sayHello() {
        CustomServiceStaticProxy customServiceStaticProxy = new CustomServiceStaticProxy();
        Assert.assertEquals("Hello World", customServiceStaticProxy.sayHello("World"));
    }
}

代理类持有委托类的实例,代为执行具体类实例方法。代理模式就是在访问实际对象时引入一定程度的间接性,因为这种间接性,可以附加多种用途。这里的间接性就是指客户端不直接调用实际对象的方法,客户端依赖公共接口并使用代理类。 那么我们在代理过程中就可以加上一些其他用途。 代理类存在的意义是为了增加一些公共的逻辑代码。代理类可以解除方法间的依赖,并可实现方法增强

动态代理

动态代理的常见实现方式有两种:JDK动态代理和CGLIB动态代理。JDK动态代理技术生成的代理类($Proxy0 extends Proxy implements XXX)时,继承了Proxy类,所以JDK动态代理类们无法实现对类的动态代理。如果需要实现对类的动态代理,可以使用CGlib技术。

JDK动态代理

JDK为实现动态代理,在java的java.lang.reflect包中提供了Proxy类InvocationHandler接口。其中,类Proxy定义了生成JDK动态代理类的方法 getProxyClass(ClassLoader loader,Class<?>… interfaces)生成动态代理类,返回class实例代表一个class文件。可以保存该 class 文件查看jdk生成的代理类文件。InvocationHandler接口是被动态代理类回调的接口,我们所有需要增加的针对委托类的统一处理逻辑都增加到invoke方法里面,在调用委托类接口方法之前或之后实现自定义功能。JDK动态代理主要有以下四个步骤:
(1) 定义需要代理的接口并声明接口方法;
(2) 定义上述接口的实现类,也即委托类;
(3) 实现InvocationHandler接口并新增newProxyInstance方法;
(4) 创建InvocationHandler实现类的实例,并转调接口。
注意,在动态代理中,没有手动创建代理类的代码,这是与静态代理的本质区别。
示例代码如下:
(1) 定义接口

public interface ICustomService {
    String sayWorld(String str);
}

(2) 定义委托类

public class CustomService implements ICustomService {
    @Override
    public String sayWorld(String str) {
        String result = str + " " + "World";
        System.out.println(result);
        return result;
    }
}

(3) 实现InvocationHandler接口并新增newProxyInstance方法

public class CustomServiceInvocationHandler implements InvocationHandler {
    /**
     * 中间类持有委托类对象的引用,这里会构成一种静态代理关系
     */
    private Object obj ;

    /**
     * 有参构造器,传入委托类的对象
     * @param obj 委托类的对象
     */
    public CustomServiceInvocationHandler(Object obj) {
        this.obj = obj;
    }

    /**
     * 动态生成代理类对象,Proxy.newProxyInstance
     * @return 返回代理类的实例
     */
    public Object newProxyInstance() {
        return Proxy.newProxyInstance(
                // 指定代理对象的类加载器
                obj.getClass().getClassLoader(),
                // 代理对象需要实现的接口,可以同时指定多个接口
                obj.getClass().getInterfaces(),
                // 方法调用的实际处理者,代理对象的方法调用都会转发到这里
                this
        );
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("invoke before");
        Object result = method.invoke(this.obj, args);
        // 死循环
        // https://blog.csdn.net/jilindaxue/article/details/105156989
        // Object result = method.invoke(proxy, args);
        System.out.println("invoke after");
        return result;
    }
}

(4) 创建InvocationHandler实现类的实例,并转调接口

public class Main {
    public static void main(String[] args) {
        CustomServiceInvocationHandler proxyInvocationHandler = new CustomServiceInvocationHandler(new CustomService());
        // 动态创建代理对象
        ICustomService customService = (ICustomService) proxyInvocationHandler.newProxyInstance();
        customService.sayWorld("Hello");
    }
}

在基于JDK动态代理创建对象过程可分为以下四个步骤:
1、通过实现InvocationHandler接口创建自己的调用处理器 IvocationHandler handler = new InvocationHandlerImpl(…);
2、通过为Proxy类指定ClassLoader对象和一组interface代理类需要实现的接口,创建动态代理类类文件,默认JDK并不会保存这个文件到文件中;可以保存起来观察生成的代理类结构Class clazz = Proxy.getProxyClass(classLoader,new Class[]{…});
3、通过上面新建的代理clazz的反射机制获取动态代理类的一个构造函数,其构造函数入参类型是调用处理器接口(IvocationHandler)类型 Constructor constructor = clazz.getConstructor(new Class[]{InvocationHandler.class});
4、通过构造函数实例创建代理类实例,此时需将调用处理器对象作为参数被传入 Interface Proxy = (Interface)constructor.newInstance(new Object[] (handler));
为了简化对象创建过程,Proxy类中的newInstance工具方法封装了2~4,只需两步即可完成代理对象的创建。

CGLIB动态代理

CGLIB(Code Generation Library)是一个功能强大,高性能的方案。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。在实现动态代理时,优先使用Java的动态代理机制创建代理,但当要代理的对象是类或者为了更好的性能,CGLIB是一个好的选择。
简言之,JDK动态代理技术只能实现接口代理,无法实现类代理。CGLIB可实现类代理。注意,因为CGLIB采用的是继承,所以不能对final修饰的类进行代理。final修饰的类不可继承。同样的,对于私有方法,因为不可继承,所以也无法实现动态代理。
在使用CGLIB实现动态代理前,需要引入CGLIB的jar包。使用Maven方式引入如下:

<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>

使用CGLIB实现动态代理主要由以下三个步骤:
(1) 定义需要代理的类,也即委托类;
(2) 实现MethodInterceptor接口并新增CglibProxyFactory方法(封装增强器的实例化);
(3) 创建MethodInterceptor接口实现类的实例,并转调接口。
示例代码如下:
(1) 定义委托类

public class CustomService {
    public String sayHelloWorld(String somebody) {
        return "Hello World to " + somebody;
    }
}

(2) 实现MethodInterceptor接口

public class CglibMethodInterceptor implements MethodInterceptor {
    public Object CglibProxyFactory(Class target) {
        // 创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();
        // 设置委托类
        enhancer.setSuperclass(target);
        // 设置方法拦截器回调引用,对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept() 方法进行拦截
        enhancer.setCallback(this);
        // 创建代理类
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before");
        Object result = proxy.invokeSuper(obj, args);
        System.out.println("after");
        return result;
    }
}

(3) 创建MethodInterceptor接口实现类的示例并转调接口

public class Main {
    public static void main(String[] args) {
        // 方式一:创建加强器,用来创建动态代理类
        Enhancer enhancer = new Enhancer();        // 为代理类指定需要代理的类,也即是父类
        enhancer.setSuperclass(CustomService.class);        // new 一个新的方法拦截器
        CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();       // 设置方法拦截器回调引用,对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept() 方法进行拦截
        enhancer.setCallback(cglibMethodInterceptor);        // 获取动态代理类对象并返回
        CustomService customService = (CustomService) enhancer.create();
        System.out.println(customService.sayHelloWorld("You"));

        // 方式二: new 一个新的方法拦截器,该拦截器还封装一个用于创建代理类的方法
        cglibMethodInterceptor = new CglibMethodInterceptor();
        customService = (CustomService) cglibMethodInterceptor.CglibProxyFactory(CustomService.class);
        System.out.println(customService.sayHelloWorld("You"));
    }
}

CGlib可以传入接口也可以传入普通的类,接口使用实现的方式,普通类使用会使用继承的方式生成代理类。
由于是继承方式,如果是static方法,private方法,final方法等描述的方法是不能被代理的。
做了方法访问优化,使用建立方法索引的方式避免了传统JDK动态代理需要通过Method方法反射调用。
提供callback 和filter设计,可以灵活地给不同的方法绑定不同的callback。编码更方便灵活。
CGLIB会默认代理Object中equals,toString,hashCode,clone等方法。比JDK代理多了clone。

静态代理、JDK动态代理、CGLIB动态代理说明

静态代理是通过在代码中显式编码定义一个业务实现类的代理类,在代理类中对同名的业务方法进行包装,用户通过代理类调用被包装过的业务方法;
JDK动态代理是通过接口中的方法名,在动态生成的代理类中调用业务实现类的同名方法;
CGLIB动态代理是通过继承业务类,生成的动态代理类是业务类的子类,通过重写业务方法进行代理;
静态代理在编译时产生class字节码文件,可以直接使用,效率高。
JDK动态代理和CGLIB代理区别:
JDK动态代理必须实现InvocationHandler接口,通过invoke调用被委托类接口方法是通过反射方式,比较消耗系统性能,但可以减少代理类的数量,使用更灵活。
CGLIB代理无需实现接口,通过生成类字节码实现代理,比反射稍快,不存在性能问题,但CGLIB会继承目标对象,需要重写方法,所以目标对象不能为final类

参考

https://www.cnblogs.com/sandaman2019/p/12636727.html
https://cloud.tencent.com/developer/article/1461796
https://www.runoob.com/w3cnote/cglibcode-generation-library-intro.html

原创不易,如果本文对您有帮助,欢迎关注我,谢谢 ~_~

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值