前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java 动态代理

Java 动态代理

作者头像
wsuo
发布2020-09-10 14:45:28
5830
发布2020-09-10 14:45:28
举报
文章被收录于专栏:技术进阶之路技术进阶之路

代理创建的 3 要素:

  1. 原始对象;
  2. 额外功能;
  3. 代理对象和原始对象实现相同的接口。

一、JDK 动态代理

JDK 实现的动态代理主要是通过 java.lang.reflect 包下的 Proxy 类实现的。

通过调用该类的 newProxyInstance 方法:

代码语言:javascript
复制
newProxyInstance(ClassLoader loader,
                 Class<?>[] interfaces,
                 InvocationHandler h)

该方法的参数详解如下:

  • ClassLoader:类加载器;
  • interfaces:原始对象实现的接口;
  • InvocationHandler:额外功能。

后两个参数比较好理解,因为文章的开头说了,动态代理的 3 要素,原始对象、额外功能、实现相同的接口。

实现相同的接口和额外功能我们都提供了,那么为什么还要提供一个类加载器呢?这里做如下解释:

首先明确类加载器的作用:

  1. 通过类加载器把对应类的字节码加载进 JVM;
  2. 通过类加载器创建类的 Class 对象,进而创建这个类的对象。

每一个类的 .class 文件自动分配与之对应的 ClassLoader。

而我们这里没有创建出代理类,所以就没有源文件,就没有 .class 文件,也就不会分配 ClassLoader,这里采用的是动态字节码技术:

代码语言:javascript
复制
动态字节码技术:创建字节码。
 在动态代理创建的过程中,需要 classloader 创建代理类的 Class 对象;
 可是因为动态代理没有对应的 .class 文件,JVM也就不会为其分配 ClassLoader,但是又需要,所以借用一个 classloader;
 具体借用谁的无所谓。

InvocationHandler 是一个接口,我们可以写个匿名内部类,也可以直接使用 Lambda 表达式实现,这里用 Lambda 演示:

代码语言:javascript
复制
(proxy, method, arguments) -> {
    System.out.println("TestJDKProxy.main");
    return method.invoke(userService, arguments);
}
  • proxy:原始对象;
  • method:待增强的方法;
  • arguments:方法的参数。

我们可以通过返回值将代理过的对象返回。

代码语言:javascript
复制
public class TestJDKProxy {
    public static void main(String[] args) {
        UserService userService = new UserServiceImpl();
        
        /*
         * 1. 借一个 classloader,谁的都行;
         * 2. 原始对象实现的接口;
         * 3. 额外功能。
         * */
        UserService proxyInstance = (UserService) Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), userService.getClass().getInterfaces(),
                (proxy, method, arguments) -> {
                    System.out.println("TestJDKProxy.main");
                    return method.invoke(userService, arguments);
                });

        proxyInstance.login();
        proxyInstance.register();
    }
}

二、CGlib 动态代理

JDK 的方式是原始对象必须实现一个接口,才能进行动态代理,如果不实现接口,可以动态代理吗?

也是可以的,这就要使用 CGlib 的动态代理了,他的代理类继承自原始类,所以也会有原始类的方法。

正如 JDK 的动态代理一样,cglib 也有一个核心类,就是 Enhance 类:

  • 他也需要一个 ClassLoader;
  • 由于是通过继承实现的,所以需要设置父类;
  • 最后设置一个与 InvocationHandler 类似的回调接口。

最后调用 enhancer.create() 方法执行。

代码语言:javascript
复制
public static void main(String[] args) {
    UserService userService = new UserService();
    // 通过 cglib 创建动态代理对象
    Enhancer enhancer = new Enhancer();
    enhancer.setClassLoader(TestCglib.class.getClassLoader());
    enhancer.setSuperclass(userService.getClass());
    
    /*
    *  method 是原始方法;
    *  objects 是原始方法参数;
    * 
    *  这里使用的是 Lambda 表达式的形式。
    */
    enhancer.setCallback((MethodInterceptor) (o, method, objects, methodProxy) -> {
        System.out.println("cglib 的动态代理");
        return method.invoke(userService, objects);
    });
    
    UserService userServiceProxy = (UserService) enhancer.create();
    
    userServiceProxy.login("ws", "1234");
    userServiceProxy.register(new Person());
}

输出结果:

代码语言:javascript
复制
cglib 的动态代理
UserService.login
cglib 的动态代理
UserService.register

三、总结

  1. JDK 动态代理: Proxy.newInstance() 通过接口创建代理的实现类;
  2. Cglib 动态代理:Enhancer 通过继承父类创建的代理类;
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-09-09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 一、JDK 动态代理
  • 二、CGlib 动态代理
  • 三、总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档