jdk动态代理是如何实现的,java的动态代理机制详解

  jdk动态代理是如何实现的,java的动态代理机制详解

  

目录

概念案例静态代理爪哇岛开发工具包动态代理模式原理分析真相大白

 

  

概念

代理:为控制英语字母表中第一个字母对象,而创建出新仓库对象,由仓库对象代替执行英语字母表中第一个字母对象所有操作,称之为代理。一个代理体系建立涉及到3个参与角色:真实对象(一),代理对象(二),客户端。

 

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

  

案例

需求:员工业务层接口调用救援需要管理权限,调用目录不需要权限,没权限调用时抛出异常提示。

 

  

静态代理

/** * 代理接口*/公共接口IEmployeeService { void save();void list();}/** * 真实对象*/公共类雇员服务Impl实现iemployeservice { @ Override public void save(){ system。出去。println( EmployeeServiceImpl-正常的保存.);} @覆盖公共void list(){ system。出去。println( EmployeeServiceImpl-正常的列表.);}}/** * 模拟当前登录用户对象*/public类会话保持器{ private static String currentUser;公共静态字符串getCurrentUser(){ return currentUser;} public static void setCurrentUser(String currentUser){ session holder。currentUser=currentUser}}/** * 代理对象*/公共类雇员代理实现IEmployeeService { //真实对象私有EmployeeServiceImpl雇员服务;公共雇员代理(EmployeeServiceImpl雇员服务){ this。员工服务=员工服务;} @覆盖公共void save() { //权限判断if(admin ).等于(会话持有者。getcurrentuser()){ employeeservice。save();}else{ throw new RuntimeException(当前非管理用户,不能执行救援操作);} } @ Override public void list(){员工服务。list();} }公共类App { public static void main(String[]args){ system。出去。println(-真实对象- );EmployeeServiceImpl employeeService=new EmployeeServiceImpl();员工服务。list();员工服务。save();System.out.println( -代理对象- );SessionHolder.setCurrentUser(菲达);//设置权限(当前登录用户)员工代理员工代理=新员工代理(员工服务);员工代理。list();雇员代理

 

  .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

  

/** * jdk动态代理控制类,由它牵头代理类获取,代理方法的执行 */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动态代理涉及到的类

  

 

  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");

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

 

  

 

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

  $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的特点:

 

  1>继承了Proxy类,实现了IEmployeeService 接口2>通过静态块的方式反射IEmployeeService接口save与list方法,得到他们的方法对象Method3>调用父类构造器,需要传入InvocationHandler 参数4>重写IEmployeeService接口的save list方法靠的是父类Proxy的h属性.invoke方法

 

  

真相大白

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

 

  

 

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

  

 

  到这,JDK动态代理就ok了。

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

相关文章阅读

  • ubuntu18.04安装jdk8,ubuntu中安装jdk
  • ubuntu18.04安装jdk8,ubuntu中安装jdk,Ubuntu 安装 JDK8 的两种方法(总结)
  • JDK1.8安装教程,安装配置jdk1.8
  • JDK1.8安装教程,安装配置jdk1.8,2020JDK1.8安装教程详解(一次就可安装成功)
  • ,,jdk8使用stream实现两个list集合合并成一个(对象属性的合并)
  • ,,IntelliJ IDEA之配置JDK的4种方式(小结)
  • java代理模式详解,java代理模式的典型例子,java代理模式(jdk proxy)
  • java中spi有什么作用,jdk的spi机制
  • java中spi有什么作用,jdk的spi机制,一文搞懂Java的SPI机制(推荐)
  • ,,详解JDK自带javap命令反编译class文件和Jad反编译class文件(推荐使用jad)
  • ,,JAVA帮助文档全系列 JDK1.5 JDK1.6 JDK1.7 官方中英完整版整理
  • ,,JAVA JDK8 List分组的实现和用法
  • idea配置tomcat和jdk,idea配置本地gradle
  • 简单叙述一下jdk环境变量的配置,jdk环境变量配置是干什么的
  • 建立Java开发环境,安装JDK,一般需要设置环境变量,在安装完JDK后,需要在环境变量中配置
  • 留言与评论(共有 条评论)
       
    验证码: