精简还是全能?如何在 Full 和 Lite 之间做出最佳选择!关于Configuration注解的Full模式与Lite模式(SpringBoot2)

在这里插入图片描述

  • 🏃‍♂️ 微信公众号: 朕在debugger
  • © 版权: 本文由【朕在debugger】原创、需要转载请联系博主
  • 📕 如果文章对您有所帮助,欢迎关注、点赞、转发和订阅专栏!


前言

关于 @Configuration 注解,相信在座的各位 Javaer 都使用过,且大部分人使用它是直接在配置类上塞一个 @Configuration 就完事了,不会去过多使用它的参数,这期文章是来讲述 @Configuration 注解的一个代理相关的参数 proxyBeanMethod。

回顾

说是讲 proxyBeanMethod 参数,但是注解本身的功能也需要大致过一下,@Configuration 注解是 Spring 框架中的一个核心注解,用于定义配置类。

配置类可以包含定义和生成 SpringBean 的方法。这些方法通过 @Bean 注解进行标记。(默认是单实例的)

同时加上 @Configuration 注解的类也允许定义额外的bean( @Import )或导入其他配置类( @ImportResource )。



一、何为 Full 模式?何为 Lite 模式?查看源码辩雌雄

说 Full,说 Lite,其实都是在说 proxyBeanMethods 这个参数,也就是代理 bean 的方法。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;

    boolean enforceUniqueMethods() default true;
}

可以看出,proxyBeanMethods 方法返回值是一个 Boolean 值,默认值是 true,什么意思呢?

它的值为 true 时,就是 Full 模式。为 false 时,就是 Lite 模式。

Full 模式

Full 模式是当你在类上使用 @Configuration 注解且没有将 proxyBeanMethods 属性显式设置为 false 时(默认为 true )的模式。

在 Full 模式下,Spring 容器会通过 CGLIB 动态代理技术创建配置类的代理实例。

这样做的目的是保证同一个配置类中对其他 @Bean 方法的调用总是返回相同的实例,即保证 @Bean 方法是以单例的方式被管理的,无论它们被调用多少次。

这对于维护配置类中 Bean 之间的依赖关系非常重要,因为它确保了依赖注入的一致性和 Spring 容器中 Bean 的单例特性。

Lite 模式

Lite 模式是当你在类上使用 @Component、@Service、@Repository 等其他组件注解,或者使用 @Configuration 注解但将 proxyBeanMethods 属性显式设置为 false 时的模式。

在 Lite 模式下,Spring 容器不会创建配置类的 CGLIB 代理。

这意味着,配置类中的 @Bean 方法在每次调用时都会生成一个新的实例(除非你自己在方法内部处理单例逻辑),并且这些方法之间的调用就像普通 Java 方法调用一样,不会被 Spring 容器特别处理。

Lite 模式通常用于轻量级的 Bean 定义,或者当配置类中的 @Bean 方法不需要相互依赖时,可以提高性能,减少资源消耗。

二、案例验证

定义一个配置类,定义了两个Bean:一个数据库连接池(DataSource)和一个 JdbcTemplate,后者依赖于前者。

2-1、将 proxyBeanMethods 设置为 true(默认)

@Configuration(proxyBeanMethods = true) // 默认为true,可以不显式设置
public class AppConfig {

    // 创建了一个DataSource实例
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        // 这里调用了dataSource()方法来获取DataSource实例
        return new JdbcTemplate(dataSource());
    }
}

在这个例子中,因为 proxyBeanMethods 设置为 true,当 Spring 调用 jdbcTemplate() 方法来创建 JdbcTemplate Bean 时,内部通过调用 dataSource() 方法得到的 DataSource 实例,将会是同一个实例。

这是因为配置类被 CGLIB 代理,确保了 dataSource() 方法的调用在整个应用上下文中只创建了一个 DataSource 实例,即使它被多次调用。

这对于保证某些 Bean 的单例特性是非常有用的,尤其是当这些 Bean 之间存在依赖关系

2-2、将 proxyBeanMethods 设置为 false

@Configuration(proxyBeanMethods = false)
public class AppConfig {

    // 创建了一个DataSource实例
    @Bean
    public DataSource dataSource() {
        return new HikariDataSource();
    }

    @Bean
    public JdbcTemplate jdbcTemplate() {
        // 这里调用了dataSource()方法来获取DataSource实例
        return new JdbcTemplate(dataSource());
    }
}

在这种情况下,每次从配置类中调用 @Bean 方法(如直接在另一个 @Bean 方法内调用或者在应用中通过上下文调用)都将创建一个新的实例。

这会导致额外的资源消耗和初始化开销,尤其是对设计为单例使用的资源密集型 Bean(如数据库连接池)来说。

但是,这种的好处就是不需要为配置类创建 CGLIB代理,所以可以提升启动时间和减少运行时的内存占用。

探究:不使用 CGLIB 代理为何可以提升启动时间和减少运行时的内存占用?

  • 减少类的加载和处理时间:
    当 proxyBeanMethods 设置为 true 时,Spring 会通过 CGLIB 库为每个配置类创建一个子类(代理类)。

    这个过程需要加载额外的类,执行字节码生成和处理,这会增加应用启动时的CPU和内存开销。

    相反,当 proxyBeanMethods 设置为 false 时,Spring 不会创建这些代理类,从而减少了启动阶段的资源消耗。

  • 避免方法拦截开销:
    在使用 CGLIB 代理的配置类中,每次调用 @Bean 标注的方法时,都会通过代理类,这个代理类会检查是否已经为该 Bean 创建了实例,并确保返回的是同一个实例(单例 Bean)。这个检查和拦截过程在运行时会产生额外的开销

    而 proxyBeanMethods=false 意味着 @Bean 方法会被直接调用,不经过代理类,从而避免了这部分开销。

  • 减少内存占用
    减少代理对象的内存占用:为配置类创建代理对象会增加 JVM 堆内存的使用。

    每个代理类实例都需要消耗一定的内存,特别是在大型应用中,可能有大量的配置类,这种内存占用就变得非常可观。

    关闭代理( proxyBeanMethods=false )意味着这部分内存开销可以被省略。

  • 优化 Spring 容器的管理开销:
    不使用 CGLIB 代理简化了 Spring 容器对 Bean 生命周期的管理。

    Spring 容器不需要维护额外的信息来处理配置类的代理逻辑,这可以进一步降低内存使用并简化容器的运行时行为。

三、何时使用 Full 模式与 Lite 模式?

Full 模式适用于复杂的配置场景,其中配置类中的 Bean 之间存在依赖关系,需要确保通过配置类方法调用获取到的 Bean 是单例的。它适合于那些需要充分利用 Spring 框架提供的依赖注入和 Bean 生命周期管理特性的应用。

Lite 模式适用于简单或性能敏感的应用,其中配置类主要用于组织 Bean 定义,而 Bean 之间的依赖关系较为简单或可以通过其他方式(如构造器注入)解决。

总之,proxyBeanMethods属性的设置取决于你的具体需求,是否需要在配置类中保持@Bean方法调用的单例特性。

在大多数情况下,默认的 true 值是合理的,但在某些高性能需求和简单配置的场景下,将其设置为 false 可能会更有利。

四、总结

Full ( proxyBeanMethods = true ) 保证每个 @Bean 方法被调用多少次返回的组件都是单实例的。

Lite ( proxyBeanMethods = false ) 每个 @Bean 方法被调用多少次返回的组件都是新创建的。

存在组件依赖关系则使用 Full 模式(默认)。否则可以使用 Lite 模式。


finally

如果大家觉得本文写得不错,别忘了给个赞哦!同时,如果您有任何疑问或建议,欢迎在评论区留言,让我们一起交流、探讨!
  • 23
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
网友反映高速版较为复杂,不易整体把握概貌。为了让高速版更加清晰明了,我对它做个改造,在无乘法的版本下,去除了所有的中断和七大模式。现在,它只工作在一种模式下,因此,我们在编写嵌入式软件时,必须注意不能让它切换模式,那么它就能工作无误。 为此,我在压缩包里包括了一个Keil Realview的project包、一个iverilog的仿真包。读者可以参照code.google.com/p/risclite上面的描述,安装这两个软件。在下载完了下面的压缩包后,在本地解压缩。 首先,打开Hello文件夹下面的Hello.uvproj,那么就会激活Keil Realview,并打开了一个嵌入式软件开发项目。我们看到了一个非常简单的startup.s。这个startup.s只是非常简单的设定了堆栈指针后,就直接跳转到main()函数去执行。所以我们打开Hello.c,里面的main()函数使用软件乘法器完成乘方运算,并打印出来。我们可以增加我们自己的c代码描述,然后重新编译。 下面,进入另外一个sim目录下。如果我们安装了iverilog软件,只需双击run.cmd文件。接下来,在一个dos窗口下,就会显示出刚才在Hello文件夹下编译的程序的实现效果。这段效果,正是我们用C语言编写程序,用Verilog仿真实现了程序的功能。 好了,接下来,我们只需在Hello.c里面增加我们自己的C函数,只要我们编译,然后在仿真目录下,就可以马上看到我们的编译效果了。仿真包里面的risclite_mx.v完全可综合,较之以前,体积更小了(加上详尽的注释,只有1400行)。 注意:由于这个版本不实现乘法指令,在编写C函数时,避免出现乘法指令。如果要实现乘法操作,可以参照本例的软件乘法功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值