1.什么是动态代理
所谓代理模式,就是指给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。这种模式有什么用呢?它可以在原对象的基础上增强原对象的功能,比如在原对象调用一个方法的前后进行日志、事务操作等。Spring AOP就使用了代理模式。
而动态代理模式就是指在程序运行期间根据需要动态地创建代理类及其实例来完成具体的功能。动态代理主要分为JDK动态代理和CGlib动态代理两大类,本文主要对JDK动态代理进行探讨。
2.JDK动态代理实例
建立项目如下:
1.ICalculatorService接口源码:
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);
}
2.CalculatorService实现类源码:
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;
}
}
3.ProxyFactory动态代理类源码:
public class ProxyFactory {//动态(程序运行时实现和目标类相同接口的java类)代理()
public static Object get(CalculatorService calculatorService) {
return Proxy.newProxyInstance(calculatorService.getClass().getClassLoader(), new Class[] {ICalculatorService.class}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//通过重写InvocationHandler()类中的invoke方法,来设定动态代理类需要在被代理类的基础上添加哪些功能
System.out.println(method.getDeclaringClass().getName());
String name = method.getName();
System.out.println(calculatorService.getClass().getName()+":The "+name+" method begins.");
System.out.println(calculatorService.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
Object result = method.invoke(calculatorService, args);//被代理类的目标方法
//这里虽然calculatorService是接口的实现类,但其实是多态,呈现的是被代理类中的方法
System.out.println(calculatorService.getClass().getName()+":Result of the "+name+" method:"+result);
System.out.println(calculatorService.getClass().getName()+":The "+name+" method ends.");
return result;
}
});//产生一个动态class类,
}
4.Test测试类源码:
public static void main(String[] args) {
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//生成动态类的编译文件,以便进行进一步分析
ICalculatorService calculatorService = (ICalculatorService) new ProxyFactory().get(new CalculatorService());//获取代理对象
int result = calculatorService.add(1, 1);//调用代理对象进行测试
System.out.println("-->"+result);
}
我们由main方法一步步分析:
第一行:
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
这一行其实是生成了一个动态类的编译文件,我们可以在com.sun.proxy文件夹中找到,对其进行反编译后得到了动态类的实例化对象(由于main方法中只讨论了add方法,为方便理解,在此将实例化对象中的其他部分删去):
import com.jd.calculator.ICalculatorService;
import java.lang.reflect.*;
public final class $Proxy0 extends Proxy
implements 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());
}
}
}
第二行:(同时也是最重要的一行)
ICalculatorService calculatorService = (ICalculatorService) new ProxyFactory().get(new CalculatorService());
这一行调用了ProxyFactory()中的get方法,其实就是由Proxy的newProxyInstance方法获得了动态代理类。
接下来进入Proxy中的newProxyInstance方法进行进一步分析:
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.
*/
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);
}
}
其实这么多代码中只有这几行是对我们理解起来有帮助的:
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)
Class<?> cl = getProxyClass0(loader, intfs);
final Constructor<?> cons = cl.getConstructor(constructorParams);
return cons.newInstance(new Object[]{h});
分别为:
- 第一行告诉了我们这个方法的三个参数。
1.指定类的类加载器(这里我们其实使用了ProxyFactory的类加载器,事实上用什么无所谓)
2.代理类需要实现的接口(这里我们传入了被代理类实现的接口,这样生成的代理类和被代理类就实现了相同的接口)(注:代理类和被代理类实现的接口必须相同)
3.invocation handler,用来处理方法的调用。这里传入我们自己实现的handler,即ProxyFactory中的那个内部类
- 第二行意味着创建了一个动态代理类
- 第三行意味着得到了代理类对象的构造方法,这个构造方法的参数由constructorParams指定,而参数constructorParames为常量值:private static final Class<?>[] constructorParams = { InvocationHandler.class }
- 第四行意味着通过刚才获得的构造方法创建了一个代理类的对象,这里的参数就使用了我们自己实现的handler
调用了构造方法,就意味着使用了刚才我们的得到的代理类对象的构造方法:
public $Proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
佐证了构造方法是使用我们自己实现的handler作为参数的这一事实。
接下来回到main方法看第三行:
int result = calculatorService.add(1, 1);
这里调用了刚刚创建的代理类对象,观察相关源码:
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
});
//try后面的部分于理解意义不大,故略去
由
return ((Integer)super.h.invoke(this, m3, new Object[] {
Integer.valueOf(i), Integer.valueOf(j)
})).intValue();
知道这里其实调用了我们定义的handler的invoke方法。三个对象分别是这个实现类对象自己,m3对应的被代理类的方法以及计算所需参数。其中的Integer和.intValue()等都是数据类型转换相关内容,于理解并无影响。
因此最后可以获得结果:
3.CGlib动态代理
CGlib的动态代理方法如下:(接口与被代理类与上例相同)
public class Test {
private static Callback callback = new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
String name = method.getName();
System.out.println(obj.getClass().getName()+":The "+name+" method begins.");
System.out.println(obj.getClass().getName()+":Parameters of the "+name+" method: ["+args[0]+","+args[1]+"]");
Object result = proxy.invokeSuper(obj, args);//目标方法
System.out.println(obj.getClass().getName()+":Result of the "+name+" method:"+result);
System.out.println(obj.getClass().getName()+":The "+name+" method ends.");
return result;
}
};
private static Object get() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(CalculatorService.class);
enhancer.setCallback(callback);
return enhancer.create();//创建动态类对象
}
public static void main(String[] args) {
ICalculatorService calculatorService = (ICalculatorService)get();//获取代理对象
int result = calculatorService.add(1, 1);
System.out.println("-->"+result);
}
}
4.JDK动态代理与CGlib动态代理的区别
1.JDK动态代理基于接口实现,所以实现JDK动态代理,必须先定义接口;CGLib动态代理基于类实现;
2.JDK动态代理机制是委托机制,委托hanlder调用原始实现类方法,被代理类和代理类是兄弟关系;CGLib则使用继承机制,被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,如果被代理类有接口,那么代理类也可以赋值给接口。