@enable*是springboot中用來啟用某一個功能特性的一類注解。其中包括我們常用的
@SpringBootApplication注解中用于開啟自動注入的annotation@EnableAutoConfiguration,開啟異步方法的annotation@EnableAsync,開啟將配置文件中的屬性以bean的方式注入到IOC容器的annotation@EnableConfigurationProperties等。
一、觀察任一@Enable*注解的源碼,以@EnableAsync為例

@EnableAsync的作用是啟用異步執(zhí)行,使標注@Async注解的方法能夠和其他方法異步執(zhí)行。讀者可以Google一下@EnableAsync這個注解的使用場景,本文不再贅述
我們發(fā)現(xiàn),這個注解的重點在我標紅的@Import({AsyncConfigurationSelector.class})這段代碼。解釋一下@Import和XxxSelector.class的作用。
1)@Import
用來導入一個或多個class,這些類會注入到spring容器中,或者配置類,配置類里面定義的bean都會被spring容器托管。在這里我們加入的AsyncConfigurationSelector.class放入Spring容器中管理。
2)AsyncConfigurationSelector.class
我們從源碼一直追溯這個類的父類,最終找到頂端的父類ImportSelector.class

打開
ImportSelector.class閱讀源碼:
Spring會把實現(xiàn)ImportSelector接口的類中的SelectImport方法返回的值注入到Spring容器中。這個方法的返回值必須是一個class的全類名的String[]。舉個例子:
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{"com.springboot.enable.User", "com.springboot.enable.Car"};
}
}
spring容器會把com.springboot.enable包下的User和Car這兩個類放入容器中。
回歸正題,我們不是需要通過@Enable*注解開啟一些功能嘛?答案就我自定義的MyImportSelector中。簡單來說就是@Enable*會將XxxImportSelector放入容器中,當Spring啟動,會執(zhí)行selectImports(AnnotationMetadata annotationMetadata)方法,在這個方法中我們做了某些處理,使得和@Enable*搭配使用的注解生效。哈哈,是不是很繞,多閱讀兩遍,你就理解了。
還有一個和ImportSelector功能差不多的類,ImportBeanDefinitionRegistrar使用beanDefinitionRegistry對象將bean加入Spring容器中,源碼如下:

二、小實驗:
下面我們做一個小實驗印證一下,下圖有三個包,每個包下分別有三個bean,他們都加了@Component注解,會被spring加入到容器中。




需求是,當注入dto和vo兩個包下的bean時,輸出一段話:echo bean :+ bean的全類名,注入entity包下的bean時,不輸出。
1)
創(chuàng)建EchoBeanPostProcessor.class,實現(xiàn)BeanPostProcessor接口,作用是實現(xiàn)上文的業(yè)務邏輯。我們同樣可以創(chuàng)建一個@EchoBean,然后通過AOP的方式實現(xiàn)。
//實現(xiàn)BeanPostProcessor接口的類,放入spring容器中后,容器啟動和關閉時會執(zhí)行以下兩個重寫的方法
public class EchoBeanPostProcessor implements BeanPostProcessor {
//getter、setter省略,讀者在試驗的時候要加上
private List<String> packages;
//該方法在spring容器初始化前執(zhí)行
@Override
public Object postProcessBeforeInitialization(Object bean, String s) throws BeansException {
for (String pack : packages) {
if (bean.getClass().getName().startsWith(pack)) {
System.out.println("echo bean: " + bean.getClass().getName());
}
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String s) throws BeansException {
return bean;
}
}
2)
創(chuàng)建BamuImportBeanDefinitionRegistrar .class,實現(xiàn)ImportBeanDefinitionRegistrar
public class BamuImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
//獲取EnableEcho注解的所有屬性的value
Map<String, Object> attributes = annotationMetadata.getAnnotationAttributes(EnableEcho.class.getName());
//獲取package屬性的value
List<String> packages = Arrays.asList((String[]) attributes.get("packages"));
//使用beanDefinitionRegistry對象將EchoBeanPostProcessor注入至Spring容器中
BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.rootBeanDefinition(EchoBeanPostProcessor.class);
//給EchoBeanPostProcessor.class中注入packages
beanDefinitionBuilder.addPropertyValue("packages", packages);
beanDefinitionRegistry.registerBeanDefinition(EchoBeanPostProcessor.class.getName(), beanDefinitionBuilder.getBeanDefinition());
}
}
3)
創(chuàng)建注解@EnableEcho,ImportBamuImportBeanDefinitionRegistrar.class
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import({BamuImportBeanDefinitionRegistrar.class})
public @interface EnableEcho {
//傳入包名
String[] packages() default "";
}
4)
在springboot啟動類中加入我們創(chuàng)建的注解,并傳入指定的包名,執(zhí)行main方法:
@SpringBootApplication
@EnableEcho(packages = {"com.springboot.vo", "com.springboot.dto"})
public class BlogApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(BlogApplication.class, args);
context.close();
}
}
控制臺輸出結(jié)果:只有dto和vo包下的bean初始化時輸出,entity包下的bean初始化時沒有輸出,試驗成功。

以上就是springboot @Enable*注解的工作原理,如有錯誤還請讀者告知,感謝!