Spring 框架中很多地方用到了动态代理机制,今天稍微梳理下。设计模式中的代理模式理解起来很简单,即访问目标对象属性或方法前先经过代理类。而动态代理主要是为了减少手工重复编码,通过JVM机制,当目标对象发生变化时候,自动添加代理方法。
Java 实现动态代理有两种方法
使用JDK实现动态代理的流程如下:
public interface TestService {
public void select();
}
2. 实现目标接口
public class TestServiceImpl implements TestService{
@Override
public void select() {
System.out.println("select");
}
}
3. 实现Java InvocationHandler
代理接口,现实代理类的功能逻辑, 比如打印日志,参数校验等。
public class ProxyHandler implements InvocationHandler {
Object target;
public ProxyHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("proxy begin");
Object result = method.invoke(target, args);
System.out.println("proxy end");
return result;
}
}
4. 调用Proxy.newProxyInstance()
生成代理类实例。
public static void main(String[] args) {
TestServiceImpl userService = new TestServiceImpl();
ClassLoader classLoader = userService.getClass().getClassLoader();
Class<?>[] interfaces = userService.getClass().getInterfaces();
ProxyHandler handler = new ProxyHandler(userService);
TestService p = (TestService)Proxy.newProxyInstance(classLoader, interfaces, handler);
p.select();
}
// 输出
//proxy begin
//select
//proxy end
Proxy.newProxyInstance() 方法调用链
getProxyClass0 => ProxyClassFactory => ProxyGenerator.generateProxyClass => defineClass0
=>Constructor.newInstance
getProxyClass0
会查找缓存 ProxyClassFactory
对象, 没有则生成 ProxyClassFactory
对象并缓存ProxyClassFactory
使用 ProxyGenerator.generateProxyClass() 生成代理类字节流。// Proxy.java 641
// 生成代理类字节流
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
// 返回动态代理类Class对象
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
...
3. defineClass0 返回的Class对象只有代理方法架子 , 因为generateProxyClass的参数包含方法名和加载类信息,所以完全可用通过反射机制在方法外部套一个同名接口,内部则调用代理函数invoke方法。
// 伪代码
class $ProxyTestService implements TestService {
InvocationHandler proxy = null;
// 生成架子后,后面使用Instance来调用它的构造函数
$ProxyTestService(InvocationHandler handle){
this.proxy = handle;
}
@Override
public void select() {
// 伪代码
proxy.invoke(TestService, Methon.select, args);
}
}
4.使用newInstance方法 ,新建对象并设置代理类。
// Proxy.java 718 删减
Class<?> cl = getProxyClass0(loader, intfs);
// 获取构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
// h为InvocationHandler实现类
return cons.newInstance(new Object[]{h});
第三步中 defineClass0 是一个JNI函数,翻了下OpenJDK源码 defineClass0, 有兴趣深入的可用瞧瞧。
顺便安利个OpenJDK源码浏览工具网站:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。