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();
}
}