一、Java中的两种动态代理方式
JDK动态代理,通过实现接口的方式完成动态代理。
Cglib动态代理,通过继承目标类或实现接口的方式完成动态代理。
二、JDK动态代理
JDK动态代理中最核心的就是Proxy类和InvocationHandler接口。
Proxy类
- 通过调用这个类中的静态方法newProxyInstance(),可以返回代理对象。
- newProxyInstance()方法需要三个参数,分别是:ClassLoader类加载、目标对象实现的所有接口、InvocationHandler实现类。
InvocationHandler接口
- 这个接口中有一个invoke方法,代理对象通过调用这个方法对目标方法进行增强。
案例
创建类实现功能
接口:
/**
* 计算器接口 有加减乘除功能
*/
public interface Calculator {
int add(int a, int b); // 加法
int sub(int a, int b); // 减法
int mul(int a, int b); // 乘法
int div(int a, int b); // 除法
}
实现类:
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
System.out.println("执行add方法,参数信息:" + a + "," + b);
int result = a + b;
System.out.println("add方法执行完毕,计算结果:" + result);
return result;
}
@Override
public int sub(int a, int b) {
System.out.println("执sub方法,参数信息:" + a + "," + b);
int result = a + b;
System.out.println("sub方法执行完毕,计算结果:" + result);
return result;
}
@Override
public int mul(int a, int b) {
System.out.println("执行mul方法,参数信息:" + a + "," + b);
int result = a + b;
System.out.println("mul方法执行完毕,计算结果:" + result);
return result;
}
@Override
public int div(int a, int b) {
System.out.println("执行div方法,参数信息:" + a + "," + b);
int result = a + b;
System.out.println("div方法执行完毕,计算结果:" + result);
return result;
}
}
改进代码
将代码提取到工具类中
public class Logging {
/**
* 方法执行前打印参数
* @param className 类名
* @param methodName 方法名
* @param args 执行方法时的参数
*/
public static void beforeMethod(String className, String methodName, Object[] args){
System.out.println("执行" + className + "类中的" + methodName + "方法,参数:" + Arrays.toString(args));
}
/**
* 方法执行后打印执行结果
* @param className 类名
* @param methodName 方法名
* @param result 方法的返回值
*/
public static void afterMethod(String className, String methodName, Object result){
System.out.println(className + "类中的" + methodName + "方法执行完毕,结果:" + result);
}
}
使用jdk动态代理的方式对功能进行增强
增强的功能:方法执行之前打印参数、方法执行之后打印结果。
public class JdkDynamicProxy {
// 目标对象,需要增强的对象
private Object target;
// 有参构造器为目标对象进行赋值
public JdkDynamicProxy(Object target) {
this.target = target;
}
// 获取代理对象的方法
public Object getProxyObject(){
// 代理对象
Object proxyObject = null;
// 获取类加载器
ClassLoader classLoader = target.getClass().getClassLoader();
// 获取目标对象实现的所有接口
Class<?>[] interfaces = target.getClass().getInterfaces();
// 调用Proxy.newProxyInstance()方法,创建代理对象
proxyObject = Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
/**
* @param proxy 代理对象
* @param method 要执行的方法
* @param args 执行方法时需要的参数
* @return 调用目标对象方法后返回的执行结果
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 目标对象类名
String targetClassName = target.getClass().getName();
// 要执行的方法名
String targetMethodName = method.getName();
// 在方法调用之前,打印方法参数
Logging.beforeMethod(targetClassName, targetMethodName, args);
// 调用目标对象的方法
Object result = method.invoke(target, args);
// 调用方法之后,输出执行结果
Logging.afterMethod(targetClassName, targetMethodName, result);
// 将执行结果返回
return result;
}
});
// 将代理对象返回
return proxyObject;
}
}
测试代码
@Test
public void test() {
// 创建目标对象,也就是需要被增强的对象
CalculatorImplJdkDynamicProxy calculator = new CalculatorImplJdkDynamicProxy();
// 创建代理对象
JdkDynamicProxy jdkDynamicProxy = new JdkDynamicProxy(calculator);
Calculator proxyObject = (Calculator) jdkDynamicProxy.getProxyObject();
// 调用方法
proxyObject.add(1, 1);
proxyObject.sub(1, 1);
proxyObject.mul(1, 1);
proxyObject.div(1, 1);
}
执行结果:
执行com.test.dynamic.proxy.demo.spring.CalculatorImplJdkDynamicProxy类中的add方法 开始,参数:[1, 1]
执行com.test.dynamic.proxy.demo.spring.CalculatorImplJdkDynamicProxy类中的add方法 完毕,结果:2
----------
执行com.test.dynamic.proxy.demo.spring.CalculatorImplJdkDynamicProxy类中的sub方法 开始,参数:[1, 1]
执行com.test.dynamic.proxy.demo.spring.CalculatorImplJdkDynamicProxy类中的sub方法 完毕,结果:0
----------
执行com.test.dynamic.proxy.demo.spring.CalculatorImplJdkDynamicProxy类中的mul方法 开始,参数:[1, 1]
执行com.test.dynamic.proxy.demo.spring.CalculatorImplJdkDynamicProxy类中的mul方法 完毕,结果:1
----------
执行com.test.dynamic.proxy.demo.spring.CalculatorImplJdkDynamicProxy类中的div方法 开始,参数:[1, 1]
执行com.test.dynamic.proxy.demo.spring.CalculatorImplJdkDynamicProxy类中的div方法 完毕,结果:1
----------
原理分析
底层其实是有创建类的,Java中必须有类才能够使用这个类的实例,JDK动态代理运行时,底层创建代理类实现目标对象的接口,并生成代理类的字节码,通过类加载器将字节码进行加载,最后再生成代理类对象实例。
查看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);
}
}
其中,通过getProxyClass0方法来获取代理类
Class<?> cl = getProxyClass0(loader, intfs);
getProxyClass0方法中有一段核心的代码:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);
这段代码就是生成代理类的字节码信息的。
我们试着调用这个方法,并获取到class字节码:
@Test
public void test01(){
// 要生成的类的名称
String className = "myClass";
// 指定class类名以及代理类要实现的接口
byte[] bytes = ProxyGenerator.generateProxyClass(className, new Class[]{Calculator.class});
FileOutputStream fos = null;
try {
// 将生成的字节信息输出到 myClass.class
fos = new FileOutputStream(new File("D:\" + className + ".class"));
fos.write(bytes);
}catch (Exception e){
System.out.println(e.getMessage());
}finally {
if(fos != null){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
用idea打开class文件:
// 继承了Proxy 并且 实现了 Calculator
public final class myClass extends Proxy implements Calculator {
private static Method m1;
private static Method m2;
private static Method m5;
private static Method m3;
private static Method m4;
private static Method m6;
private static Method m0;
// 这个构造方法的参数,就是调用Proxy.newProxyInstance方法时传入的InvocationHandler实现类
public myClass(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int mul(int var1, int var2) throws {
try {
return (Integer)super.h.invoke(this, m5, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final int add(int var1, int var2) throws {
try {
// super.h 就是调用Proxy.newProxyInstance方法时传入的InvocationHandler实现类
// 执行InvocationHandler实现类的invoke方法
// this是当前代理对象
// m3是增强的方法
// var1 var2是方法入参
return (Integer)super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final int sub(int var1, int var2) throws {
try {
return (Integer)super.h.invoke(this, m4, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final int div(int var1, int var2) throws {
try {
return (Integer)super.h.invoke(this, m6, new Object[]{var1, var2});
} catch (RuntimeException | Error var4) {
throw var4;
} catch (Throwable var5) {
throw new UndeclaredThrowableException(var5);
}
}
public final int hashCode() throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m5 = Class.forName("com.test.dynamic.proxy.demo.spring.Calculator").getMethod("mul", Integer.TYPE, Integer.TYPE);
m3 = Class.forName("com.test.dynamic.proxy.demo.spring.Calculator").getMethod("add", Integer.TYPE, Integer.TYPE);
m4 = Class.forName("com.test.dynamic.proxy.demo.spring.Calculator").getMethod("sub", Integer.TYPE, Integer.TYPE);
m6 = Class.forName("com.test.dynamic.proxy.demo.spring.Calculator").getMethod("div", Integer.TYPE, Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
三、Cglib动态代理
创建类实现功能
public class CalculatorImpl4Cglib {
public int add(int a, int b) {
return a + b;
}
public int sub(int a, int b) {
return a - b;
}
public int mul(int a, int b) {
return a * b;
}
public int div(int a, int b) {
return a / b;
}
}
使用Cglib动态代理的方式对功能进行增强
public class CglibProxy implements MethodInterceptor {
public Object newProxy(Class<?> clazz) {
// 通过CGLIB动态代理获取代理对象的过程
Enhancer enhancer = new Enhancer();
// 设置enhancer对象的父类
enhancer.setSuperclass(clazz);
// 设置enhancer的回调对象
enhancer.setCallback(new CglibProxy());
// 创建代理对象
return enhancer.create();
}
/**
* @param o 代理对象
* @param method 被代理对象的方法
* @param args 方法入参
* @param methodProxy 代理方法
* @return
* @throws Throwable
*/
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
// 代理对象类名
String proxyClassName = o.getClass().getName();
// 要执行的方法名
String targetMethodName = method.getName();
// 在方法调用之前,打印方法参数
Logging.beforeMethod(proxyClassName, targetMethodName, args);
// 调用代理类FastClass对象
Object result = methodProxy.invokeSuper(o, args);
// 调用方法之后,输出执行结果
Logging.afterMethod(proxyClassName, targetMethodName, result);
return result;
}
}
测试代码
public static void main(String[] args) {
// 代理类class文件存入本地磁盘方便我们反编译查看源码
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "./code");
// 创建目标对象
CalculatorImpl4Cglib target = new CalculatorImpl4Cglib();
// 创建代理对象
CalculatorImpl4Cglib proxyObject = (CalculatorImpl4Cglib) new CglibProxy(target).newProxy();
// 调用方法
proxyObject.add(1, 1);
proxyObject.sub(1, 1);
proxyObject.mul(1, 1);
proxyObject.div(1, 1);
}
调用流程
四、CGLIB 和 Jdk 动态代理的特点和区别
| 类型 | 机制 | 回调方式 | 适用场景 | 效率 |
|---|---|---|---|---|
| JDK动态代理 | 委托机制。代理类和目标类都实现了同样的接口。InvocationHandler持有目标类。代理类委托InvocationHandler去调用目标类原始方法 | 反射 | 目标类实现接口 | 反射调用稍慢。(jdk1.8之后性能反超cglib) |
| CGLIB动态代理 | 继承机制。代理类继承了目标类并重写了目标方法.通过回调函数MethodInterceptor调用父类方法执行原始逻辑 | 通过FastClass方法索引调用 | 非final类,非final方法 | 第一次调用因为要生成多个Class对象较]DK慢,但是调用时方法索引较反射方式快 |