您的位置:

深入解析Spring的proxy-target-class属性

一、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属性,在大型应用程序中或需要高性能的场景中,建议使用基于接口的动态代理来提高系统的性能。