說明
Spring或Spring Boot中我們經常會用到很多@EnableXXX 註解,加上這些註解之後我們就可以 ‘啟用’ 某一個功能,或者可以使用某些Bean,比如:
@EnableAsync 使用異步執行、
@EnableTransactionManagement 啟用事務、
@EnableAutoConfiguration 開啟自動裝配
等等,那麼你知道背後的原理是什麼樣的嗎?本文簡要窺探一下。
原理
無論是Spring內建的,還是我們自定義的@Enable 模塊,基本就3種實現方式,其目標都是將指定的類定位為Spring Bean:
- 導入類為@Configuration Class。
- 導入類為 ImportSelector實現;
- 導入類為 ImportBeanDefinitionRegistrar實現;
實例1:
引導類為 @Configuration ,聲明Enable註解
<code>@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(HelloworldConfiguration.class) public @interface EnableHelloWorld { }/<code>
@Configuration Class中定義Bean
@Configuration
public class HelloworldConfiguration {
@Bean
public String helloWorld() {
return "Hello, world";
}
}
開啟EnableHelloWorld註解,獲取 helloWorld Bean
<code>@Configuration@EnableHelloWorldpublic class EnableHelloWorldBootstrap { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); // 註冊當前引導類(被@Configuration標註)到Spring上下文 context.register(EnableHelloWorldBootstrap.class); // 啟動上下文 context.refresh(); // 獲取Bean String helloWorld = context.getBean("helloWorld", String.class); System.out.printf("helloWorld = %s \n", helloWorld); // 關閉上下文 context.close(); } }/<code>
執行結果:
helloWorld = Hello, world
實例2:導入類為 ImportSelector實現
<code>public interface Server { void start(); void close(); enum ServerType { HTTP, TCP } }/<code>
<code>@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ServerImportSelector.class) public @interface EnableServer { Server.ServerType type(); } /<code>
根據註解屬性,選擇要定義的Bean
<code>public class ServerImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { Map attributes = annotationMetadata.getAnnotationAttributes(EnableServer.class.getName()); Server.ServerType serverType = (Server.ServerType)attributes.get("type"); String[] importClassNames = new String[0]; switch (serverType) { case HTTP: importClassNames = new String[]{HttpServer.class.getName()}; break; case TCP: importClassNames = new String[]{TcpServer.class.getName()}; break; } return importClassNames; } }/<code>
啟用Server,獲取對應的功能:
<code>@Configuration @EnableServer(type = Server.ServerType.TCP) public class EnableServerBootsrap { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(); context.register(EnableServerBootsrap.class); context.refresh(); // 獲取Bean Server server = context.getBean(Server.class); // 啟用功能 server.start(); server.close(); context.close(); } }/<code>
執行結果:
TcpServer start ...
TcpServer close ....
實例3:
導入類為 ImportBeanDefinitionRegistrar實現
<code>@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(ServerImportBeanDefinitionRegistrar.class) public @interface EnableServer { Server.ServerType type(); }/<code>
註冊Bean
<code>public class ServerImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar { @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { ImportSelector importSelector = new ServerImportSelector(); // 篩選Class名稱集合 String[] selectedClassNames = importSelector.selectImports(importingClassMetadata); // 創建Bean定義 Stream.of(selectedClassNames) // 轉化為BeanDefinitionBuilder對象 .map(BeanDefinitionBuilder::genericBeanDefinition) // 轉化為BeanDefinition .map(BeanDefinitionBuilder::getBeanDefinition) .forEach(beanDefinition -> { // 註冊BeanDefinition到BeanDefinitionRegistry BeanDefinitionReaderUtils.registerWithGeneratedName(beanDefinition, registry); }); } }/<code>
啟用Server,獲取對應的功能:見實例2。
內建模塊
Spring 內建模塊
<code>@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(AsyncConfigurationSelector.class) public @interface EnableAsync { }/<code>
Dubbo 內建模塊
<code>@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited@Documented @Import(DubboConfigConfigurationRegistrar.class) public @interface EnableDubboConfig { }/<code>
具體實現可以看下源碼,都是相同的原理。