JDK动态代理执行过程分析


文章目录

一、为什么使用动态代理

二、JDK动态代理执行过程分析

三、CGLib动态代理与JDK的区别


一、为什么使用动态代理

类中代码可以通过代理模式进行优化,Java中代理模式分为静态代理和动态代理,动态代理又分为JDK动态代理和CGLib动态代理。


二、JDK动态代理执行过程分析

CalculatorService中的代码

package com.jd.calculator;public class CalculatorService implements ICalculatorService {@Overridepublic int add(int a, int b) {int result = a+b;return result;}@Overridepublic int sub(int a, int b) {int result = a-b;return result;}@Overridepublic int mul(int a, int b) {int result = a*b;return result;}@Overridepublic int div(int a, int b) {int result = a/b;return result;}
}

ICalculatorService中的代码

package com.jd.calculator;public interface ICalculatorService {int add(int a,int b);int sub(int a,int b);int mul(int a,int b);int div(int a,int b);
}

Test中的代码

package com.jd.test;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;import com.jd.calculator.CalculatorService;
import com.jd.calculator.ICalculatorService;public class Test {//动态(程序运行时实现和目标类相同接口的java类)代理()CalculatorService calculatorService;public Test(CalculatorService calculatorService) {this.calculatorService = calculatorService;}InvocationHandler h = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(proxy.getClass().getName());System.out.println(method.getDeclaringClass().getName());String name = method.getName();System.out.println(this.getClass().getName()+":The "+name+" method begins.");System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");Object result = method.invoke(calculatorService, args);//目标方法System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);System.out.println(this.getClass().getName()+":The "+name+" method ends.");return result;}};public Object get() {return Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {ICalculatorService.class}, h);//产生一个动态class类,}public static void main(String[] args) {System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");Test test = new Test(new CalculatorService());ICalculatorService calculatorService = (ICalculatorService) test.get();//获取代理对象System.out.println(calculatorService.getClass().getName());int result = calculatorService.add(1, 1);System.out.println("-->"+result);}
}

代码分析:

执行Test类中main方法中的第一行代码

System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

由于生成的代码无法生成class文件,这样就不便于我们进行代码的分析,因此此行代码起到的作用就是打印生成的动态类对象,方便我们观察分析,生成的动态类对象代码,在这里进行了适当的删减,保留了与add方法相关的代码方便我们直观的分析

执行Test类中main方法中的第二行代码

 Test test = new Test(new CalculatorService());

该行代码通过调用构造方法的方式,给Test中的CalculatorService赋值,即通过如下代码进行赋值

CalculatorService calculatorService;public Test(CalculatorService calculatorService) {this.calculatorService = calculatorService;
}

执行Test类中main方法中的第三行代码

 ICalculatorService calculatorService = (ICalculatorService) test.get();//获取代理对象

该行码实现的是获取代理对象,当执行该行代码时,执行流达到如下代码,该get方法会产生一个动态代理对象,那如何产生的呢?

public Object get() {return Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {ICalculatorService.class}, h);//产生一个动态class类,
}

按住Ctrl键,鼠标点击newProxyInstance,进入后台代码,首先看到如下代码

    @CallerSensitivepublic static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}

分析以上代码

Class<?> cl = getProxyClass0(loader, intfs);

该行代码作用为:生成指定接口ICalculatorService的class对象的代理类,即proxy class

  final Constructor<?> cons = cl.getConstructor(constructorParams);

该行代码作用为:获取代理类cl的构造方法,constructorParams是传入的参数,按住Ctrl,点击constructorParams,出现如下代码

/** parameter types of a proxy class constructor */
private static final Class<?>[] constructorParams =
{ InvocationHandler.class };

由以上代码可知传入的参数为InvocationHandler接口,按住Ctrl,点击InvocationHandler,可验证它为接口

public interface InvocationHandler {/*** Processes a method invocation on a proxy instance and returns* the result.  This method will be invoked on an invocation handler* when a method is invoked on a proxy instance that it is* associated with.** @param   proxy the proxy instance that the method was invoked on** @param   method the {@code Method} instance corresponding to* the interface method invoked on the proxy instance.  The declaring* class of the {@code Method} object will be the interface that* the method was declared in, which may be a superinterface of the* proxy interface that the proxy class inherits the method through.** @param   args an array of objects containing the values of the* arguments passed in the method invocation on the proxy instance,* or {@code null} if interface method takes no arguments.* Arguments of primitive types are wrapped in instances of the* appropriate primitive wrapper class, such as* {@code java.lang.Integer} or {@code java.lang.Boolean}.** @return  the value to return from the method invocation on the* proxy instance.  If the declared return type of the interface* method is a primitive type, then the value returned by* this method must be an instance of the corresponding primitive* wrapper class; otherwise, it must be a type assignable to the* declared return type.  If the value returned by this method is* {@code null} and the interface method's return type is* primitive, then a {@code NullPointerException} will be* thrown by the method invocation on the proxy instance.  If the* value returned by this method is otherwise not compatible with* the interface method's declared return type as described above,* a {@code ClassCastException} will be thrown by the method* invocation on the proxy instance.** @throws  Throwable the exception to throw from the method* invocation on the proxy instance.  The exception's type must be* assignable either to any of the exception types declared in the* {@code throws} clause of the interface method or to the* unchecked exception types {@code java.lang.RuntimeException}* or {@code java.lang.Error}.  If a checked exception is* thrown by this method that is not assignable to any of the* exception types declared in the {@code throws} clause of* the interface method, then an* {@link UndeclaredThrowableException} containing the* exception that was thrown by this method will be thrown by the* method invocation on the proxy instance.** @see     UndeclaredThrowableException*/public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}
 return cons.newInstance(new Object[]{h});

该行代码作用为: 创建一个代理类对象并返回,而且调用其中的有参构造方法,参数为h,h为传入的InvocationHandler匿名内部类对象,此时代码跳到代理类对象中

生成的动态类对象$Proxy0中的代码package com.sun.proxy;import com.jd.calculator.ICalculatorService;
import java.lang.reflect.*;public final class $Proxy0 extends Proxyimplements ICalculatorService
{public $Proxy0(InvocationHandler invocationhandler){super(invocationhandler);}public final int add(int i, int j){try{return ((Integer)super.h.invoke(this, m3, new Object[] {Integer.valueOf(i), Integer.valueOf(j)})).intValue();}catch(Error _ex) { }catch(Throwable throwable){throw new UndeclaredThrowableException(throwable);}}private static Method m3;static {try{m3 = Class.forName("com.jd.calculator.ICalculatorService").getMethod("add", new Class[] {Integer.TYPE, Integer.TYPE});}catch(NoSuchMethodException nosuchmethodexception){throw new NoSuchMethodError(nosuchmethodexception.getMessage());}catch(ClassNotFoundException classnotfoundexception){throw new NoClassDefFoundError(classnotfoundexception.getMessage());}}
}

调用以上动态代理类对象中的构造方法,把匿名内部类的值h赋值为父类的h,即

public $Proxy0(InvocationHandler invocationhandler){super(invocationhandler);}

按住Ctrl,鼠标点击super即可看到,该代码将

protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h = h;}

该代码的作用是为了使全局变量h的值也为匿名内部类,便于在add方法中调用

此时执行到Test类中main方法中的第5行代码,

int result = calculatorService.add(1, 1);

calculatorService的值是代理对象,所以执行代理类中的add方法,即

 public final int add(int i, int j){try{return ((Integer)super.h.invoke(this, m3, new Object[] {Integer.valueOf(i), Integer.valueOf(j)})).intValue();}catch(Error _ex) { }catch(Throwable throwable){throw new UndeclaredThrowableException(throwable);}}

此时执行流会到达,Test类中的匿名内部类

InvocationHandler h = new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(proxy.getClass().getName());System.out.println(method.getDeclaringClass().getName());String name = method.getName();System.out.println(this.getClass().getName()+":The "+name+" method begins.");System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");Object result = method.invoke(calculatorService, args);//目标方法System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);System.out.println(this.getClass().getName()+":The "+name+" method ends.");return result;}}; 

执行到如下代码时,method与proxy都继承自ICalculatorService,因此输出是相同的

System.out.println(proxy.getClass().getName());
System.out.println(method.getDeclaringClass().getName());

执行到如下代码时,动态代理对象进行处理:

String name = method.getName();
System.out.println(this.getClass().getName()+":The "+name+" method begins.");
System.out.println(this.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
System.out.println(this.getClass().getName()+":The "+name+" method ends.");
return result;

执行到如下代码时,属于接口回调,检查时是ICalculatorService接口中的add方法,运行时是CalculatorService中的add方法,即目标类中的add方,得到一个结果,继续执行接下来的代码时,将类型为Object类结果返回到动态代理对象add的方法中,由于输入的参数为整数类型,因此要进行数据类型转换,转换为整型,代码如下所示:

 return ((Integer)super.h.invoke(this, m3, new Object[] {Integer.valueOf(i), Integer.valueOf(j)})).intValue();

最后执行流到达Test类中main方法中的最后一行代码,输出结果,代码执行完

System.out.println("-->"+result);

三、CGLib动态代理与JDK的区别

CGLib动态代理
程序执行时通过ASM(开源的Java字节码编辑库,操作字节码)jar包动态地为被代理类生成一个代理子类,通过该代理子类创建代理对象,由于存在继承关系,所以父类不能使用final修饰。

JDK动态代理与CGLib动态代理区别:
1、JDK动态代理基于接口实现,所以实现JDK动态代理,必须先定义接口;CGLib动态代理基于类实现;
2、JDK动态代理机制是委托机制,委托hanlder调用原始实现类方法;CGLib则使用继承机制,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。

动态代理优点: 
1、静态代理在程序执行前需手动创建代理类,如果需要很多代理类,每一个都手动创建不仅浪费时间,而且可能产生大量重复性代码,此时我们就可以采用动态代理。
2、动态代理通过InvocationHandler接口invoke方法或MethodInterceptor接口intercept方法为被代理对象中的方法增加额外功能,这种方式比静态代理中通过代理类逐一为被代理对象中的方法增加额外功能,更加的灵活。

JDK动态代理执行过程分析相关推荐

  1. 23种设计模式——JDK动态代理(AOP)

    文章目录 01 代理 1.1 什么是代理? 1.2 为什么要找中介 02 静态代理 2.1 使用代理模式的作用 2.2 实现代理的方式 2.3 具体实现 2.4 静态代理的优缺点 03 动态代理 3. ...

  2. JDK动态代理的底层实现原理

    JavaEE的开发中,许多框架用到了动态代理机制,例如Spring的AOP编程. 这里不介绍动态代理和静态代理概念,有兴趣的朋友自行百度. Java中的动态代理一般就两种:1. JDK自带 : 2. ...

  3. (转)面试必备技能:JDK动态代理给Spring事务埋下的坑!

    一.场景分析 最近做项目遇到了一个很奇怪的问题,大致的业务场景是这样的:我们首先设定两个事务,事务parent和事务child,在Controller里边同时调用这两个方法,示例代码如下: 1.场景A ...

  4. 【spring】初识aop(面向切面编程) 使用jdk动态代理

    BankServiceIImple.java 代码实现: package com.zzxtit.aop;import java.math.BigDecimal;public interface Ban ...

  5. 【原创】分布式之缓存击穿 【原创】自己动手实现静态资源服务器 【原创】自己动手实现JDK动态代理...

    [原创]分布式之缓存击穿 什么是缓存击穿 在谈论缓存击穿之前,我们先来回忆下从缓存中加载数据的逻辑,如下图所示 因此,如果黑客每次故意查询一个在缓存内必然不存在的数据,导致每次请求都要去存储层去查询, ...

  6. 【干货】JDK动态代理的实现原理以及如何手写一个JDK动态代理

    动态代理 代理模式是设计模式中非常重要的一种类型,而设计模式又是编程中非常重要的知识点,特别是在业务系统的重构中,更是有举足轻重的地位.代理模式从类型上来说,可以分为静态代理和动态代理两种类型. 在解 ...

  7. JDK动态代理小例子

    一个小汽车,有一个跑run()的方法,我们想使用jdk动态代理使小汽车执行run之前 加点油,run之后洗车. 有四个类,接口Car(小汽车)Kayan(具体实现类(卡宴)) CarProxy(汽车的 ...

  8. jdk动态代理实例和cglib动态代理实例_CGLib 动态代理 原理解析

    JDK 动态代理实现与原理 首先来看一段CGLib代理的测试代码(MethodInterceptor的测试, 其他类型这里不做展开了). Util类的代码在后面给出的码云片段中 public 下面的输 ...

  9. java jdk动态代理学习记录

    转载自: https://www.jianshu.com/p/3616c70cb37b JDK自带的动态代理主要是指,实现了InvocationHandler接口的类,会继承一个invoke方法,通过 ...

  10. 利用JDK动态代理机制实现简单拦截器

    利用JDK动态代理机制实现简单的多层拦截器 首先JDK动态代理是基于接口实现的,所以我们先定义一个接口 public interface Executer {public Object execute ...

最新文章

  1. Coing-二叉树(bibary Tree)
  2. MapReduce—第一个WordCount程序
  3. 9.2.4 .net core 通过ViewComponent封装控件
  4. Bootstrap4+MySQL前后端综合实训-Day04-PM【PowerDesigner 图形化数据库设计软件(设置依赖关系、自动增长主键、生成sql语句)、SQLyog软件(备份数据库)】
  5. Mule ESB-3.Build a webservice proxy
  6. matlab矩阵初等变换矩阵,实验一 MATLAB基本操作及矩阵初等运算
  7. 织梦蓝色简洁大气电脑操作系统软件下载网站模板 带手机版
  8. action对象 java_struts2通过action返回json对象
  9. 对java的集合的理解_谈谈你对java集合类的理解
  10. 稀疏矩阵的创建--十字链表
  11. iText 实践的目录(the content of iText in Action)
  12. bzoj1565: [NOI2009]植物大战僵尸
  13. 用Excel求解线性规划问题
  14. 【解决】 io.lettuce.core.RedisCommandExecutionException: ERR wrong number of arguments for 'set' command
  15. 【SAP-SD】物料 X 未对销售组织 XXXX 分销渠道 X 语言 DE 定义
  16. 为什么宿醉那么缺水_坚决应对云的宿醉
  17. 金耀初教授:进化计算在人工智能领域的发展
  18. android系统开机logo定制
  19. 黄金搭档:老虎+猫头鹰+孔雀+考拉
  20. 【记录】深度学习之蒸馏法训练网络

热门文章

  1. MVC学习系列7--下拉框的联动
  2. apache tuscany(一)
  3. Jquery和JS获取ul中li标签
  4. Nagios搭建及问题详解(一)
  5. 【java面试题】equals()方法和==的比较区别?
  6. iframe操作ie,firefox兼容
  7. python discover()没有加载测试用例_Python系统学习 - Unittest
  8. 拓端tecdat|R语言逻辑回归(Logistic Regression)、回归决策树、随机森林信用卡违约分析信贷数据集
  9. 拓端tecdat|R语言非参数模型厘定保险费率:局部回归、广义相加模型GAM、样条回归
  10. 拓端tecdat|R语言对NASA元数据进行文本挖掘的主题建模分析