这些天看了一下spring的动态代理,写点笔记记录一下,帮助自己理清思路,同时也希望能帮助到大家。理解不是很清楚,有什么错误,欢迎指正。
spring 动态代理用到的地方很多,常见的AOP等。spring的动态代理实现由两种方式,JDK和CGLIB。JDK方式需要被代理的类是某个接口的实现,且只能代理该接口中的方法。CGLIB方式没有这个限制但是CGLIB是通过集成的方式的来实现方法的增强的,所以需要被代理的类与方法不是final修饰的。
先写一个JDK动态代理的例子
定义一个A接口
public interface A { public void test(); }
A接口的实现
public class AImpl implements A { @Override public void test() { System.out.println("A impl"); } }
后切接口
public interface AfterAdvice {
public void after();
}
前切接口
public interface BeforeAdvice {
public void before();
}
代理类
@Data
public class ProxyFactory {
private Object targetObject;
private BeforeAdvice beforeAdvice;
private AfterAdvice afterAdvice;
public Object createProxy(){
ClassLoader classLoader = this.getClass().getClassLoader();
Class<?>[] interfaces = targetObject.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if(beforeAdvice !=null){
beforeAdvice.before();
}
Object invoke = method.invoke(targetObject, args);
if(afterAdvice !=null){
afterAdvice.after();
}
return invoke;
}
};
return Proxy.newProxyInstance(classLoader,interfaces,invocationHandler);
}
}
测试代码
public static void main(String[] args) throws Exception {
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTargetObject(new AImpl());
proxyFactory.setBeforeAdvice(new BeforeAdvice() {
@Override
public void before() {
System.out.println("before....");
}
});
proxyFactory.setAfterAdvice(new AfterAdvice() {
@Override
public void after() {
System.out.println("after...");
}
});
A proxy = (A)proxyFactory.createProxy();
proxy.test();
}
运行输出
before....
A impl
after...
Process finished with exit code 0
从上面可以看到一个简单的基于JDK动态代理的代码增强功能就是实现了。
感觉上面JDK动态代理实现很简单。下面类似梳理一下原代码。可以看到整个程序的核心代码就一句Proxy.newProxyInstance(classLoader,interfaces,invocationHandler); 当我们调用proxy.test()的时候实际上执行的是invocationHandler的invoke()方法的。所以主要目的就是找到我们的test()是如何被invoke()代理的。
下面走一走源码。
在Proxy.newProxyInstance()方法中重要的方法入如图一标注的两个方法。
getProxyClass0()获取代理classd对象是重中之重。
proxyClassCache的初始化代码如下,
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
在getProxyClass0方法中只有一句代码,proxyClassCache.get(loader, interfaces);
再看一下proxyClassCache.get()方法,key是我们调用Proxy.newProxyInstance()时传入的classLoader,paramter是interfaces。在构造Factory时也传入到了Factory实例中。subkeyFactory是proxyClassCache初始化时传入的第一个参数即KeyFactory。
然后Factory的get方法。Factory是WeakCache的内部类。在get方法中重要的方法就是valueFactory.apply()。valueFactory是proxyClassCache初始化的时候传入的第二个参数即ProxyClassFactory。所以接下来就要看ProxyClassFactory的apply方法了。
在apply方法中关键代码,如下,generateProxyClasss生成class字节码,defineClass0是一个本地方法,将字节码生成对应的class对象。这两个方法是重头戏。但是可能需要对Class文件结构相当了解,有点懵,没有理解得很透彻。由于defineClass0是一个本地方法没有什么好说的。再捋一捋ProxyGenerator.generateProxyClass()方法。proxyName是一个jdk动态生成的代理类名看一下源码就知道了,interfaces当然是我们传入的接口。
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
看一下generateProxyClass的实现,generateClass就生成了class字节码了。
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
final byte[] var4 = var3.generateClassFile();
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
return var4;
}
generateClassFile方法实现如下图。
再看一下generateConstructor方法,在这个方法中java/lang/reflect/Proxy,Ljava/lang/reflect/InvocationHandler;)V好像很眼熟,我们在调用Proxy.newProxyInstance的时候的Proxy就是java/lang/reflect/Proxy,同时此Proxy也有一个构造方法是传入InvocationHandler的。再看一下我们的图一,在获取了Class对象后newInstance()方法也传入了InvocationHandler参数。
private ProxyGenerator.MethodInfo generateConstructor() throws IOException {
ProxyGenerator.MethodInfo var1 = new ProxyGenerator.MethodInfo("<init>", "(Ljava/lang/reflect/InvocationHandler;)V", 1);
DataOutputStream var2 = new DataOutputStream(var1.code);
this.code_aload(0, var2);
this.code_aload(1, var2);
var2.writeByte(183);
var2.writeShort(this.cp.getMethodRef("java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V"));
var2.writeByte(177);
var1.maxStack = 10;
var1.maxLocals = 2;
var1.declaredExceptions = new short[0];
return var1;
}
generateClassFile可能是生成一个实现接口,继承了Proxy的class。接口的方法实现就就是调用InvocationHandler的invoke方法。
到底是不是这么回事呢?在idea VM options中加入-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true 再跑一次代码。
会生成一个com.sun.proxy.$Proxy0.class文件,用Idea打开看一下。如下图
是不是跟猜测的一模一样。h 当然是InvocationHandler了。
到此我们基本上把jdk代理流程捋了一次。cglib的实现方式再看看吧,感觉比jdk方式更难。