动态代理
什么是动态代理
动态代理:在程序运行期间JVM根据需要通过反射等机制动态地创建代理类及其代理对象。
JDK动态代理详解
1.创建工程
创建如下java文件
各个java文件代码如下:
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);
}
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;
}
}
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 {
CalculatorService calculatorService;
public Test(CalculatorService calculatorService) {
this.calculatorService = calculatorService;
}
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
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);
//public static Object newProxyInstance(ClassLoader loader,
}
public static void main(String[] args) {
Test test = new Test(new CalculatorService());
ICalculatorService calculatorService = (ICalculatorService) test.get();//返回目标对象的代理对象 获取代理对象
//写成i c = 因为上边的new实现了ICalculatorService接口
int result = calculatorService.add(1, 1);
System.out.println("-->"+result);
}
}
2.动态代理过程
接口中一共定义了加减乘除四个方法,下面,以加法为例,说明jdk动态代理的过程
在main方法中,依次分析每一步的过程:
1. Test test = new Test(new CalculatorService());
给
CalculatorService calculatorService;
这一步的calculatorService对象赋值
2. ICalculatorService calculatorService = (ICalculatorService) test.get();
上转型对象调用get方法获取代理对象,产生动态类,产生动态对象
在get方法中,点进去newProxyInstance到Proxy.class中
其中有一行代码:
Class<?> cl = getProxyClass0(loader, intfs);
这行代码产生一个动态类,获得了动态类对应的class对象,这个cl相当于是$Proxy0所对应的class对象
然后执行
final Constructor<?> cons = cl.getConstructor(constructorParams);
从反射角度来看,这是为了获得cl类里的构造方法,具体哪个构造方法,取决于括号里的变量constructorParams
点进去constructorParams,有这样一行代码:
private static final Class<?>[] constructorParams = { InvocationHandler.class };
看到这个变量是数组类型的,数组里面是InvocationHandler,说明想获得一个constructor对象,而且这个constructor是public级别的,含有一个参数,这个参数是InvocationHandler
继续往下,找到刚才的代码
final Constructor<?> cons = cl.getConstructor(constructorParams);
此时Cons代表反编译文件$Proxy0.java里的构造方法
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
点进super,跳转到如下代码行:
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
在这里给h赋值
再往下,调用构造方法的h就是
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
中的h,也就是Test类中
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
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.");
这个h,即匿名内部类对象,所以现在生成的对象是借助这个匿名内部类来创建的对象
执行过
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
方法后,点进去super,跳转到Proxy.class中,给h赋值,
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
那么,$Proxy0.java中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);
}
}
中的super.h指的就是Proxy.class中的
protected InvocationHandler h;
即$Proxy0.java继承自Proxy.class。
然后
int result = calculatorService.add(1, 1);
开始调用。这里调用add表面调用接口中的add方法,invoke出现多态,表面上调用的是invocationhandler里的方法,实际上调用的是匿名内部类里的invoke
3.int result = calculatorService.add(1, 1);
开始调用,这里调用add方法。
表面上调用接口中的add,实际上调用的是动态对象里的add方法,即
P
r
o
x
y
0.
j
a
v
a
中
的
a
d
d
方
法
,
在
Proxy0.java中的add方法,在
Proxy0.java中的add方法,在Proxy0.java中的add方法中,super.h即匿名内部类对象调用,this指的是动态对象(Test中proxy和calculatorService是同一个对象),m3是下方的静态代码块,在类加载的时候就已经执行,m3已经代表接口中的add方法,执行权回到Test中invoke中,程序执行。
先前置增强,打印前两句话,
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);
method指定了到底调用哪个方法(接口中的add方法)但是目标类也实现了接口,这时候传的就是调用的就是目标类里的方法,执行权到达CalculatorService中的add方法,执行加法。
然后后置增强,打印后两句话
System.out.println(this.getClass().getName()+":Result of the "+name+" method:"+result);
System.out.println(this.getClass().getName()+":The "+name+" method ends.");
接着类型转换后返回结果result。
JDK和CGLib动态代理区别
1、JDK动态代理基于接口实现,所以实现JDK动态代理,必须先定义接口;CGLib动态代理基于类实现
2、JDK动态代理机制是委托机制,委托hanlder调用原始实现类方法;CGLib则使用继承机制,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。
动态代理优点:
1、静态代理在程序执行前需手动创建代理类,如果需要很多代理类,每一个都手动创建不仅浪费时间,而且可能产生大量重复性代码,此时我们就可以采用动态代理。
2、动态代理通过InvocationHandler接口invoke方法或MethodInterceptor接口intercept方法为被代理对象中的方法增加额外功能,这种方式比静态代理中通过代理类逐一为被代理对象中的方法增加额外功能,更加的灵活。