首页 > 其他分享 >@Configuration 注解的 Full 模式和 Lite 模式!

@Configuration 注解的 Full 模式和 Lite 模式!

时间:2023-09-03 13:01:12浏览次数:35  
标签:Full dog 模式 Bean user Lite 注解


@Configuration 注解相信各位小伙伴经常会用到,但是大家知道吗,这个注解有两种不同的模式,一种叫做 Full 模式,另外一种则叫做 Lite 模式。

准确来说,Full 模式和 Lite 模式其实 Spring 容器在处理 Bean 时的两种不同行为。

这两种不同的模式在使用时候的表现完全不同,今天松哥就来和各位小伙伴捋一捋这两种模式。

1. 概念梳理

首先我们先来看一下 Spring 官方文档中对 Full 模式和 Lite 模式的一个介绍:

@Configuration 注解的 Full 模式和 Lite 模式!_spring

截图来自:https://docs.spring.io/spring-framework/reference/core/beans/java/basic-concepts.html

这个文档主要讲了这样几件事情:

  1. 我们可以通过在一个方法上添加 @Bean 注解,进而将该方法的返回值暴露给 Spring 容器,在这种场景下,@Bean 注解实际上就是一种通用的工厂方法机制。
  2. 当一个添加了 @Bean 注解的方法位于一个没有添加 @Configuration 注解的类里边时,那么这个添加了 @Bean 注解的方法在处理时就会按照 Lite 模式来处理。
  3. 当一个 Bean 被声明在添加了 @Component 注解的类中,那么会按照 Lite 模式来处理。
  4. 当一个 Bean 被声明在一个普通的类中时(plain old class),按照 Lite 模式来处理(这一点感觉和第二点差不多)。
  5. 在 Lite 模式下,@Bean 注解标记的方法最终不会被 CGLIB 进行代理,就是一个普通的工厂方法,因此,在 @Bean 标记的方法中,不能调用其他 @Bean 注解标记的方法,如果有需要,可以通过方法参数注入自己所需要的 Bean。
  6. 由于 Lite 模式下并不会使用 CGLIB,因此 @Bean 标记的方法可以是 final 类型的。
  7. 在大多数场景下,我们在一个 @Configuration 注解标记的类中,使用 @Bean 注解向 Spring 容器注册一个 Bean,都是 Full 模式。

官网文档的介绍还是有些抽象,接下来松哥通过具体的案例来和大家演示 Full 模式和 Lite 模式的差别。

2. Full 模式

先看 Full 模式,中文也可以称之为 完整 模式,我们平时使用时,在一个配置类上添加 @Configuration 注解,且不添加任何额外属性,这就是 Full 模式了。

Full 模式最大的特点是会给配置类通过 CGLIB 生成一个代理,所有被 @Bean 注解标记的方法将来都是通过代理方法进行调用。

假设我有如下配置类:

@Configuration
public class JavaConfig {

    @Bean
    User user() {
        return new User();
    }
}

现在,我们去 Spring 容器获取这个配置类:

public class JavaDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        JavaConfig config = ctx.getBean(JavaConfig.class);
        System.out.println("config.getClass() = " + config.getClass());
    }
}

打印结果如下:

@Configuration 注解的 Full 模式和 Lite 模式!_java_02

大家看到,最终从 Spring 容器中拿到的 JavaConfig 实例并不是原始的 JavaConfig 对象,而是一个被代理的 JavaConfig 对象。

为什么要代理呢?肯定是为了实现某些功能。

大家看下面这个案例:

@Configuration
public class JavaConfig {

    @Bean
    User user() {
        User user = new User();
        user.setDog(dog());
        return user;
    }
    
    @Bean
    Dog dog() {
        return new Dog();
    }
}

在 Full 模式下,在 user() 方法中调用 dog() 方法的时候,调用的是一个代理对象的 dog 方法,在这个代理对象的 dog 方法中,会首先去检查 Spring 容器中是否存在 Dog 对象,如果存在,则直接使用 Spring 容器中的 dog 对象,就不会真正去执行 dog 方法而获取到一个新的 dog 对象了,如果 Spring 容器中不存在 dog 对象,才会创建新的 dog 对象出来。

一言以蔽之,在 Full 模式下,user 中的 dog 对象和 dog 方法注册到 Spring 容器的 dog 对象是同一个。

在 Full 模式下,由于要给当前类生成代理,然后去代理 @Bean 注解标记的方法,因此,这些 @Bean 注解标记的方法不能是 final 或者 private 类型的,因为 final 或者 private 类型的方法无法被重写,也就没法生成代理对象,如果添加了 final 或者 private 修饰符,那么会抛出如下异常:

@Configuration 注解的 Full 模式和 Lite 模式!_spring_03

3. Lite 模式

再来看 Lite 模式,这种模式可以认为是一种精简模式。

怎么开启呢?我们可以去除配置类上的 @Configuration 注解,或者去除之后添加 @Component 注解,又或者使用 @ComponentScan、@ImportResource、@Import 等注解标记类,那么最终都是 Lite 模式:

@Component
public class JavaConfig {

    @Bean
    final User user() {
        User user = new User();
        user.setDog(dog());
        return user;
    }

    @Bean
    Dog dog() {
        return new Dog();
    }
}

此时就是 Lite 模式,现在我们去 Spring 容器中获取这个配置类:

public class JavaDemo {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
        JavaConfig config = ctx.getBean(JavaConfig.class);
        System.out.println("config.getClass() = " + config.getClass());
    }
}

最终打印结果如下:

@Configuration 注解的 Full 模式和 Lite 模式!_动态代理_04

大家看到,我们从 Spring 容器中拿到的就是原始的对象,而不是一个被代理过的对象。因此:

  1. 由于 @Bean 注解标记的方法没有被代理,因此,该方法可以是 final 也可以是 private,运行时都不会报错。
  2. 由于 @Bean 方法没有被代理,因此在 user 方法中调用 dog 方法的时候,就直接调用了,这就导致 user 中的 dog 和最终 dog 方法注册到 Spring 容器中的 dog 不是同一个。

针对第二点,如果想要确保 user 中的 dog 和 Spring 容器中的 dog 是同一个,那么可以通过参数将所需要的对象注入进来,类似下面这样:

@Component
public class JavaConfig {

    @Bean
    final User user(Dog dog) {
        User user = new User();
        user.setDog(dog);
        return user;
    }

    @Bean
    Dog dog() {
        return new Dog();
    }
}

当 Spring 容器调用 user 方法初始化 User 对象时,发现该方法还有参数,因此会去容器中查找这个参数,找到了直接使用。

另外,我们也可以在类上添加 @Configuration 注解,但是通过修改属性值来启用 Lite 模式:

@Configuration(proxyBeanMethods = false)
public class JavaConfig {

    @Bean
    final User user(Dog dog) {
        User user = new User();
        user.setDog(dog);
        return user;
    }

    @Bean
    Dog dog() {
        return new Dog();
    }
}

如果设置了 proxyBeanMethods 属性为 false,那么也就是 Lite 模式了,其实我们从属性名称上也能看出来端倪:是否代理 @Bean 注解标记的方法。

4. 小结

总结一下:

  1. Lite 模式下,配置类中的方法就是普通方法,可以是 final 类型,也可以是 private。
  2. Lite 模式下,不需要通过 CGLIB 生成动态代理类,所以启动速度会快一些。
  3. Lite 模式下,一个 @Bean 方法调用另外一个 @Bean 方法,会导致同一个 Bean 被初始化两次。
  4. Full 模式下,会给配置类生成一个动态代理类,配置类中的所有方法都将被动态代理,因此配置类中的方法不能是 final 或者 private 的。
  5. Full 模式下,一个 @Bean 方法调用另外一个 @Bean 方法,动态代理方法会先去容器中检查是否存在该 Bean,如果存在,则直接使用容器中的 Bean,否则才会去创建新的对象。

日常开发中,我们使用较多的是 Full 模式。


标签:Full,dog,模式,Bean,user,Lite,注解
From: https://blog.51cto.com/u_9806927/7339526

相关文章

  • javaee spring注解设置单例模式和懒加载模式
    @Lazy懒加载@Scope(scopeName=“prototype”)设置多例模式,不加默认单例模式@Lazy@Component@Scope(scopeName="prototype")publicclassDrink{@Value("橙汁")privateStringname;@Value("半糖")privateStringsugar;@Value(&quo......
  • python操作sqlite
    importjsonimportsqlite3importpandasaspdclassSqliteTool:def__init__(self,db_path):self.db_path=db_pathself.conn=sqlite3.connect(self.db_path)self.conn.row_factory=sqlite3.Rowself.cursor=self.con......
  • 设计模式
    1类与类之间的关系继承(泛化)组合:整体对象可以控制成员对象的生命周期,一旦主体对象不存在,成员对象也不存在,整体对象和成员对象之间具有同生共死的关系,例如人的头部与眼、耳朵聚合:成员对象是整体的一部分,但是成员对象可以脱离主体对象独立存在。主体对象析构到的时候成员对象依......
  • 设计模式:迭代器模式
    设计模式wiki中将设计模式分为四类,分别是:创建模式(creationalpatterns)结构模式(structuralpatterns)行为模式(behavioralpatterns)并发模式(concurrencypatterns)迭代器模式属于其中的行为模式。迭代器做的只有一件事,就是解决集合的遍历问题。以下是wiki对迭代器模式的概括......
  • 网页版B站暗黑模式:Chrome Dark Reader 插件
    https://chrome.google.com/webstore/detail/dark-reader/eimadpbcbfnmbkopoojfekhnkhdbieeh?utm_source=ext_app_menu使用说明https://darkreader.org/help/zh-CN/效果还不错......
  • 设计模式学习1 设计原则
    设计原则1.开闭原则对扩展开放,修改关闭。在程序需要扩展的时候,不能去修改原有代码,实现一个热插拔的效果。为了使程序的扩展性好,易于维护和升级为了达到这样的效果,我们需要使用接口和抽象类2.里氏代换原则任何基类可以出现的地方,子类一定可以出现。也就是子类继承父类时,除了添......
  • wangEditor增加源码模式,添加查看源码功能
    wangEditor是一款轻量级的富文本编辑器。使用还比较方便,但是缺少查看源码模式,需要我们自定义一个menu给增加查看源码模式下面是wangEditor增加源码模式的代码:<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="......
  • 多线程|饿汉模式和懒汉模式
    单例模式是只有单个实例的模式,应用在只能有一个实例的场景中。单例模式有很多种,这里介绍饿汉模式和懒汉模式两个单例。一、饿汉模式“饿汉”是一种形象的描述,“饿汉”看到吃的就非常急切,把这种急切的形象类比到Java中就是在类加载阶段就把实例创建出来了。什么是类加载?Java代码......
  • 设备正常在线内网正常播放但公网无法播放国标GB28181视频平台LiteCVR异常处理
    近期我们整理并汇总了一些往期使用者在使用LiteCVR视频汇聚平台时候出现的技术问题反馈,并将逐步分享出根据使用者的反馈和问题描述的技术问题的解决办法和优化步骤来供大家参考。 根据使用者的反馈,我们了解到已接入到LiteCVR平台的设备在内网正常播放视频,但在公网无法播放,并且一直......
  • 【从互联网商业思维的角度分析商业模式在国内各大互联网产品的运用】
    随着互联网技术的不断进步,互联网商业模式也在不断变化,各个互联网企业都在不断尝试各种商业模式,以满足不同消费者群体的需求,提高企业营销效益,下面我们将从互联网商业思维的角度,分析一下商业模式在国内各大互联网产品的运用。一、电商模式电商模式是指在互联网上建立电子商务平台,通......