接上篇《SpringBoot实战9-Spring基础-条件配置@Condtional》我们学习了条件注解@Conditonal的使用,本篇我们将学习开启注解@Enable*和导入注解@Import的使用。
![c3c9d79ea47543f570bd63958f233f62.png](https://img-blog.csdnimg.cn/img_convert/c3c9d79ea47543f570bd63958f233f62.png)
2.9 开启配置 - @Enable*和@Import
在后面的内容里会出现大量以@Enable*开头的注解,通过@Enable*将会自动对相应的功能进行自动配置。如:@EnableWebMvc、@EnableCaching、@EnableScheduling、@EnableAsync、@EnableWebSocket、EnableJpaRepositories、@EnableTransactionManagement、@EnableJpaAuditing、@EnableAspectJAutoProxy等。
而这开启配置的功能是由@Import注解提供的,@Import注解支持导入如下的配置:
- 直接导入@Configuration配置类;
- 配置类选择器ImportSelector的实现;
- 动态注册器ImportBeanDefinitionRegistrar的实现。
- 混合以上三种
下面我们将分别演示三种方式的实现:
2.9.1 直接导入@Configuration配置类
应用了注解了@Configuration都会被Spring Boot的默认组件扫描自动注册,所以我们将本节的注解类代码放入io.github.wiselyman.annotations包中,配置类的代码放入io.github.wiselyman.config中:
定义注解:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(AConfig.class) //直接导入配置类AConfig.classpublic @interface EnableA {}
定义配置类:
@Configurationpublic class AConfig { @Bean public String a(){ return "A"; }}
在JavaConfig中使用@EnableA注解,即可获得导入的配置类AConfig中的Bean a:
@EnableApublic class JavaConfig {}
在JavaConfig中使用CommandLineRunner执行:
@BeanCommandLineRunner enableAClr(String a){ return args -> System.out.println(a);}
![577d9521a6cfb3a9f4e82f8f225b0428.png](https://img-blog.csdnimg.cn/img_convert/577d9521a6cfb3a9f4e82f8f225b0428.png)
2.9.2 配置类选择器ImportSelector的实现
在这个例子里,我们通过注解选择起效的配置类,注解定义如下:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(BSelector.class) public @interface EnableB { boolean isUppercase() default true; //isUppercase是用来作为选择条件的}
在io.github.wiselyman.selector里定义选择器:
public class BSelector implements ImportSelector { //1 @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { //2 AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes (EnableB.class.getName(), false)); boolean isUppercase = attributes.getBoolean("isUppercase"); //3 if(isUppercase == true) return new String[]{"io.github.wiselyman.config.BUppercaseConfig"}; //4 else return new String[]{"io.github.wiselyman.config.BLowercaseConfig"}; //5 }}
- 选择器要实现ImportSelector接口;
- 实现接口的selectImports方法,参数AnnotationMetadata importClassMetadata是注解使用类(本例为JavaConfig)上@EnableB的元数据信息;
- 通过@EnableB在实际使用中的元数据,获得isUppercase的值;
- 如果isUppercase == true,此时实际使用时是@EnableB或者@EnableB(isUppercase = true)使用BUppercaseConfig提供的配置;
- 若实际使用时@EnableB(isUppercase = false)否则使用BLowercaseConfig提供的配置。
BUppercaseConfig的定义:
@Configurationpublic class BUppercaseConfig { @Bean public String b(){ return "B"; //返回一个大写B的Bean的配置 }}
BLowercaseConfig的定义:
@Configurationpublic class BLowercaseConfig { @Bean public String b(){ return "b"; //返回一个小写b的Bean的配置 }}
在JavaConfig中使用@EnableB,并用CommandLineRunner检验:
@EnableBpublic class JavaConfig {}
@BeanCommandLineRunner enbaleBClr(String b){ return args -> System.out.println(b);}
![1529b4dfd845a1293a6427cf817582fd.png](https://img-blog.csdnimg.cn/img_convert/1529b4dfd845a1293a6427cf817582fd.png)
若将isUppercase设置为false,如:
@EnableB(isUppercase = false)public class JavaConfig {}
![d749f58ff946295d48c6b1077f66cd20.png](https://img-blog.csdnimg.cn/img_convert/d749f58ff946295d48c6b1077f66cd20.png)
2.9.3 动态注册器ImportBeanDefinitionRegistrar的实现
本例通过ImportBeanDefinitionRegistrar可动态注册Bean到容器里。
注解定义:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import(CBeanDefinitionRegistrar.class)public @interface EnableC {}
在io.github.wiselyman.registrar包中定义注册器:
public class CBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { //1 @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, //2 BeanDefinitionRegistry registry) { //3 BeanDefinition bd = BeanDefinitionBuilder.genericBeanDefinition(String.class) //4 .addConstructorArgValue("C") //5 .setScope(BeanDefinition.SCOPE_SINGLETON) //6 .getBeanDefinition(); //7 registry.registerBeanDefinition("c",bd); //8 }}
- 注册器需实现ImportBeanDefinitionRegistrar接口;
- 实现registerBeanDefinitions,参数AnnotationMetadata importClassMetadata是注解使用类(本例为JavaConfig)上@EnableB的元数据信息;
- 参数BeanDefinitionRegistry registry是用来注册所有Bean的定义的接口;
- 我们可以使用BeanDefinitionBuilder来编程式实现Bean的定义(BeanDefinition),此句定义了一个类型为String的Bean;
- 构造String的值是C;
- 设置Bean的Scope是singleton;
- 获得Bean的定义;
- 将Bean注册为名称为c的Bean。
此时我们在JavaConfig上使用@EnableC,并用CommandLineRunner检验:
@EnableCpublic class JavaConfig {}
@BeanCommandLineRunner enableCClr(String c){ return args -> System.out.println(c);}
静态注册的Bean,IntelliJ IDEA可以检测到,而动态注册的检测不到,所以这时候IDE会标红,但是正常运行:
![6fab532f3ab2126f8020a5413cab733f.png](https://img-blog.csdnimg.cn/img_convert/6fab532f3ab2126f8020a5413cab733f.png)
![234cfbca9f3fa4e400ac1355eda351b1.png](https://img-blog.csdnimg.cn/img_convert/234cfbca9f3fa4e400ac1355eda351b1.png)
2.9.4 混合使用
@Import支持导入配置类的数组,我们可以混合上面三种配置,如我们可以定义一个注解具备上面三个的功能:
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)@Documented@Import({AConfig.class, BForABCSelector.class, CBeanDefinitionRegistrar.class})public @interface EnableABC { boolean isUppercase() default true;}
选择器里指定了使用的注解的类,所以要新建一个新选择器:
public class BForABCSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { AnnotationAttributes attributes = AnnotationAttributes.fromMap( importingClassMetadata.getAnnotationAttributes (EnableABC.class.getName(), false)); // 此处使用的是@EnableABC boolean isUppercase = attributes.getBoolean("isUppercase"); if(isUppercase == true) return new String[]{"io.github.wiselyman.config.BUppercaseConfig"}; else return new String[]{"io.github.wiselyman.config.BLowercaseConfig"}; }}
我们在JavaConfig中启用@EnableABC并用CommandLineRunner检验:
@EnableABCpublic class JavaConfig {}
@BeanCommandLineRunner enableABCClr(String a, String b, String c){ return args -> { System.out.println(a); System.out.println(b); System.out.println(c); };}
![e526e0685c6d720dd625a93df1bd94f6.png](https://img-blog.csdnimg.cn/img_convert/e526e0685c6d720dd625a93df1bd94f6.png)
下一篇《SpringBoot实战11-Spring基础-全局Bean处理BeanPostProcessor》