SpringBoot的@Enable* 註解的工作原理
阿新 • • 發佈:2019-02-01
SpringBoot 提供了@EnableAutoConfiguration
、@EnableConfigurationProperties
、@EnableAsync
等註解用來啟用某些特性。
工作原理
每個以 Enable
開頭的註解中,都有 @Import
註解,這個註解是用來匯入一個或者多個類(由Spring
管理),或者配置類(配置類中的bean
由Spring
管理),因此 @Import
可以代替 @Component
和 @Configuration
等註解。
在 Import
原始碼中定義了Class
陣列的value
屬性,官方註釋是可以將帶有Configuration
ImportSelector
、ImportBeanDefinitionRegistrar
介面的類,還有其他符合要求的class
交給Spring
管理。 也就是說,
SpringBoot
的@Enable*
的實現是在@Import
註解中指定需要的配置類,包括帶有@Configuration
的類,實現了ImportSelector
或者ImportBeanDefinitionRegistrar
介面的類並在實現的方法中將需要的Bean
註冊到Spring
容器中。
例如:
@EnableAutoConfiguration 的配置類中是實現了 ImportSelector 介面
@EnableConfigurationProperties 的配置類中實現了 ImportSelector 介面
@EnableAsync 配置類中實現了 ImportSelector 介面
@EnableWebMvc 的配置類中使用了 @Configuration 註解
與配置啟用相關的類和註解
Import
註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {
Class<?>[] value();
}
ImportSelector
介面
public interface ImportSelector {
/**
* 實現此方法,Spring 會將所有返回值註冊到容器中
*/
String[] selectImports(AnnotationMetadata importingClassMetadata);
}
ImportBeanDefinitionRegistrar
介面
public interface ImportBeanDefinitionRegistrar {
/**
* 實現此方法,需要手動將 Bean 註冊到 Spring 容器中
*/
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry);
}
案例
ImportSelector
案例
// 註解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(value = BeanImportSelector.class)
public @interface EnableBean {
}
// 準備兩個類
public class UserDto {
}
public class UserVo {
}
// 配置類
public class BeanConfiguration {
@Bean
public UserDto userDto1(){
return new UserDto();
}
@Bean
public UserVo userVo1(){
return new UserVo();
}
}
// ImportSelector 實現類
public class BeanImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 將 bean 注入到 spring 容器
return new String[] { UserVo.class.getName(), "com.p7.boot.enable.test.dto.UserDto",
BeanConfiguration.class.getName() };
}
}
// 啟動類
@SpringBootApplication
// 啟動特性
@EnableBean
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
System.out.println(context.getBeansOfType(UserDto.class));
System.out.println(context.getBeansOfType(UserVo.class));
context.close();
}
}
ImportBeanDefinitionRegistrar
案例,繼續使用上面的案例
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(value = LogImportBeanDefinitionRegistrar.class)
public @interface EnableLog {
String[] packages();
}
// 啟動 @EnableLog 特性,在載入 Bean 時,根據包名列印日誌
public class LogBeanPostProcessor implements BeanPostProcessor {
List<String> packageList;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 遍歷 LogImportBeanDefinitionRegistrar 穿過來的所有包名
for (String packageName : packageList) {
if (bean.getClass().getName().startsWith(packageName)) {
System.out.println(bean.getClass().getName() + " Log ...");
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public List<String> getPackageList() {
return packageList;
}
public void setPackageList(List<String> packageList) {
this.packageList = packageList;
}
}
// 實現 ImportBeanDefinitionRegistrar 介面
public class LogImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
// 獲取 EnableLog 的所有屬性
Map<String, Object> attr = importingClassMetadata.getAnnotationAttributes(EnableLog.class.getName());
// 得到 packages 屬性所有的值
String[] packages = (String[]) attr.get("packages");
List<String> packageList = Arrays.asList(packages);
// 生成 LogBeanPostProcessor 物件,並將所有包含的包傳給該物件
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(LogBeanPostProcessor.class.getName());
builder.addPropertyValue("packageList", packageList);
// 將 LogBeanPostProcessor 物件註冊到 Spring 中
registry.registerBeanDefinition(LogBeanPostProcessor.class.getName(), builder.getBeanDefinition());
}
}
// 啟動類
@SpringBootApplication
@EnableBean
// 配置 com.p7.boot.enable.test.vo 下所有的類註冊到 Spring 容器前,列印日誌
@EnableLog(packages = "com.p7.boot.enable.test.vo")
public class App {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(App.class, args);
context.close();
}
}