Configuration

  1. 配置类其实相当于一个工厂, 标注 @Bean 注解的方法相当于工厂方法
  2. @Bean 不支持方法重载, 如果有多个重载方法, 仅有一个能入选为工厂方法
  3. @Configuration 默认会为标注的类生成代理, 其目的是保证 @Bean 方法相互调用时, 仍然能保证其单例特性
  4. @Configuration 中如果含有 BeanFactory 后处理器, 则实例工厂方法会导致 MyConfig 提前创建, 造成其依赖注入失败,
    1. 解决方法
      1. 改用静态工厂方法
      2. 直接为 @Bean 的方法参数依赖注入, 针对 Mapper 扫描可以改用注解方式
      3. 对于 MapperScannerConfigurer 后处理器还可以用 @MapperScan 注解

🧑🏻‍💻 为什么 @Configuration 配置类中如果有后处理器,为导致配置类提前创建,造成其他依赖注入失败?

  1. 正常情况下,@Configuration 配置类会在 Spring 初始化 refresh() 中第十一步才会创建;
  2. 由于配置类中加入了后处理器,后处理器初始化是在 refresh() 的第五步,此时后处理器是一个成员方法,要想使用成员方法,要提前创建配置类对象,所以会提前创建配置类,导致配置类其他依赖注入失败(使用成员变量的时候,如:@Value 解析在第十一步才执行);

🧑🏻‍💻 为什么后处理器使用 static 修饰,配置类就不会提前创建?

  1. 使用 static 修饰后处理器对象,在创建处理器对象时,通过类名.方法名使用,这时并不会创建配置类对象;
  2. 而使用局部变量的方法时,在 refresh() 第一步才开始解析如 @Value 注解,完成配置类对象的创建,完成依赖注入;
  1. public class TestConfiguration {
  2. public static void main(String[] args) {
  3. GenericApplicationContext context = new GenericApplicationContext();
  4. AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
  5. context.registerBean("myConfig", MyConfig.class);
  6. context.refresh();
  7. // 注意点3: @Configuration 默认会为标注的类生成代理, 其目的是保证 @Bean 方法相互调用时, 仍然能保证其单例特性
  8. // System.out.println(context.getBean(MyConfig.class).getClass());
  9. }
  10. @Configuration
  11. @MapperScan("aaa") // 注意点4-解决方法
  12. // 注意点1: 配置类其实相当于一个工厂, 标注 @Bean 注解的方法相当于工厂方法 - debug 的时候可以看到
  13. static class MyConfig {
  14. // 注意点2: @Bean 不支持方法重载, 如果有多个重载方法, 仅有一个能入选为工厂方法
  15. // 注意点3: @Configuration 默认会为标注的类生成代理, 其目的是保证 @Bean 方法相互调用时, 仍然能保证其单例特性
  16. /*@Bean
  17. public Bean1 bean1() {
  18. System.out.println("bean1()");
  19. System.out.println(bean2());
  20. System.out.println(bean2());
  21. System.out.println(bean2());
  22. return new Bean1();
  23. }*/
  24. /*
  25. 测试:注意点2: @Bean 不支持方法重载, 如果有多个重载方法, 仅有一个能入选为工厂方法
  26. @Bean
  27. public Bean1 bean1(@Value("${java.class.version}") String a) {
  28. System.out.println("bean1(" + a + ")");
  29. return new Bean1();
  30. }
  31. @Bean
  32. public Bean1 bean1(@Value("${java.class.version}") String a, @Value("${JAVA_HOME}") String b) {
  33. System.out.println("bean1(" + a + ", " + b + ")");
  34. return new Bean1();
  35. }*/
  36. /*@Bean
  37. public Bean2 bean2() {
  38. System.out.println("bean2()");
  39. return new Bean2();
  40. }*/
  41. // 注意点4: @Configuration 中如果含有 BeanFactory 后处理器, 则实例工厂方法会导致 MyConfig 提前创建, 造成其依赖注入失败
  42. // 解决方法是该用静态工厂方法或直接为 @Bean 的方法参数依赖注入, 针对 MapperScanner 可以改用注解方式
  43. @Value("${java.class.version}")
  44. private String version;
  45. /*@Bean
  46. public static MapperScannerConfigurer configurer() {
  47. MapperScannerConfigurer scanner = new MapperScannerConfigurer();
  48. scanner.setBasePackage("aaa");
  49. return scanner;
  50. }*/
  51. // 注意点4:解决方法-@Bean 的方法参数依赖注入,
  52. @Bean
  53. public Bean3 bean3(@Value("${java.class.version}") String version) {
  54. System.out.println("bean3() " + version);
  55. return new Bean3();
  56. }
  57. @Bean
  58. public Bean3 bean3() {
  59. System.out.println("bean3() " + version);
  60. return new Bean3();
  61. }
  62. }
  63. static class Bean1 {
  64. }
  65. static class Bean2 {
  66. }
  67. static class Bean3 {
  68. }
  69. }

Configuration 配置类使用模版:将后处理器成员方法用 static 修饰

@Configuration
static class MyConfig {
    @Value("${java.class.version}")
    private String version;

    @Bean
    public static MapperScannerConfigurer configurer() {
        MapperScannerConfigurer scanner = new MapperScannerConfigurer();
        scanner.setBasePackage("aaa");
        return scanner;
    }

    // version 不会失效
    @Bean
    public Bean3 bean3() {
        System.out.println("bean3() " + version);
        return new Bean3();
    }
}

static class Bean3 {

}

import

  • 四种用法
    • 引入单个 bean
    • 引入一个配置类
    • 通过 Selector 引入多个类
    • 通过 beanDefinition 注册器
  • 解析规则
    • 同一配置类中, @Import 先解析 @Bean 后解析
    • 同名定义, 默认后面解析的会覆盖前面解析的
    • 不允许覆盖的情况下, 如何能够让 MyConfig(主配置类) 的配置优先? (虽然覆盖方式能解决)
      • 采用 DeferredImportSelector,因为它最后工作, 可以简单认为先解析 @Bean, 再 Import
public class TestImport {
    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        AnnotationConfigUtils.registerAnnotationConfigProcessors(context.getDefaultListableBeanFactory());
        context.registerBean(MyConfig.class);
        context.refresh();

        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }

    }

    @Configuration
//    @Import(Bean1.class) // 1. 引入单个 bean,先加载 import 后加载 configuration
//    @Import(OtherConfig.class) // 2. 引入一个配置类,先加载 import 后加载 configuration
    @Import(MySelector.class) // 3. 通过 Selector 引入多个类,使得 import 后加载
//    @Import(MyRegistrar.class) // 4. 通过 beanDefinition 注册器
    static class MyConfig {            // --- 程序员编写的主配置

    }

    static class MySelector implements DeferredImportSelector {
        @Override
        public String[] selectImports(AnnotationMetadata importingClassMetadata) {
            return new String[]{Bean3.class.getName(), Bean4.class.getName()};
        }
    }

    static class MyRegistrar implements ImportBeanDefinitionRegistrar {
        @Override
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            registry.registerBeanDefinition("bean5", BeanDefinitionBuilder.genericBeanDefinition(Bean5.class).getBeanDefinition());
        }
    }

    static class Bean5 {

    }

    static class Bean3 {

    }

    static class Bean4 {

    }

    static class Bean1 {

    }

    @Configuration
    static class OtherConfig {        // --- 从属配置
        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean2 {

    }

}