最近准备写Spring的AOP源码的,结果看了看源码发现思想逻辑设计都比较简单,本来想要从开始注入到后面的方法执行都跟下来走一遍的,但是忘了JDK动态代理和CGLIB动态代理是怎么实现的了,所以写了这篇文章来介绍回忆一下JDK动态代理与CGLIB动态代理。
//创建被代理对象和接口
public interface PersonInterface {
public void doSomething();
}
public class Person implements PersonInterface{
@Override
public void doSomething() {
System.out.println("Hello World!!!");
}
}
创建PersonInvocationHandler实现invoke()方法
public class PersonInvocationHandler implements InvocationHandler {
private Object target;
public PersonInvocationHandler(Object target) {
super();
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Do SomeThing before method");
Object result = method.invoke(target, args);
System.out.println("Do SomeThing after method");
return result;
}
}
测试类
public static void main(String[] args) {
//设置这行会在项目中生成对应的代理类,后面查看代理类使用
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//创建一个Person类
PersonInterface person = new Person();
//创建代理工具类
InvocationHandler handler = new PersonInvocationHandler(person);
//获取Person的信息,下面传参使用
ClassLoader loader = person.getClass().getClassLoader();
Class[] interfaces = person.getClass().getInterfaces();
//获取到代理类
PersonInterface personInterface = (PersonInterface) Proxy.newProxyInstance(loader, interfaces,
handler);
//执行代理类的方法
personInterface.doSomething();
}
看一下生成的代理类:
//其中h是InvocationHandler
public final class $Proxy0 extends Proxy implements PersonInterface {
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);
}
}
//可以看到,生成的代理类中调用了InvocationHandler中的invoke()执行的方法
public final void doSomething() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
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("com.jack.proxy.PersonInterface").getMethod("doSomething");
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());
}
}
}
下面再使用CGLIB动态代理Person
被代理类
public class Person {
public void doSomeThing() {
System.out.println("doSomeThing");
}
}
代理拦截器
public class PersonInterceptor implements MethodInterceptor {
@Override
public Object intercept(Object obj, Method method, Object[] arg,
MethodProxy proxy) throws Throwable {
System.out.println("Do SomeThing before method");
Object object = proxy.invokeSuper(obj, arg);
System.out.println("Do SomeThing after method");
return object;
}
}
测试代码:
public static void main(String[] args) {
//将生成的代理类输出到磁盘,后面研究
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\cglibProxyClass");
Enhancer enhancer = new Enhancer();
//继承被代理类
enhancer.setSuperclass(Person.class);
//设置回调
enhancer.setCallback(new PersonInterceptor());
//生成代理类对象
Person person = (Person) enhancer.create();
//在调用代理类中方法时会被我们实现的方法拦截器进行拦截
person.doSomeThing();
}
CGLIB生成的文件有3个,将其放在idea下的target目录反编译下,内容太多了就不看了,同样看到doSomething()方法,然后调用了interceptor中的intercept去执行doSomething()方法。
看上面生成的class字节码文件中其实两种动态代理的实现方式最终都是通过代理拦截器等在重新生成的代理方法中使用了intercept或invoke去调用方法,最终实现可以在方法执行的不通情况下做出对应的处理。同时可以猜测Spring的异常通知是不是在方法上加了try/catch去处理的?
JDK动态代理必须实现接口,而CGLIB动态代理不需要实现接口,所以在Spring AOP选择代理方式的时候有这么一个判断,根据同的情况选择不同的代理方式。
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
return new ObjenesisCglibAopProxy(config);