一直想了解一下 Spring AOP 的实现,所以看了下 java 动态代理(jdk 8)。
java Proxy 实现动态代理
Proxy 原理是在运行期创建指定的被代理接口的一个实现类,这个类是 Proxy 的子类,并使用反射机制,对接口中声明的方法,用 InvocationHandler 转发函数调用。
Proxy 只能代理接口的实现,会被转发到从其原理不难看出,这是因为生成的代理类已经继承了 Proxy,因为 java 单继承的机制,所以只能实现接口作为其代理。
假设有接口 Target:
1 2 3 4 5
| public interface Target {
void fun(String s); }
|
及其实现 RealTarget:
1 2 3 4 5 6 7 8 9 10 11
| public class RealTarget implements Target {
@Override public void fun(String s) { System.out.println(this.getClass().getSimpleName() + " fun " + s); }
public void fun2(String s) { System.out.println(this.getClass().getSimpleName() + " fun2 " + s); } }
|
要使用 Proxy 对 RealTarget 动态代理,首先,创建一个类实现 InvocationHandler,作为方法调用的处理器:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public class InvokeHandler implements InvocationHandler {
private Object proxiedObj;
public InvokeHandler(Object proxiedObj) { this.proxiedObj = proxiedObj; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(this.getClass().getSimpleName() + " invoke " + Arrays.deepToString(args)); return method.invoke(proxiedObj, args); } }
|
然后可使用以下方式创建和使用代理:
1 2 3 4 5 6 7 8 9 10 11
| public static void main(String[] args) { Target t = new RealTarget(); Target proxy = (Target) Proxy.newProxyInstance( Target.class.getClassLoader(), new Class[]{Target.class}, new InvokeHandler(t)); proxy.fun("ma"); System.out.println(proxy.getClass()); System.out.println(Proxy.class.isAssignableFrom(proxy.getClass())); System.out.println(proxy instanceof RealTarget); }
|
以上代码的输出为:
1 2 3 4 5
| InvokeHandler invoke [ma] RealTarget fun ma class com.sun.proxy.$Proxy0 true false
|
另,Proxy 会把从 Object 中继承的非 final 方法,包括 hashCode()、equals()、toString() 转发到 InvocationHandler,但是其他从 Object 中继承的 final 方法不会转发。
运行时创建的代理类如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64
| public final class $Proxy0 extends Proxy implements Target { private static Method m1; private static Method m3; private static Method m2; private static Method m0;
public $Proxy0(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 void fun(String var1) throws { try { super.h.invoke(this, m3, 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 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")); m3 = Class.forName("Target").getMethod("fun", Class.forName("java.lang.String")); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
|
了解了其原理,不难理解这种情况:假设 fun() 和 fun2() 都是实现的接口中的方法,如果在 fun() 的实现中调用了 fun2(),因为实际是用 this 即被代理类引用来调用的,所以这一 fun2() 的调用不会代理。
Spring CGLIB 实现动态代理
以上对 jdk Proxy 的了解可以知道,Proxy 只能代理接口的实现,想对别的类动态代理,可以用 CGLIB 实现。这里我们使用spring-core包内置的 CGLIB 实现进行实例。
CGLIB 的原理是动态创建一个被代理类的子类,覆盖其方法,实现代理。可以实现 MethodInterceptor 接口,作用与 jdk Proxy 的 InvocationHandler 类似。如果不为代理设置 MethodInterceptor,将默认调用代理类的父类(被代理类)的方法。
显然,CGLIB 对于 final 类无法代理;类中的 final 方法无法代理。
而因为 CGLIB 是通过继承被代理类来实现动态代理的,如果在 MethodInterceptor 中使用代理对象调用方法,由于多态,那么在被代理类内部的方法的相互调用,也是经过代理对象来调用的,都会被代理。
CGLIB 的使用可见下例。
首先假设有被代理类 RealSubject:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| package com.tianma.resTest;
public class RealSubject {
public void fun(String s) { System.out.println(this.getClass().getSimpleName() + " fun " + s); fun2(); }
public void fun2() { System.out.println(this.getClass().getSimpleName() + " fun2"); }
public final void fun3(String s) { System.out.println(this.getClass().getSimpleName() + " fun3 " + s); } }
|
定义 MethodInterceptor 的实现,其中,第一个参数 o,是代理对象的实例,methodProxy.invokeSuper(o, objects),是使用代理对象调用了父类的方法。
1 2 3 4 5 6 7 8 9 10
| public class MyMethodInterceptor implements MethodInterceptor {
@Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println(o.getClass().getSimpleName()); System.out.println(method.toString()); System.out.println(methodProxy.getSignature().toString()); return methodProxy.invokeSuper(o, objects); } }
|
然后可以通过以下方式来创建和使用代理:
1 2 3 4 5 6 7 8 9 10 11 12
| Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(RealSubject.class); enhancer.setCallback(new MyMethodInterceptor());
RealSubject subject = (RealSubject) enhancer.create(); System.out.println("----------------------");
subject.fun("ma"); System.out.println("---------"); subject.fun2(); System.out.println("---------"); subject.fun3("tian");
|
其输出为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| ---------------------- RealSubject$$EnhancerByCGLIB$$9e951628 public void com.tianma.resTest.RealSubject.fun(java.lang.String) fun(Ljava/lang/String;)V RealSubject$$EnhancerByCGLIB$$9e951628 fun ma RealSubject$$EnhancerByCGLIB$$9e951628 public void com.tianma.resTest.RealSubject.fun2() fun2()V RealSubject$$EnhancerByCGLIB$$9e951628 fun2 --------- RealSubject$$EnhancerByCGLIB$$9e951628 public void com.tianma.resTest.RealSubject.fun2() fun2()V RealSubject$$EnhancerByCGLIB$$9e951628 fun2 --------- RealSubject$$EnhancerByCGLIB$$9e951628 fun3 tian
|
使用 java HSDB 工具可以查看 CGLIB 运行时生成的字节码,可以看下 fun() 部分的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| public class RealSubject$$EnhancerByCGLIB$$9e951628 extends RealSubject implements Factory {
...... public final void fun(String var1) { MethodInterceptor var10000 = this.CGLIB$CALLBACK_0; if (var10000 == null) { CGLIB$BIND_CALLBACKS(this); var10000 = this.CGLIB$CALLBACK_0; }
if (var10000 != null) { var10000.intercept(this, CGLIB$fun$0$Method, new Object[]{var1}, CGLIB$fun$0$Proxy); } else { super.fun(var1); } } ......
}
|
可见,被代理类中的可以被 override 的方法(非 private、非 final、非 static),被动态创建的子类覆盖,我们可以实现 MethodInterceptor 接口对调用进行自定义处理,如果不设置 MethodInterceptor,将默认调用代理类的父类(被代理类)的方法。
同时,从被反编译的代码中可以看到,Object 类中可 override 的方法:hashCode()、toString()、equals()、clone() 也可以代理。
java 查看运行时动态创建的 class
使用 java 的 HSDB 工具
简记下查看动态生成类的方式:
- 启动项目,使用断点等使不要退出
- cmd 执行 java -classpath “%JAVA_HOME%/lib/sa-jdi.jar” sun.jvm.hotspot.HSDB,启动 HSDB
- cmd 执行 jps,查看要调试的项目的 id
- HSDB 中 attach 到此 id 的项目,查看类,搜索要查看的类的类名即可搜到
- create,然后 class 会被保存到执行 2 中命令的目录中
HSDB 工具还可以查看运行时 Thread 等信息。
使用 ProxyGenerator 生成和保存 java Proxy 动态生成的 class 文件
使用下面代码,我们可以用 ProxyGenerator 生成 class 文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", RealTarget.class.getInterfaces());
FileOutputStream out = null;
try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } }
|