Spring @Enable 模塊驅動原理

說明

Spring或Spring Boot中我們經常會用到很多@EnableXXX 註解,加上這些註解之後我們就可以 ‘啟用’ 某一個功能,或者可以使用某些Bean,比如:

@EnableAsync 使用異步執行、

@EnableTransactionManagement 啟用事務、

@EnableAutoConfiguration 開啟自動裝配

等等,那麼你知道背後的原理是什麼樣的嗎?本文簡要窺探一下。

原理

無論是Spring內建的,還是我們自定義的@Enable 模塊,基本就3種實現方式,其目標都是將指定的類定位為Spring Bean:

  1. 導入類為@Configuration Class。
  2. 導入類為 ImportSelector實現;
  3. 導入類為 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>

具體實現可以看下源碼,都是相同的原理。


分享到:


相關文章: