一、proxy-target-class概念介绍
在Spring框架中,AOP被广泛用于各种类型的应用程序,用于将横切关注点与业务逻辑相分离,以提高应用程序的灵活性和可重用性。 Spring Framework通过动态代理或CGLIB库实现了AOP。 Spring AOP支持两种类型的代理:接口代理(默认情况下)和基于类的代理(使用proxy-target-class属性)。 接口代理要求目标对象实现至少一个接口。 基于类的代理(一般使用CGLIB库)则无需接口。相反,它们使用字节代码生成来创建目标对象的代理。 建议在以下情况下使用基于类的代理:
• 目标对象不实现任何接口
• 要从代理方法中调用另一个方法而不添加太多额外的代码,这可能是使用AOP的一个常见要求
• 希望代理位置更快(默认情况下,接口代理通常比基于类的代理慢)
二、proxy-target-class属性用法
Spring框架中提供了两种代理方式proxy-target-class和proxy-interface。proxy-target-class默认值为false,代表使用基于接口的JDK动态代理,此时目标对象的实现类必须至少实现一个接口;而如果proxy-target-class设置为true,则使用CGLIB方式的动态代理。
使用proxy-target-class的时候,目标类不需要实现任何接口也能被AOP代理拦截,Spring会选择CGLIB代理方式生成动态代理对象。
下面是具体的proxy-target-class使用示例
@Configuration @EnableAspectJAutoProxy(proxyTargetClass = true) public class AppConfig { @Bean public MyAspect myAspect() { return new MyAspect(); } @Bean public UserService userService() { return new UserServiceImpl(); } }
可以看到,通过@EnableAspectJAutoProxy(proxyTargetClass = true)开启CGLIB代理,默认为false,开启CGLIB代理需要导入CGLIB库。
三、CGLIB与JDK动态代理的区别
基于类的代理(即CGLIB)通常比基于接口的代理(即JDK)更快,因为它们直接操作字节码。但是,CGLIB不适用于代理类中的final方法 - 您不能在CGLIB类中覆盖final方法。 此外,使用CGLIB可能会将代理类的大小从一个接口代理的62字节增加到100字节或更多,而增加的码元数量可能对某些JIT编译器效果不佳。
下面是一个基于类的代理的简单示例
public class HelloWorld { public void sayHello(String name) { System.out.println("Hello " + name); } public static void main(String[] args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(HelloWorld.class); enhancer.setCallback(new MethodInterceptorImpl()); HelloWorld proxy = (HelloWorld) enhancer.create(); proxy.sayHello("World"); } } class MethodInterceptorImpl implements MethodInterceptor { public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before Method Invocation"); Object returnObj = proxy.invokeSuper(obj, args); System.out.println("After Method Invocation"); return returnObj; } }
四、proxy-target-class的使用场景
proxy-target-class属性的最主要的使用场景就是处理没有实现任何接口的独立类的AOP。在Java中,我们往往缺乏混入委托等支持多重继承的特性,导致同一个对象分支下的逻辑散布于不同类中,这就是所谓的横切关注点。AOP可以解决这个问题,而对于没有实现接口的类来说,proxy-target-class就派上用场了。
五、总结
在Spring AOP中,默认情况下使用基于接口的动态代理,可以通过配置proxy-target-class属性来选择使用CGLIB提供的基于类的动态代理。对于那些不实现任何接口的类来说,proxy-target-class是必不可少的。
最后,需要注意的是,应谨慎使用proxy-target-class属性,在大型应用程序中或需要高性能的场景中,建议使用基于接口的动态代理来提高系统的性能。