“Spring Boot @Configuration 详解”的版本间的差异

来自姬鸿昌的知识库
跳到导航 跳到搜索
 
(未显示同一用户的14个中间版本)
第7行: 第7行:
 
=== @Configuration 的作用 ===
 
=== @Configuration 的作用 ===
 
告诉 Spring Boot 添加了 @Configuration 注解的类,是一个配置类,作用等同于以前的 XML 配置文件。
 
告诉 Spring Boot 添加了 @Configuration 注解的类,是一个配置类,作用等同于以前的 XML 配置文件。
 +
 +
==== 使用 <code>@Bean</code> 往容器中注册组件 ====
 +
<syntaxhighlight lang="java">
 +
@Configuration //告诉 SpringBoot 这是一个配置类 == 配置文件
 +
public class MyConfig {
 +
 +
    /**
 +
    * 给容器中添加组件。
 +
    * 以方法名作为组件的id。
 +
    * 返回类型就是组件类型。
 +
    * 返回的值,就是组件在容器中的实例
 +
    *
 +
    * 外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
 +
    * @return
 +
    */
 +
    @Bean
 +
    public User user01() {
 +
        return new User("zhangsan", 18);
 +
    }
 +
 +
    @Bean
 +
    public Pet tomcatPet() {
 +
        return new Pet("tomcat");
 +
    }
 +
 +
}
 +
</syntaxhighlight>运行 MainApplication:<syntaxhighlight lang="java">
 +
/**
 +
* 主程序类
 +
* @SpringBootApplication:这是一个 Spring Boot 应用
 +
*/
 +
@SpringBootApplication(scanBasePackages = "io.github.jihch")
 +
public class MainApplication {
 +
 +
    public static void main(String[] args) {
 +
        //1、返回 IOC 容器
 +
        ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
 +
 +
        //2、查看容器里面的组件
 +
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
 +
        Arrays.sort(beanDefinitionNames);
 +
        for (String name : beanDefinitionNames) {
 +
            System.out.println(name);
 +
        }
 +
 +
    }
 +
 +
}
 +
</syntaxhighlight><syntaxhighlight lang="console">
 +
……
 +
spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties
 +
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
 +
spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties
 +
spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties
 +
standardJacksonObjectMapperBuilderCustomizer
 +
stringHttpMessageConverter
 +
taskExecutorBuilder
 +
taskSchedulerBuilder
 +
tomcatPet
 +
tomcatServletWebServerFactory
 +
tomcatServletWebServerFactoryCustomizer
 +
tomcatWebServerFactoryCustomizer
 +
user01
 +
viewControllerHandlerMapping
 +
viewResolver
 +
webServerFactoryCustomizerBeanPostProcessor
 +
websocketServletWebServerCustomizer
 +
welcomePageHandlerMapping
 +
worldController
 +
……
 +
</syntaxhighlight>可以观察到容器中多了 tomcatPet 和 user01 两个 bean,默认使用方法名作为组件名,也可以通过 <code>@Bean("tom")</code> 自定义组件名,再运行 MainApplication:<syntaxhighlight lang="console">
 +
……
 +
spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties
 +
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
 +
spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties
 +
spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties
 +
standardJacksonObjectMapperBuilderCustomizer
 +
stringHttpMessageConverter
 +
taskExecutorBuilder
 +
taskSchedulerBuilder
 +
tom
 +
tomcatServletWebServerFactory
 +
tomcatServletWebServerFactoryCustomizer
 +
tomcatWebServerFactoryCustomizer
 +
user01
 +
viewControllerHandlerMapping
 +
viewResolver
 +
webServerFactoryCustomizerBeanPostProcessor
 +
websocketServletWebServerCustomizer
 +
welcomePageHandlerMapping
 +
worldController
 +
……
 +
</syntaxhighlight>可以发现组件名就变成自定义的组件名了。
 +
 +
 +
 +
==== 默认注册的组件是单实例的,无论从容器中获取多少次都是一样的 ====
 +
修改 MainApplication 并运行、验证<syntaxhighlight lang="java">
 +
/**
 +
* 主程序类
 +
* @SpringBootApplication:这是一个 Spring Boot 应用
 +
*/
 +
@SpringBootApplication(scanBasePackages = "io.github.jihch")
 +
public class MainApplication {
 +
 +
    public static void main(String[] args) {
 +
        //1、返回 IOC 容器
 +
        ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
 +
 +
        //2、查看容器里面的组件
 +
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
 +
        Arrays.sort(beanDefinitionNames);
 +
        for (String name : beanDefinitionNames) {
 +
            System.out.println(name);
 +
        }
 +
 +
        //3、从容器中获取组件
 +
        Pet tom01 = context.getBean("tom", Pet.class);
 +
 +
        Pet tom02 = context.getBean("tom", Pet.class);
 +
 +
        System.out.println("组件:" + (tom01 == tom02));
 +
 +
    }
 +
 +
}
 +
</syntaxhighlight><syntaxhighlight lang="console">
 +
……
 +
组件:true
 +
</syntaxhighlight>
 +
 +
 +
 +
 +
 +
==== @Configuration 注解的配置类也是容器中的一个组件 ====
 +
修改 MainApplication 运行、验证<syntaxhighlight lang="java">
 +
/**
 +
* 主程序类
 +
* @SpringBootApplication:这是一个 Spring Boot 应用
 +
*/
 +
@SpringBootApplication(scanBasePackages = "io.github.jihch")
 +
public class MainApplication {
 +
 +
    public static void main(String[] args) {
 +
        //1、返回 IOC 容器
 +
        ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
 +
 +
        //2、查看容器里面的组件
 +
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
 +
        Arrays.sort(beanDefinitionNames);
 +
        for (String name : beanDefinitionNames) {
 +
            System.out.println(name);
 +
        }
 +
 +
        //3、从容器中获取组件
 +
        Pet tom01 = context.getBean("tom", Pet.class);
 +
 +
        Pet tom02 = context.getBean("tom", Pet.class);
 +
 +
        System.out.println("组件:" + (tom01 == tom02));
 +
 +
        //4、io.github.jihch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$f0fca2ff@26c89563
 +
        MyConfig bean = context.getBean(MyConfig.class);
 +
 +
        System.out.println(bean);
 +
 +
    }
 +
 +
}
 +
</syntaxhighlight><syntaxhighlight lang="console">
 +
……
 +
io.github.jihch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$f0fca2ff@26c89563
 +
 +
</syntaxhighlight>
 +
 +
 +
 +
==== SpringBoot 2.x 使用的 spring-context-5.2 中为 @Configuration 新增的 proxyBeanMethods ====
 +
SpringBoot 2.x 使用的 spring-context-5.2 中为 @Configuration 新增了一个属性:proxyBeanMethods,默认值是 true
 +
 +
===== 配置类里注册的方法,在外面调用多少遍获得的始终是容器中的实例 =====
 +
修改 MainApplication 运行、验证<syntaxhighlight lang="java">
 +
@SpringBootApplication(scanBasePackages = "io.github.jihch")
 +
public class MainApplication {
 +
 +
    public static void main(String[] args) {
 +
        //1、返回 IOC 容器
 +
        ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
 +
 +
        //2、查看容器里面的组件
 +
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
 +
        Arrays.sort(beanDefinitionNames);
 +
        for (String name : beanDefinitionNames) {
 +
            System.out.println(name);
 +
        }
 +
 +
        //3、从容器中获取组件
 +
        Pet tom01 = context.getBean("tom", Pet.class);
 +
 +
        Pet tom02 = context.getBean("tom", Pet.class);
 +
 +
        System.out.println("组件:" + (tom01 == tom02));
 +
 +
        //4、io.github.jihch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$f0fca2ff@26c89563
 +
        MyConfig bean = context.getBean(MyConfig.class);
 +
 +
        System.out.println(bean);
 +
       
 +
        //如果 @Configuration(proxyBeanMethods = true) 代理对象调用方法。
 +
        //SpringBoot 总会检查这个组件是否在容器中有,保持组件单实例
 +
        User user = bean.user01();
 +
 +
        User user1 = bean.user01();
 +
 +
        System.out.println(user == user1);
 +
 +
    }
 +
 +
}
 +
 +
</syntaxhighlight><syntaxhighlight lang="console">
 +
……
 +
true
 +
 +
</syntaxhighlight>
 +
 +
 +
 +
应用了 @Configuration 注解的 MyConfig 本身不再是一个普通对象,它是一个被 SpringCGLIB 增加强了的对象:
 +
 +
<code>io.github.jihch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$f0fca2ff@26c89563</code>
 +
 +
 +
 +
 +
 +
==== proxyBeanMethods = false ====
 +
修改 MyConfig 为<syntaxhighlight lang="java">
 +
@Configuration(proxyBeanMethods = false)
 +
public class MyConfig {
 +
</syntaxhighlight>再运行 MainApplication<syntaxhighlight lang="java">
 +
……
 +
        MyConfig bean = context.getBean(MyConfig.class);
 +
 +
        System.out.println(bean);
 +
 +
        //如果 @Configuration(proxyBeanMethods = true) 代理对象调用方法。
 +
        //SpringBoot 总会检查这个组件是否在容器中有,保持组件单实例
 +
        User user = bean.user01();
 +
 +
        User user1 = bean.user01();
 +
 +
        System.out.println(user == user1);
 +
……
 +
 +
</syntaxhighlight><syntaxhighlight lang="console">
 +
……
 +
io.github.jihch.boot.config.MyConfig@5e0ec41f
 +
false
 +
</syntaxhighlight>发现 MyConfig 不再是代理对象,并且两次调用同一个方法得到的也不是同一个对象。
 +
 +
 +
 +
==== Full 全配置 和 Lite 轻量级配置 ====
 +
 +
===== Full(proxyBeanMethods = true) =====
 +
 +
===== Lite(proxyBeanMethods = false) 配置在容器中再也不会保存代理对象,在外部调用配置类的方法,每一次都会产生一个新的对象 =====
 +
 +
 +
修改 MyConfig 说明 proxyBeanMethods 属性作用:<syntaxhighlight lang="java">
 +
    @Bean
 +
    public User user01() {
 +
        User zhangsan = new User("zhangsan", 18);
 +
        zhangsan.setPet(tomcatPet());
 +
        return zhangsan;
 +
    }
 +
 +
    @Bean("tom")
 +
    public Pet tomcatPet() {
 +
        return new Pet("tomcat");
 +
    }
 +
</syntaxhighlight>proxyBeanMethods = true,user01 方法产生的 User 类的 bean,每次 setPet(tomcatPet()) 的都是同一个容器里的对象;
 +
 +
proxyBeanMethods = false,则 user01 方法产生的 User 类的 bean,每次 setPet(tomcatPet()) 的都是不同的对象;
 +
 +
 +
配置类组件之间无依赖关系用 Lite 模式加速容器启动过程,减少判断
 +
 +
配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用 Full 模式

2023年2月12日 (日) 11:03的最新版本

https://www.bilibili.com/video/BV19K4y1L7MT/?p=8

回顾旧版本通过 XML 配置文件进行 bean 配置

Spring XML 配置文件模板


@Configuration 的作用

告诉 Spring Boot 添加了 @Configuration 注解的类,是一个配置类,作用等同于以前的 XML 配置文件。

使用 @Bean 往容器中注册组件

@Configuration //告诉 SpringBoot 这是一个配置类 == 配置文件
public class MyConfig {

    /**
     * 给容器中添加组件。
     * 以方法名作为组件的id。
     * 返回类型就是组件类型。
     * 返回的值,就是组件在容器中的实例
     *
     * 外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
     * @return
     */
    @Bean
    public User user01() {
        return new User("zhangsan", 18);
    }

    @Bean
    public Pet tomcatPet() {
        return new Pet("tomcat");
    }

}

运行 MainApplication:

/**
 * 主程序类
 * @SpringBootApplication:这是一个 Spring Boot 应用
 */
@SpringBootApplication(scanBasePackages = "io.github.jihch")
public class MainApplication {

    public static void main(String[] args) {
        //1、返回 IOC 容器
        ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);

        //2、查看容器里面的组件
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        Arrays.sort(beanDefinitionNames);
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }

    }

}
……
spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties
spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties
standardJacksonObjectMapperBuilderCustomizer
stringHttpMessageConverter
taskExecutorBuilder
taskSchedulerBuilder
tomcatPet
tomcatServletWebServerFactory
tomcatServletWebServerFactoryCustomizer
tomcatWebServerFactoryCustomizer
user01
viewControllerHandlerMapping
viewResolver
webServerFactoryCustomizerBeanPostProcessor
websocketServletWebServerCustomizer
welcomePageHandlerMapping
worldController
……

可以观察到容器中多了 tomcatPet 和 user01 两个 bean,默认使用方法名作为组件名,也可以通过 @Bean("tom") 自定义组件名,再运行 MainApplication:

……
spring.resources-org.springframework.boot.autoconfigure.web.ResourceProperties
spring.servlet.multipart-org.springframework.boot.autoconfigure.web.servlet.MultipartProperties
spring.task.execution-org.springframework.boot.autoconfigure.task.TaskExecutionProperties
spring.task.scheduling-org.springframework.boot.autoconfigure.task.TaskSchedulingProperties
standardJacksonObjectMapperBuilderCustomizer
stringHttpMessageConverter
taskExecutorBuilder
taskSchedulerBuilder
tom
tomcatServletWebServerFactory
tomcatServletWebServerFactoryCustomizer
tomcatWebServerFactoryCustomizer
user01
viewControllerHandlerMapping
viewResolver
webServerFactoryCustomizerBeanPostProcessor
websocketServletWebServerCustomizer
welcomePageHandlerMapping
worldController
……

可以发现组件名就变成自定义的组件名了。


默认注册的组件是单实例的,无论从容器中获取多少次都是一样的

修改 MainApplication 并运行、验证

/**
 * 主程序类
 * @SpringBootApplication:这是一个 Spring Boot 应用
 */
@SpringBootApplication(scanBasePackages = "io.github.jihch")
public class MainApplication {

    public static void main(String[] args) {
        //1、返回 IOC 容器
        ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);

        //2、查看容器里面的组件
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        Arrays.sort(beanDefinitionNames);
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }

        //3、从容器中获取组件
        Pet tom01 = context.getBean("tom", Pet.class);

        Pet tom02 = context.getBean("tom", Pet.class);

        System.out.println("组件:" + (tom01 == tom02));

    }

}
……
组件:true



@Configuration 注解的配置类也是容器中的一个组件

修改 MainApplication 运行、验证

/**
 * 主程序类
 * @SpringBootApplication:这是一个 Spring Boot 应用
 */
@SpringBootApplication(scanBasePackages = "io.github.jihch")
public class MainApplication {

    public static void main(String[] args) {
        //1、返回 IOC 容器
        ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);

        //2、查看容器里面的组件
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        Arrays.sort(beanDefinitionNames);
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }

        //3、从容器中获取组件
        Pet tom01 = context.getBean("tom", Pet.class);

        Pet tom02 = context.getBean("tom", Pet.class);

        System.out.println("组件:" + (tom01 == tom02));

        //4、io.github.jihch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$f0fca2ff@26c89563
        MyConfig bean = context.getBean(MyConfig.class);

        System.out.println(bean);

    }

}
……
io.github.jihch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$f0fca2ff@26c89563


SpringBoot 2.x 使用的 spring-context-5.2 中为 @Configuration 新增的 proxyBeanMethods

SpringBoot 2.x 使用的 spring-context-5.2 中为 @Configuration 新增了一个属性:proxyBeanMethods,默认值是 true

配置类里注册的方法,在外面调用多少遍获得的始终是容器中的实例

修改 MainApplication 运行、验证

@SpringBootApplication(scanBasePackages = "io.github.jihch")
public class MainApplication {

    public static void main(String[] args) {
        //1、返回 IOC 容器
        ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);

        //2、查看容器里面的组件
        String[] beanDefinitionNames = context.getBeanDefinitionNames();
        Arrays.sort(beanDefinitionNames);
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }

        //3、从容器中获取组件
        Pet tom01 = context.getBean("tom", Pet.class);

        Pet tom02 = context.getBean("tom", Pet.class);

        System.out.println("组件:" + (tom01 == tom02));

        //4、io.github.jihch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$f0fca2ff@26c89563
        MyConfig bean = context.getBean(MyConfig.class);

        System.out.println(bean);
        
        //如果 @Configuration(proxyBeanMethods = true) 代理对象调用方法。
        //SpringBoot 总会检查这个组件是否在容器中有,保持组件单实例
        User user = bean.user01();

        User user1 = bean.user01();

        System.out.println(user == user1);

    }

}
……
true


应用了 @Configuration 注解的 MyConfig 本身不再是一个普通对象,它是一个被 SpringCGLIB 增加强了的对象:

io.github.jihch.boot.config.MyConfig$$EnhancerBySpringCGLIB$$f0fca2ff@26c89563



proxyBeanMethods = false

修改 MyConfig 为

@Configuration(proxyBeanMethods = false)
public class MyConfig {

再运行 MainApplication

……
        MyConfig bean = context.getBean(MyConfig.class);

        System.out.println(bean);

        //如果 @Configuration(proxyBeanMethods = true) 代理对象调用方法。
        //SpringBoot 总会检查这个组件是否在容器中有,保持组件单实例
        User user = bean.user01();

        User user1 = bean.user01();

        System.out.println(user == user1);
……
……
io.github.jihch.boot.config.MyConfig@5e0ec41f
false

发现 MyConfig 不再是代理对象,并且两次调用同一个方法得到的也不是同一个对象。


Full 全配置 和 Lite 轻量级配置

Full(proxyBeanMethods = true)
Lite(proxyBeanMethods = false) 配置在容器中再也不会保存代理对象,在外部调用配置类的方法,每一次都会产生一个新的对象

修改 MyConfig 说明 proxyBeanMethods 属性作用:

    @Bean
    public User user01() {
        User zhangsan = new User("zhangsan", 18);
        zhangsan.setPet(tomcatPet());
        return zhangsan;
    }

    @Bean("tom")
    public Pet tomcatPet() {
        return new Pet("tomcat");
    }

proxyBeanMethods = true,user01 方法产生的 User 类的 bean,每次 setPet(tomcatPet()) 的都是同一个容器里的对象;

proxyBeanMethods = false,则 user01 方法产生的 User 类的 bean,每次 setPet(tomcatPet()) 的都是不同的对象;


配置类组件之间无依赖关系用 Lite 模式加速容器启动过程,减少判断

配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用 Full 模式