目录
JDK动态代理的执行过程分析
JDK动态代理和CGLib动态代理的区别
前提:创建一个目标类和测试类进行动态代理执行过程的分析
目标类:CalculatorService
package com.jd.calculator;
public class CalculatorService implements ICalculatorService {
@Override
public int add(int a, int b) {
int result = a+b;
return result;
}
@Override
public int sub(int a, int b) {
int result = a-b;
return result;
}
@Override
public int mul(int a, int b) {
int result = a*b;
return result;
}
@Override
public 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.Constructor;
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() {//h为匿名内部类对象
@Override
public 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() { //产生一个动态class类,返回一个动态代理类的对象
return Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {ICalculatorService.class}, h);
}
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);//执行动态类中的add方法,并返回int值
System.out.println("-->"+result);
}
}
结果:
com.sun.proxy.$Proxy0
com.sun.proxy.$Proxy0
com.jd.calculator.ICalculatorService
com.jd.test.Test$1:The add method begins.
com.jd.test.Test$1:Parameters of the add method: [1,1]
com.jd.test.Test$1:Result of the add method:2
com.jd.test.Test$1:The add method ends.
-->2
执行过程的代码分析:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
第一步:通过上行代码生成使用JDK创建动态代理对象的class文件(即$Proxy0.Class),并通过反编译器产生其.java文件
Test test = new Test(new CalculatorService());
第二步:为上面声明的calculatorService赋值
ICalculatorService calculatorService = (ICalculatorService) test.get();
第三步:执行Test中的get()方法,产生一个动态类,并且返回一个动态类的实例对象
分析具体执行过程:
①找到get()方法,进行简单剖析
public Object get() { return Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {ICalculatorService.class}, h); }
Proxy:动态代理类的父类 newProxyInstance:动态代理类中的方法
newProxyInstance方法有三个参数:
loader: 用哪个类加载器去加载代理对象——>此例子用Test类加载器
interfaces:动态代理类需要实现的接口——>实现的接口是ICalculatorService
h:动态代理方法在执行时,会调用h里面的invoke方法去执行——>实际执行的是目标方法
②进入到newProxyInstance()方法中,进行详细的分析
@CallerSensitive public 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. */ (1) Class> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { if (sm != null) { checkNewProxyPermission(Reflection.getCallerClass(), cl); } (2) final Constructor> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction
() { public Void run() { cons.setAccessible(true); return null; } }); } (3) 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); } } 如代码中表示的序号分析主要程序:
(1) Class> cl = getProxyClass0(loader, intfs); ——>生成指定接口ICalculatorService的Class对象的代理类
(2) final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
——>获取代理类的构造函数对象,
参数constructorParames为传入的参数,即为常量值:
private static final Class>[] constructorParams = { InvocationHandler.class };
(3) return cons.newInstance(new Object[]{h}); ——>根据代理类的构造函数对象来创建代理类对象并返回,
而且调用其中的有参构造方法,参数为h。
注意:h为传入的InvocationHandler匿名内部类对象
分析:
1)首先,h为 final InvocationHandler ih = h; 中的h;
2)然后,h为 newProxyInstance(ClassLoader loader, Class>[] interfaces InvocationHandler h) 中的h;
3)然后,h为Proxy.newProxyInstance(Test.class.getClassLoader(), new Class[] {ICalculatorService.class}, h); 中的h;
4)最后,h为传入的匿名内部类对象。
System.out.println(calculatorService.getClass().getName());
第四步:验证得到的代理对象calculatorService和invoke()中传匿名内部类中传入的proxy,是相同的
int result = calculatorService.add(1, 1);
第五步:执行动态类对象中的add方法,并返回int值(calculatorService为动态代理对象)
分析具体执行过程:
通过Java反编译器,可以得到编译后的java代码表示的java文件(即动态代理类),下面是为了容易理解,删去部分代码后得到的:
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) package com.sun.proxy; import com.jd.calculator.ICalculatorService; import java.lang.reflect.*; public final class $Proxy0 extends Proxy implements ICalculatorService //继承了Proxy类和实现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 {//对变量进行一些初始化工作 m3 = Class.forName("com.jd.calculator.ICalculatorService").getMethod("add", new Class[] { Integer.TYPE, Integer.TYPE }); } } }
①下式为动态类的构造函数,其参数正是InvocationHandler实例,Proxy.newProxyInstance()方法就是通过这个构造函数来创建代理实例。
public $Proxy0(InvocationHandler invocationhandler) { super(invocationhandler); }
②点击进入super,可以知道第三步中返回的代理类对象中执行的有参构造方法,就是这个代理类中的有参构造方法;这个构造方法为父类Proxy中的h赋值,得到了一个代理对象;再次验证Proxy类中传入的参数就是匿名内部类对象h。
protected InvocationHandler h; /** * Prohibits instantiation. */ private Proxy() { } /** * Constructs a new {@code Proxy} instance from a subclass * (typically, a dynamic proxy class) with the specified value * for its invocation handler. * * @param h the invocation handler for this proxy instance * * @throws NullPointerException if the given invocation handler, {@code h}, * is {@code null}. */ protected Proxy(InvocationHandler h) { Objects.requireNonNull(h); this.h = h; }
③此代理类$Proxy0继承了Proxy类和实现ICalculatorService接口,可知与CalculatorService类实现的是同一接口,即可以说是代理类与目标类是兄弟同级关系;且通过第三步赋值知道calculatorService是动态代理对象。
④分析执行代理类中的add()方法
(1)add()方法中
return (Integer)super.h.invoke(this, m3, new Object[] {Integer.valueOf(i), Integer.valueOf(j)
由于上面调用有参构造方法$Proxy0(InvocationHandler invocationhandler)
给这个类的父类Proxy中invocationHandler赋的值为invocationHandler对象,所以这里super.h是invocationHandler,
super.h.invoke相当于invocationHandler.invoke,也就是调用Test.java匿名内部类invocationHandler中的invoke()方法。
(2)下面对传入的参数进行解释:
this:就是调用invoke方法的对象,calculatorService的值,也就是代理对象;
m3:就是method,也就是代码最下面静态代码块中加载的ICalculatorService接口中的add方法;
传入的常量值i,j : 在这里是先将二者转换成了Integer类型;
⑤分析add()匿名内()方法中涉及执行的invoke()方法
InvocationHandler h = new InvocationHandler() { @Override public 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; } };
分析:
invoke三个参数:
proxy:就是代理对象,newProxyInstance方法的返回对象
method:接口中定义的抽象方法
args: 抽象方法需要传入的参数列表
①method.invoke(calculatorService, args);
1)method:指的是ICalculatorService接口中的那个抽象add()方法;
2)calculatorService:调用该方法时传入的是该接口实现类创建的对象
3)该行代码等效于
ICalculatorService calculatorService = new CalculatorService(); calculatorService.add(1, 1);
4)即编译时,调用接口中方法;运行时,调用接口实现类中的方法;将得到的结果返回给result;
②匿名内部类的invoke方法执行结束,代理类中的super.h.invoke(this, m3, new Object[] {Integer.valueOf(i), Integer.valueOf(j);这部分代码执行结束。
③由于匿名内部类的invoke方法是Object类,所以要将其转换成Integer类,再调用intValue(),将其转换成int类型。
④至此代理类中的add()方法结束,并将结果返回给Test中main方法第五行里面的result。
第六步:输出得到的结果。
1、动态代理:
在程序运行期间JVM根据需要通过反射等机制动态地创建代理类及其代理对象。
注意:
(1)代理类是在程序运行期间创建,可以在classpath目录(在Eclipse中,Java工程中的bin目录;Web工程中的build目录)中看到该文件,
(2)代理类和委托类的关系是在程序运行时确定的。
2、动态代理优点:
(1)静态代理在程序执行前需手动创建代理类,如果需要很多代理类,每一个都手动创建不仅浪费时间,而且可能产生大量重复性代码,此时我们就可以采用动态代理。
(2)动态代理通过InvocationHandler接口invoke方法或MethodInterceptor接口intercept方法为被代理对象中的方法增加额外功能,这种方式比静态代理中通过代理类逐一为被代理对象中的方法增加额外功能,更加的灵活。
3、JDK动态代理:程序执行时使用java.lang.reflect包中Proxy类与InvocationHandler接口动态地生成一个实现代理接口的匿名代理类及其对象,无论调用代理对象哪个方法,最终都会执行invoke方法。
4、CGLib动态代理:程序执行时通过ASM(开源的Java字节码编辑库,操作字节码)jar包动态地为被代理类生成一个代理子类,通过该代理子类创建代理对象,由于存在继承关系,所以父类不能使用final修饰。
5、JDK动态代理与CGLib动态代理区别:
(1)JDK动态代理基于接口实现,所以实现JDK动态代理,必须先定义接口;CGLib动态代理基于类实现;
(2)JDK动态代理机制是委托机制,委托hanlder调用原始实现类方法;CGLib则使用继承机制,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。