文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Java如何实现JDK动态代理

2023-07-02 14:15

关注

这篇文章主要讲解了“Java如何实现JDK动态代理”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java如何实现JDK动态代理”吧!

概念

代理:为控制A对象,而创建出新B对象,由B对象代替执行A对象所有操作,称之为代理。一个代理体系建立涉及到3个参与角色:真实对象(A),代理对象(B),客户端。

其中的代理对象(B)起到中介作用,连通真实对象(A)与客户端,如果进一步拓展,代理对象可以实现更加复杂逻辑,比如对真实对象进行访问控制。

案例

需求:员工业务层接口调用save需要admin权限,调用list不需要权限,没权限调用时抛出异常提示。

静态代理

public interface IEmployeeService {    void save();     void list();}
public class EmployeeServiceImpl implements IEmployeeService {    @Override    public void save() {        System.out.println("EmployeeServiceImpl-正常的save....");    }    @Override    public void list() {        System.out.println("EmployeeServiceImpl-正常的list....");    }}
public class SessionHolder {    private static String currentUser;    public static String  getCurrentUser(){        return currentUser;    }    public static void   setCurrentUser(String currentUser){        SessionHolder.currentUser = currentUser;    }}
public class EmployeeProxy implements IEmployeeService {    //真实对象    private EmployeeServiceImpl employeeService;    public EmployeeProxy(EmployeeServiceImpl employeeService){        this.employeeService = employeeService;    }    @Override    public void save() {        //权限判断        if("admin".equals(SessionHolder.getCurrentUser())){            employeeService.save();        }else{            throw new RuntimeException("当前非admin用户,不能执行save操作");        }    }    @Override    public void list() {        employeeService.list();    }}
public class App {    public static void main(String[] args) {        System.out.println("----------------真实对象--------------------");        EmployeeServiceImpl employeeService = new EmployeeServiceImpl();        employeeService.list();        employeeService.save();        System.out.println("----------------代理对象--------------------");        SessionHolder.setCurrentUser("dafei");  //设置权限(当前登录用户)        EmployeeProxy employeeProxy = new EmployeeProxy(employeeService);        employeeProxy.list();        employeeProxy.save();    }}
----------------真实对象--------------------EmployeeServiceImpl-正常的list....EmployeeServiceImpl-正常的save....----------------代理对象--------------------EmployeeServiceImpl-正常的list....Exception in thread "main" java.lang.RuntimeException: 当前非admin用户,不能执行save操作at com.langfeiyes.pattern.proxy.demo.EmployeeProxy.save(EmployeeProxy.java:20)at com.langfeiyes.pattern.proxy.demo.App.main(App.java:16)

使用真实对象EmployeeServiceImpl 直接调用时,不管是list 还是save都能直接访问,但不符合需求上的admin权限限制。如果使用代理对象EmployeeProxy,可以完成需求实现。

通过直接创建新类新类代理对象方式完成代理逻辑,这种方式称之为静态代理模式。

JDK动态代理模式

Java常用的动态代理模式有JDK动态代理,也有cglib动态代理,此处重点讲解JDK的动态代理

还是原来的需求,前面的IEmployeeService EmployeeServiceImpl SessionHolder 都没变,新加一个JDK代理控制器-EmployeeInvocationHandler

public class EmployeeInvocationHandler  implements InvocationHandler {    //真实对象-EmployeeServiceImpl    private Object target;    public EmployeeInvocationHandler(Object target){        this.target = target;    }    //获取jvm在内存中生成代理对象    public Object getProxy(){        return  Proxy.newProxyInstance(                target.getClass().getClassLoader(),                target.getClass().getInterfaces(),                this);    }    //代理对象控制执行方法    //参数1:代理对象    //参数2:真实对象的方法(使用方式得到方法对象)    //参数3:真实对象方法参数列表    //此处是代理对象对外暴露的可编辑的方法处理场所,代理对象每调用一个次方法,就会执行一次invoke    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        String name = method.getName();        if("save".equals(name) && !"admin".equals(SessionHolder.getCurrentUser())){            throw new RuntimeException("当前非admin用户,不能执行save操作");        }        return method.invoke(target, args);    }}

测试App类稍微改动下:

public class App {    public static void main(String[] args) {        System.out.println("----------------真实对象--------------------");        EmployeeServiceImpl employeeService = new EmployeeServiceImpl();        employeeService.list();        employeeService.save();         System.out.println("----------------代理对象--------------------");        SessionHolder.setCurrentUser("dafei");        EmployeeInvocationHandler handler =             new EmployeeInvocationHandler(employeeService);        IEmployeeService proxy = (IEmployeeService) handler.getProxy();        proxy.list();        proxy.save();     }}

上面代码一样可以实现需求,跟静态代理区别就在于少创建了代理对象。此时存在疑问点,没有创建代理对象,为啥可以实现代理类调用呢??

原理分析

先抛出结论JDK动态代理底层实现原理:使用接口实现方式,运行时,在内存中动态构建出一个类,然后编译,执行。这个类是一次性的,JVM停止,代理类就消失。

参与角色 要理解JDK动态代理原理,首先得了解JDK动态代理涉及到的类

Java如何实现JDK动态代理

InvocationHandler:真实对象方法调用处理器,内置invoke方法,其功能:为真实对象定制代理逻辑

EmployeeInvocationHandler:员工服务真实对象方法调用处理器,此类有3个用途: 1>设置真实对象

     //真实对象-EmployeeServiceImpl    private Object target;    public EmployeeInvocationHandler(Object target){        this.target = target;    }

2>定制代理方法实现逻辑

为真实对象save方法添加了权限校验逻辑

    //代理对象控制执行方法    //参数1:代理对象    //参数2:真实对象的方法(使用方式得到方法对象)    //参数3:真实对象方法参数列表    //此处是代理对象对外暴露的可编辑的方法处理场所,代理对象每调用一个次方法,就会执行一次invoke    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        String name = method.getName();        if("save".equals(name) && !"admin".equals(SessionHolder.getCurrentUser())){            throw new RuntimeException("当前非admin用户,不能执行save操作");        }        return method.invoke(target, args);    }

3>返回代理对象

方法执行完之后,返回一个名为:$ProxyX的代理类(其中的X是序号,一般默认为0),这代理类由JDK动态构建出来。

    //获取jvm在内存中生成代理对象    public Object getProxy(){        return  Proxy.newProxyInstance(                target.getClass().getClassLoader(),                target.getClass().getInterfaces(),                this);    }

Proxy:动态代理控制类,是JDK动态生成的$ProxyX类的父类,它作用如下:
1>通过调用ProxyBuilder 类builder方法构建代理对象类

private static Constructor<?> getProxyConstructor(Class<?> caller,                                                      ClassLoader loader,                                                      Class<?>... interfaces){            return proxyCache.sub(intf).computeIfAbsent(                loader,                (ld, clv) -> new ProxyBuilder(ld, clv.key()).build()            );}

2>通过newProxyInstance方法返回$ProxyX类的实例

   public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h) {    //...   }

$Proxy0:App类运行时,JDK动态构建出来的代理类,继承至Proxy类

public class App {    public static void main(String[] args) {        //System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");        System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");        System.out.println("----------------真实对象--------------------");        EmployeeServiceImpl employeeService = new EmployeeServiceImpl();        employeeService.list();        employeeService.save();        System.out.println("----------------代理对象--------------------");        SessionHolder.setCurrentUser("dafei");        EmployeeInvocationHandler handler =                      new EmployeeInvocationHandler(employeeService);        IEmployeeService proxy = (IEmployeeService) handler.getProxy();        proxy.list();        proxy.save();     }}

默认情况下JVM是不保存动态创建代理类字节码对象的,可以在main方法中配置代理参数让字节码保留

//JDK8之前System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");//JDK8之后System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

执行完之后,会在项目根目录生成代理类字节码对象。 

Java如何实现JDK动态代理

为了方便解读,将一些不需要的方法剔除之后

$Proxy0类

public class $Proxy0 extends Proxy implements IEmployeeService {    private static Method m4;    private static Method m3;    static {        try {            m4 = Class.forName("com.langfeiyes.proxy.demo.IEmployeeService")                 .getMethod("save");            m3 = Class.forName("com.langfeiyes.proxy.demo.IEmployeeService")                 .getMethod("list");        } catch (Exception e) {            e.printStackTrace();        }    }    public $Proxy0(InvocationHandler var1) throws Throwable {        super(var1);    }    public final void save() throws Throwable {        super.h.invoke(this, m4, (Object[])null);    }     public final void list() throws  Throwable{        super.h.invoke(this, m3, (Object[])null);    }}

从源码上看,$Proxy0的特点:

真相大白

下图所有参与动态代理的类:

Java如何实现JDK动态代理

 下图是上图的操作时序图,跟着走就对了

Java如何实现JDK动态代理

感谢各位的阅读,以上就是“Java如何实现JDK动态代理”的内容了,经过本文的学习后,相信大家对Java如何实现JDK动态代理这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     801人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     348人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     311人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     432人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     220人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯