Administrator
发布于 2025-01-13 / 7 阅读
0
0

proxy-target-class & proxyBeanMethods

proxy-target-class

含义

proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。

  • true,则是基于类的代理将起作用(需要cglib库),
  • false或者省略这个属性,则标准的JDK 基于接口的代理将起作用。

但是,即使设置为false,如果目标类没有生命接口,则Spring将自动使用CGLib动态代理。


用大白话来说,就是当要使用实现了某个接口的类,让Spring来生成bean时,会使用aop动态代理技术,生成代理类。此时,程序需要知道,采用哪一种代理技术,是CGLIB代理还是JDK代理。


JDK动态代理通过反射来接收被代理的类,并且要求被代理的类必须实现一个接口。JDK动态代理的核心是InvocationHandler接口和Proxy类。

如果目标类没有实现接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。

CGLIB(Code Generation Library),是一个代码生成的类库,是利用asm开源包,可以在运行时动态的生成某个类的子类。注意,CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的。

示例

TransactionAutoConfiguration中,有如下代码:


@Configuration(proxyBeanMethods = false)
	@ConditionalOnBean(TransactionManager.class)
	@ConditionalOnMissingBean(AbstractTransactionManagementConfiguration.class)
	public static class EnableTransactionManagementConfiguration {

		@Configuration(proxyBeanMethods = false)
		@EnableTransactionManagement(proxyTargetClass = false)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false")
		public static class JdkDynamicAutoProxyConfiguration {

		}

		@Configuration(proxyBeanMethods = false)
		@EnableTransactionManagement(proxyTargetClass = true)
		@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
				matchIfMissing = true)
		public static class CglibAutoProxyConfiguration {

		}

	}

可以看到:

  • proxy-target-class为false,那么启用JdkDynamicAutoProxyConfiguration
  • proxy-target-class为true,那么启用CglibAutoProxyConfiguration

proxyBeanMethods

一、proxyBeanMethods属性的含义

  1. 代理机制
  • proxyBeanMethods = true(默认值),Spring会通过CGLIB为配置类生成代理类,拦截@Bean方法的调用,确保每次调用都返回容器内的单例Bean

  • proxyBeanMethods = false,配置类不会被代理,@Bean方法作为普通工厂方法执行,每次调用可能生成新对象(但注册到容器的Bean仍为单例)

  1. 模式对比
  • Full模式(proxyBeanMethods = true)​

    • 保证单例性:通过代理拦截方法调用,确保同一配置类中@Bean方法返回的实例是容器中的唯一对象

    • 支持Bean生命周期行为:如@PostConstruct@Destroy等注解生效

  • Lite模式(proxyBeanMethods = false)​

    • 无代理:方法调用直接执行,不通过容器,可能导致同一配置类中方法调用生成新对象

    • 性能优化:避免生成CGLIB代理类,减少启动时间和内存占用

二、proxyBeanMethods的作用

  1. 性能优化
  • 在Lite模式下,SpringBoot跳过代理类的生成,显著提升启动速度,尤其适用于大量配置类的场景(如SpringBoot的自动配置类)
  1. 单例控制
  • Full模式通过代理确保Bean的单例性,而Lite模式下,若直接在配置类内部调用@Bean方法,可能生成多个实例(但通过容器获取的Bean仍是单例)
  1. 方法调用限制
  • Full模式允许@Bean方法之间相互调用(通过容器获取依赖),而Lite模式下需通过参数传递依赖,否则会导致多次实例化
  1. 灵活性与限制
  • Lite模式支持将配置类作为普通类使用,允许@Bean方法为privatefinal,而Full模式因代理限制不允许这些修饰符

三、适用场景

  1. 使用Full模式的场景
  • 配置类中@Bean方法存在相互依赖(需通过方法调用获取其他Bean)

  • 需要保证Bean生命周期行为(如初始化/销毁回调)

  1. 使用Lite模式的场景
  • @Bean方法之间无依赖,且追求启动性能优化(如SpringBoot自动配置类)

  • 需要将配置类作为普通工具类使用(如静态工厂方法)

四、示例说明


@Configuration(proxyBeanMethods = false)
public class AppConfig {
    @Bean
    public MyBean myBean() {
        return new MyBean(); // 直接调用会生成新对象
    }
    
    @Bean
    public User user() {
        // Lite模式下需通过参数注入依赖,否则会创建新实例
        return new User(myBean()); 
    }
}

​输出分析:若在Lite模式下调用myBean()两次,会触发两次构造函数,生成两个对象;但通过容器获取的myBean始终是单例。

五、总结

  • 核心权衡:在单例安全性与性能之间选择。

  • 推荐实践:无依赖的配置类优先使用Lite模式,复杂依赖场景使用Full模式

  • SpringBoot默认策略:自动配置类普遍采用proxyBeanMethods = false,以减少启动开销


评论