JDK动态代理与CGLIB动态代理
文章目录
- 前言
- 一、JDK动态代理
-
- 示例
- 代理类
- 二、使用步骤
-
- 示例
- 代理类
- 总结
前言
最近准备写Spring的AOP源码的,结果看了看源码发现思想逻辑设计都比较简单,本来想要从开始注入到后面的方法执行都跟下来走一遍的,但是忘了JDK动态代理和CGLIB动态代理是怎么实现的了,所以写了这篇文章来介绍回忆一下JDK动态代理与CGLIB动态代理。
一、JDK动态代理
示例
//创建被代理对象和接口
public interface PersonInterface {public void doSomething();}public class Person implements PersonInterface{@Overridepublic 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;}@Overridepublic 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是InvocationHandlerpublic 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 {@Overridepublic 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);