一、Spring Boot 核心之自動裝配實現

一、簡介和目標

  • 簡介:在 Spring Boot 場景下,基於約定大於配置的原則,實現 Spring 組件自動裝配的目的。
  • 目標:完成一個可通過配置和@EnableXXX 來控制的是否裝配的Bean

二、底層裝配技術簡述

  • Spring 模式註解裝配

  • Spring @Enable 模塊裝配

    • 註解驅動方式 eg:

      @Retention(RetentionPolicy.RUNTIME)
      @Target(ElementType.TYPE) @Documented
      @Import(DelegatingWebMvcConfiguration.class) 
      public @interface EnableWebMvc {
      
      }
      
      @Configuration 
      public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {
       
       }
      
    • 接口編程方式 eg:

      @Target(ElementType.TYPE) 
      @Retention(RetentionPolicy.RUNTIME) 
      @Documented 
      @Import(CachingConfigurationSelector.class) 
      public @interface EnableCaching { 
       
       }
      
      public class CachingConfigurationSelector extends AdviceModeImportSelector<EnableCaching> {
          public String[] selectImports(AdviceMode adviceMode) {
              switch(adviceMode) {
              case PROXY:
                  return this.getProxyImports();
              case ASPECTJ:
                  return this.getAspectJImports();
              default:
                  return null;
              }
          }
       }
      
  • Spring 條件裝配

    • 配置方式 - @Profile,根據不同環境進行裝配
    • 編程方式 - @Conditional
      @Target({ElementType.TYPE, ElementType.METHOD})
      @Retention(RetentionPolicy.RUNTIME)
      @Documented
      @Conditional({OnClassCondition.class})
      public @interface ConditionalOnClass {
          Class<?>[] value() default {};
      
          String[] name() default {};
      }
      
  • Spring 工廠加載機制

    • 實現類: SpringFactoriesLoader
    • 配置資源: META-INF/spring.factories

三、實現

1、激活自動裝配 @EnableAutoConfiguration

項目結構如下
在這裏插入圖片描述
添加核心Bean=>HelloWorld,只有一個hello方法用於測試輸出結果

@Slf4j
public class HelloWorld {
    public void hello(){
        log.info("hello world 2019!");
    }
}

添加HelloWorldConfiguration用於註冊HelloWorld

@Slf4j
public class HelloWorldConfiguration {
    @Bean
    public HelloWorld hello(){
        log.info("Load HelloWorld");
        return new HelloWorld();
    }
}

添加HelloWorldImportSelector實現ImportSelector,通過接口編程方式實現@Enable功能

@Slf4j
public class HelloWorldImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata annotationMetadata) {
        log.info("annotationMetadata.getAnnotationTypes():{}",annotationMetadata.getAnnotationTypes());
        // 此處可寫分支條件,根據指定條件選擇性註冊某些類 或者返回null

        return new String[]{HelloWorldConfiguration.class.getName()};
    }
}

添加@EnableHelloWorld用於控制是否裝配HelloWorld

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//@Import(HelloWorldConfiguration.class) // 基於註解驅動實現Sprig @Enable模塊
@Import(HelloWorldImportSelector.class) // 基於接口驅動實現Spring @Enable模塊
public @interface EnableHelloWorld {
}

注:如果直接使用@Import(HelloWorldConfiguration.class)註解方式實現,則不需要HelloWorldImportSelector類,但是註解方式無法添加分支判斷,只能指定加載指定類

2、實現自動裝配配置類 HelloWorldAutoConfiguration
@Configuration // 模式註解,聲明是一個bean
@ConditionalOnSystemProperty(name = "user.name", value = "Administrator") // 正確的條件裝配
//@ConditionalOnSystemProperty(name = "user.name", value = "lxt") // 錯誤的條件裝配
@EnableHelloWorld // Spring @Enable 模塊裝配
public class HelloWorldAutoConfiguration {
}

  • 先根據條件註解@ConditionalOnSystemProperty判斷是否滿足
  • 滿足則執行@EnableHelloWorld,加載HelloWorldImportSelector,註冊HelloWorldConfiguration進而註冊HelloWorld
3、配置自動裝配實現 META-INF/spring.factories

在resources下添加META-INF/spring.factories配置文件,用於啓動是通過工廠機制(SpringFactoriesLoader)加載

# 自動裝配
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.lxt.springboot.autoconfigure.configuration.HelloWorldAutoConfiguration
4、測試

添加測試類HelloWorldService

@Component
public class HelloWorldService {

    @Autowired
    private HelloWorld helloWorld;

    @PostConstruct
    public void init(){
        helloWorld.hello();
    }

}

啓動項目,控制檯輸出如下,測試開啓情況成功

2019-11-24 21:25:02.299  INFO 13880 --- [           main] c.l.s.a.a.HelloWorldImportSelector       : annotationMetadata.getAnnotationTypes():[com.lxt.springboot.autoconfigure.condition.ConditionalOnSystemProperty, com.lxt.springboot.autoconfigure.annotation.EnableHelloWorld]
2019-11-24 21:25:02.710  INFO 13880 --- [           main] c.l.s.a.c.HelloWorldConfiguration        : Load HelloWorld
2019-11-24 21:25:02.712  INFO 13880 --- [           main] c.l.s.autoconfigure.entity.HelloWorld    : hello world 2019!

去掉@EnableHelloWorld 註解或者條件註解修改爲ConditionalOnSystemProperty(name = “user.name”, value = “lxt”),分別重啓,控制檯輸出如下,測試關閉情況成功

***************************
APPLICATION FAILED TO START
***************************

Description:

Field helloWorld in com.lxt.springboot.autoconfigure.service.HelloWorldService required a bean of type 'com.lxt.springboot.autoconfigure.entity.HelloWorld' that could not be found.

The injection point has the following annotations:
	- @org.springframework.beans.factory.annotation.Autowired(required=true)


Action:

Consider defining a bean of type 'com.lxt.springboot.autoconfigure.entity.HelloWorld' in your configuration.

五、源碼

  • https://github.com/hdlxt/dive-in-spring-boot
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章