Spring的AOP在运行时多以jdk及cglib动态代理来实现。(作者jdk是1.8版本)
1 jdk 动态代理
Java中使用动态代理,只能对接口进行代理,不能对普通类进行代理。主要是由一个类及一个接口来实现:
InvocationHandler:调用处理器接口,我们需要实现该接口的invoke方法,用来完成代理工作。当代理实例在调用代理方法时,将会调用该接口的invoke方法。
Proxy: 提供了用于创建动态代理类和实例的静态方法。
public interface ShopInterface {void buy(String goodsInfo,Double price);ShopInterface showInfo(Object object);}public class People implements ShopInterface{@Overridepublic void buy(String goodsInfo, Double price) {System.out.println("购物,商品是:" + goodsInfo + ",价格是:" + price);}@Overridepublic ShopInterface showInfo(Object object) {System.out.println("信息展示:" + object);return this;}
}public class CustomInvocationHandler implements InvocationHandler {/*** 被代理的实例*/private final Object instance;public CustomInvocationHandler(Object instance) {this.instance = instance;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("jdk动态代理开始---");System.out.println("拦截的方法是:" + method.getName() + ",所属接口:" + method.getDeclaringClass().getName());if (args != null) {System.out.println("参数是:" + Arrays.asList(args));}Object result = method.invoke(instance, args);System.out.println("结果是:" + result);System.out.println("jdk动态代理结束---");return result;}public static void main(String[] args) {People people = new People();ShopInterface proxyInstance = (ShopInterface) Proxy.newProxyInstance(People.class.getClassLoader(), new Class<?>[]{ShopInterface.class}, new CustomInvocationHandler(people));proxyInstance.buy("iPhone",6999d);}
}
1.1 invocationHandler的invoke方法
public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
proxy: 代理实例。即Proxy的newProxyInstance方法创建的实例。
method: 被代理的方法
args: 方法参数
我们可以通过返回proxy来实现对某些被代理方法的连续调用:
public class ProxyReturnInvocationHandler implements InvocationHandler {private final Object instance;public ProxyReturnInvocationHandler(Object instance) {this.instance = instance;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("代理开始---");Object result = method.invoke(instance, args);System.out.println("代理结束---" + result);if ("showInfo".equals(method.getName())) return proxy;return result;}public static void main(String[] args) {People people = new People();ShopInterface proxyInstance = (ShopInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{ShopInterface.class}, new ProxyReturnInvocationHandler(people));proxyInstance.showInfo("黄先生").showInfo(27).showInfo("深圳");proxyInstance.buy("iphone",5999d);}}
1.2 可实现多接口
在调用Proxy到newProxyInstance方法时,可以传入多个接口类型。使用时把生成的代理实例转化为特定的接口类型即可:
public interface StudyInterface {void read(String book);}public class Student implements ShopInterface,StudyInterface {@Overridepublic void buy(String goodsInfo, Double price) {System.out.println("购买:" + goodsInfo + ",价钱:" + price);}@Overridepublic ShopInterface showInfo(Object object) {System.out.println("展示信息:" + object);return this;}@Overridepublic void read(String book) {System.out.println("看书:" + book);}}public class MoreInvocationHandler implements InvocationHandler {private final Object instance;public MoreInvocationHandler(Object instance) {this.instance = instance;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("方法定义来源:" + method.getDeclaringClass());Object result = method.invoke(instance, args);System.out.println("结束代理----" + result);if ("showInfo".equals(method.getName())) return proxy;return result;}public static void main(String[] args) {Student student = new Student();ShopInterface shopInterface = (ShopInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class<?>[]{ShopInterface.class, StudyInterface.class}, new MoreInvocationHandler(student));shopInterface.showInfo("学生").showInfo(18);StudyInterface studyInterface = (StudyInterface) shopInterface;studyInterface.read("Java从入门到放弃");}
}
1.3 原理
Proxy类的newProxyInstance方法,生成代理类实例。
图 newProxyInstance方法的部分截图
最后是在Proxy内部静态类ProxyClassFactory的apply方法生成代理类的字节码。
图 ProxyClassFactory的apply方法的部分截图
下面,我们将使用ProxyGenerator的generateProxyClass方法来生成代理类的class文件并对其进行反编译:
public class GenerateProxyClass {public static void main(String[] args) throws IOException {String proxyName = "com.article.CustomProxy"; //代理类名词byte[] bytes = ProxyGenerator.generateProxyClass(proxyName, new Class<?>[]{StudyInterface.class, ShopInterface.class}, Modifier.FINAL);//final修饰符String pathStr = "/Users/huangzaizai/Desktop/temp/CustomPoxy.class"; // class输出位置Path path = Paths.get(pathStr);OutputStream outputStream = Files.newOutputStream(path, StandardOpenOption.WRITE);outputStream.write(bytes);outputStream.close();}}
生成的代理类class文件经过反编译后如下:
final class CustomProxy extends Proxy implements StudyInterface, ShopInterface {private static Method m1;private static Method m5;private static Method m3;private static Method m2;private static Method m4;private static Method m0;public CustomProxy(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 ShopInterface showInfo(Object var1) throws {try {return (ShopInterface)super.h.invoke(this, m5, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final void read(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 void buy(String var1, Double var2) throws {try {super.h.invoke(this, m4, new Object[]{var1, var2});} catch (RuntimeException | Error var4) {throw var4;} catch (Throwable var5) {throw new UndeclaredThrowableException(var5);}}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"));m5 = Class.forName("article.dynamic.ShopInterface").getMethod("showInfo", Class.forName("java.lang.Object"));m3 = Class.forName("article.dynamic.more.StudyInterface").getMethod("read", Class.forName("java.lang.String"));m2 = Class.forName("java.lang.Object").getMethod("toString");m4 = Class.forName("article.dynamic.ShopInterface").getMethod("buy", Class.forName("java.lang.String"), Class.forName("java.lang.Double"));m0 = Class.forName("java.lang.Object").getMethod("hashCode");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}
可以看到,生成的代理类继承了Proxy,而实现了传入的接口。并且在代理方法执行时,是通过调用InvocationHandler 实例的invoke方法,并把代理类的实例,被代理方法及参数传入了该方法。
1.3.1代理模式的本质
图 代理模式UML
jdk动态代理通过动态生成代理类本质上还是“静态代理”。
图 动态代理相关类
在运行时,动态生成CustomProxy动态代理类,继承于Proxy(所以不能代理类实例转化为目标类类型,只能转化成其接口类型。同时只能对接口进行代理而不是类),在调用代理类方法时,会调用InvocationHandlerd的invoke方法。
2 cglib代理
cglib是一个开源库,也经常用于实现动态代理(Spring用得较多)。它不仅可以对接口进行代理,还可以对类进行代理。
其主要也是由一个类及一个接口来实现:
Enhancer:增强器。用于设置代理方法拦截器、拦截器的过滤器及生成代理类。
Callback(通常是MethodInterceptor): 代理方法拦截器,用于实现对代理方法的增强。
public class Employee {public void work() {System.out.println("努力工作");}public void work(String task) {System.out.println("工作任务是:" + task);}}public class CustomTest {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Employee.class);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("cglib动态代理开始");if (args != null) {System.out.println("参数是:" + Arrays.asList(args));}Object result = proxy.invokeSuper(obj, args);System.out.println("动态代理结束---" + result);return result;}});Employee employee = (Employee) enhancer.create();employee.work();employee.work("开发某个模块");}}
2.1 MethodInterceptor
代理方法“around advice”增强的回调。即在通过代理类调用方法时,会调用MethodInterceptor的方法来实现对目标方法的增强。
public Object intercept(Object obj, java.lang.reflect.Method method, Object[] args,MethodProxy proxy) throws Throwable;
obj: 代理类实例,即增强对象。
method: 执行的方法;
args:方法参数;
proxy: 目标方法的代理
MethodProxy类有两个方法用来执行目标方法:invoke和invokeSuper。
图MethodProxy的 invoke和invokeSuper方法
这两处方法唯一的不同是红圈处,即不同的f实例执行了invoke方法。而这两个实例分别是:
FastClass f1: 目标类的FastClass
FastClass f2: 代理类的FastClass
FastClass 是cglib用于快速寻找类的方法的一种机制类。对一个类的方法建立索引,通过索引来直接调用相应的方法,避免反射调用,提高效率。
图 MethoProxy 构造器断点调试
2.2 Enhancer
通过生成直接继承于目标类的子类来实现动态代理,但是不能代理final方法。
该类的setCallbacks用于设置一组方法拦截器。而setCallbackFilter则是对这些拦截器进行过滤选择。主要,在任何情况下,代理类只能调用一个Callback,所以如果设置了多个Callback,则必须设置CallbackFilter。
public class MethodInterceptor1 implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("拦截器1");Object result = proxy.invokeSuper(obj, args);System.out.println("结果是:" + result);return result;}
}public class MethodInterceptor2 implements MethodInterceptor {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("拦截器2");Object result = proxy.invokeSuper(obj, args);System.out.println("结果是:" + result);return result;}
}public class CustomTest2 {public static void main(String[] args) {Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Employee.class);enhancer.setCallbacks(new Callback[]{new MethodInterceptor1(),new MethodInterceptor2()});enhancer.setCallbackFilter(new CallbackFilter() {@Overridepublic int accept(Method method) {if (method.getParameterCount() > 0) return 1;return 0;}});Employee employee = (Employee) enhancer.create();employee.work();employee.work("开发某个模块");}
}
2.3 原理
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "文件输出位置"); 来生成cglib调试版本生成的代理类等相关类。
public class CustomTest3 {public static void main(String[] args) {// 设置 CGLIB 的 debug 输出位置System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/huangzaizai/Desktop/debugging");Enhancer enhancer = new Enhancer();enhancer.setSuperclass(Employee.class);enhancer.setCallback(new MethodInterceptor() {@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("代理开始");proxy.invokeSuper(obj,args);System.out.println("代理结束");return null;}});Employee employee = (Employee) enhancer.create();employee.work();}
}
图 cglib生成的相关类
图 代理类继承了目标类
图 代理类中实现的父类(目标类)方法
当通过生成的代理实例调用方法时,会执行对MethodIntercaptor的intercept方法的调用。另外,代理类还生成了一个类似代理方法的方法:CGLIB$work$1。这个方法名在为“work”方法创建ProxyMethod时,作为proxyMethod的一个方法签名创建。
当在ProxyMethod中执行invokerSuper这个方法时,是根据这个签名的索引来查找方法。
3 jdk 动态代理与cglib的区别
jdk | cglib | |
生成类 | 只生成一个代理类。 | 生成类的数目较多,包括代理类,代理类和目标类(包括目标类的祖先)的FastClass类。而且代理类的代码量也更多。 |
可代理对象 | 只能是接口。 | 可以是接口也可以是是类,但是不能代理final方法。 |
实现方式 | 生成继承与Proxy并实例了代理接口的子类。 | 生成继承目标类的子类。 |
执行速度 | 大量依靠反射,执行效率会更慢些。 | 通过FastClass机制,根据方法索引来执行查找方法,避免了反射执行,速度上有提升,但是当需要代理的方法较多时,查找方法所耗费的时间也更多。同时在运行期间生成了这么多类,占据了大量的元空间内存。 |
表 jdk动态代理与cglib的对比