Spring Boot 基礎,理論,簡介

整理自:
博客園:龍四丶:SpringBoot系列

1、SpringBoot自動裝配

SpringBoot 中運用了大量的 Spring 註解,其註解大致分爲這幾類:

  1. 配置註解:@Configuration、@ComponentScan、@Import、@Conditional、Bean
  2. 模式註解:@Componnt、@Repository、@Service、@Controller
  3. @Enable 模塊註解:@EnableWebMvc、@EnableTransactionManagement、@EnableWebFlux

模式註解都標註了 @Component 註解,屬於 @Component 的派生註解,@ComponentScan 會掃描標註 @Component 及其派生註解的類,並將這些類加入到 Spring 容器中;@Enable 模塊註解中通過 @Import 導入配置類,在這些配置類中加載 @Enable 模塊需要的組件。

模式註解是一種用於聲明在應用中扮演“組件”角色的註解。如 Spring 中的 @Repository 是用於扮演倉儲角色的模式註解,用來管理和存儲某種領域對象。還有如@Component 是通用組件模式、@Service 是服務模式、@Configuration 是配置模式等。其中@Component 作爲一種由 Spring 容器託管的通用模式組件,任何被 @Component 標註的組件均爲組件掃描的候選對象。類似地,凡是被 @Component 標註的註解,如@Service ,當任何組件標註它時,也被視作組件掃描的候選對象。

1.1 Spring裝配方式

  • @ComponentScan 方式
@ComponentScan(basePackages = "com.loong.spring.boot")
public class SpringConfiguration {

}

註解的形式,依靠 basePackages 屬性指定掃描範圍。Spring 在啓動時,會在某個生命週期內創建所有的配置類註解解析器,而 @ComponentScan 的處理器爲 ComponentScanAnnotationParser。

1.2 Spring @Enable 模塊驅動

所謂“模塊”是指具備相同領域的功能組件集合,組合所形成的一個獨立的單元,比如 Web MVC 模塊、AspectJ代理模塊、Caching(緩存)模塊、JMX(Java 管理擴展)模塊、Async(異步處理)模塊等。

Spring Boot和Spring Cloud版本中都一直被使用,這種模塊化的註解均以 @Enable 作爲前綴,如下所示:

框架實現 @Enable註解模塊 激活模塊
Spring Framework @EnableWebMvc Web Mvc 模塊
/ @EnableTransactionManagement 事物管理模塊
/ @EnableWebFlux Web Flux 模塊
Spring Boot @EnableAutoConfiguration 自動裝配模塊
/ @EnableConfigurationProperties 配置屬性綁定模塊
/ @EnableOAuth2Sso OAuth2 單點登陸模塊
Spring Cloud @EnableEurekaServer Eureka 服務器模塊
/ @EnableFeignClients Feign 客戶端模塊
/ @EnableZuulProxy 服務網關 Zuul 模塊
/ @EnableCircuitBreaker 服務熔斷模塊

1.3 Spring 條件裝配

條件裝配指的是通過一些列操作判斷是否裝配 Bean ,也就是 Bean 裝配的前置判斷。實現方式主要有兩種:@Profile 和 @Conditional。

@Conditional(HelloWorldCondition.class)
@Component
public class HelloWorldConfiguration {
    public HelloWorldConditionConfiguration (){
        System.out.println("HelloWorldConfiguration初始化。。。");
    }
}

public class HelloWorldCondition implements Condition {
    @Override
    public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
        // ...
        return true;
    }
}

通過自定義一個 HelloWorldConfiguration 配置類,再標註 @Conditional 註解導入 HelloWorldCondition類,該類必須實現 Condition 接口,然後重寫 matches 方法,在方法中可以通過兩個入參來獲取一系列的上下文數據和元數據,最終返回ture或false來判定該類是否初始化,

2、自動裝配正文

在 SpringBoot 時代,通過一個main方法就可以啓動一個應用,其底層依賴的就是 Spring 幾個註解。從 @SpringBootApplication 註解中的 @EnableAutoConfiguration 註解開始,@EnableAutoConfiguration 屬於 Spring 的 @Enable 模塊註解,在該註解中通過 @Import 導入 AutoConfigurationImportSelector 類,在該類中加載所有以 AutoConfiguration 爲後綴且標註 @Configuration 註解的自動配置類,每個自動配置類可以裝配一個外部模塊,如 Web MVC 模塊對應的配置類是 WebMvcAutoConfiguration 。在自動配置類中又有衆多 @Conditional 條件註解,可達到靈活裝配的目的。

2.1 Spring Boot 自動裝配實現

Spring Boot 的啓動過程非常簡單,只需要啓動一個 main 方法,項目就可以運行,就算依賴了諸多外部模塊如:MVC、Redis等,也不需要我們進行過多的配置。

@SpringBootApplication
public class LoongSpringBootApplication {
	public static void main(String[] args) {
		SpringApplication.run(LoongSpringBootApplication.class, args);
	}
}

//關注的是 @SpringBootApplication 這個註解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
		@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {

	@AliasFor(annotation = EnableAutoConfiguration.class)
	Class<?>[] exclude() default {};
	@AliasFor(annotation = EnableAutoConfiguration.class)
	String[] excludeName() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackages")
	String[] scanBasePackages() default {};

	@AliasFor(annotation = ComponentScan.class, attribute = "basePackageClasses")
	Class<?>[] scanBasePackageClasses() default {};

}
  • @SpringBootConfiguration:它裏面標註了 @Configuration 註解,表明這是個配置類,功能與 @Configuration 無異。
  • @EnableAutoConfiguration:這個就是實現自動裝配的核心註解,是用來激活自動裝配的,其中默認路徑掃描以及組件裝配、排除等都通過它來實現。
  • @ComponentScan:這是用來掃描被 @Component標註的類 ,只不過這裏是用來過濾 Bean 的,指定哪些類不進行掃描,而且用的是自定義規則。
  • Class<?>[] exclude():根據class來排除,排除指定的類加入spring容器,傳入的類型是class類型。且繼承自 @EnableAutoConfiguration 中的屬性。
  • String[] excludeName():根據class name來排除,排除特定的類加入spring容器,參數類型是class的全類名字符串數組。同樣繼承自 @EnableAutoConfiguration。
  • String[] scanBasePackages():可以指定多個包名進行掃描。繼承自 @ComponentScan 。
  • Class<?>[] scanBasePackageClasses():可以指定多個類或接口的class,然後掃描 class 所在包下的所有組件。同樣繼承自 @ComponentScan 。

2.2 @EnableAutoConfiguration 實現

@EnableAutoConfiguration 是實現自動裝配的核心註解,是用來激活自動裝配的,看註解前綴我們應該知道是Spring @Enable 模塊驅動的設計模式,所以它必然會有 @Import 導入的被 @Configuration 標註的類或實現 ImportSelector 或 ImportBeanDefinitionRegistrar 接口的類。

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {

	...
}
  • @AutoConfigurationPackage:這是用來將啓動類所在包,以及下面所有子包裏面的所有組件掃描到Spring容器中,這裏的組件是指被 @Component或其派生註解標註的類。這也就是爲什麼不用標註@ComponentScan的原因。
  • @Import(AutoConfigurationImportSelector.class):這裏導入的是實現了 ImportSelector 接口的類,組件自動裝配的邏輯均在重寫的 selectImports 方法中實現。

2.2.1 獲取默認包掃描路徑

Spring Boot通過 @AutoConfigurationPackage 註解獲取默認包掃描路徑:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}

它是通過 @Import 導入了 AutoConfigurationPackages.Registrar 類,該類實現了 ImportBeanDefinitionRegistrar 接口。

2.3 自動裝配的組件內部實現

@Configuration
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
@ConditionalOnClass({ Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class,
        ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {
    ...
    @Configuration
    @Import(EnableWebMvcConfiguration.class)
    @EnableConfigurationProperties({WebMvcProperties.class, ResourceProperties.class})
    @Order(0)
    public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ResourceLoaderAware {
        ...
        @Bean
        @ConditionalOnBean(View.class)
        @ConditionalOnMissingBean
        public BeanNameViewResolver beanNameViewResolver() {
            ...
        }
        ...
    }

    @Configuration
    public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration {

        @Bean
        @Override
        public RequestMappingHandlerAdapter requestMappingHandlerAdapter() {
            ...
        }

        @Bean
        @Primary
        @Override
        public RequestMappingHandlerMapping requestMappingHandlerMapping() {
            ...
        }
    }
    ...
}

註解部分:

  • @Configuration:標識該類是一個配置類
  • @ConditionalXXX:Spring 條件裝配,只不過經由 Spring Boot 擴展形成了自己的條件化自動裝配,且都是@Conditional 的派生註解。
    • @ConditionalOnWebApplication:參數值是 Type 類型的枚舉,當前項目類型是任意、Web、Reactive其中之一則實例化該 Bean。這裏指定如果爲 Web 項目才滿足條件。
    • @ConditionalOnClass:參數是 Class 數組,當給定的類名在類路徑上存在,則實例化當前Bean。這裏當Servlet.class、 DispatcherServlet.class、 WebMvcConfigurer.class存在才滿足條件。
    • @ConditionalOnMissingBean:參數是也是 Class 數組,當給定的類沒有實例化時,則實例化當前Bean。這裏指定當 WebMvcConfigurationSupport 該類沒有實例化時,才滿足條件。

裝配順序:

  • @AutoConfigureOrder:參數是int類型的數值,數越小越先初始化。
  • @AutoConfigureAfter:參數是 Class 數組,在指定的配置類初始化後再加載。
  • @AutoConfigureBefore:參數同樣是 Class 數組,在指定的配置類初始化前加載。

代碼部分:
這部分就比較直接了,實例化了和 Web MVC 相關的Bean,如 HandlerAdapter、HandlerMapping、ViewResolver等。其中,出現了 DelegatingWebMvcConfiguration 類,這是 @EnableWebMvc 所 @Import導入的配置類。
可以看到,在Spring Boot 自動裝配的類中,經過了一系列的 @Conditional 條件判斷,然後實例化某個模塊需要的Bean,且無需我們配置任何東西。

3、SpringApplication啓動類準備階段

在構造 SpringApplication 啓動類時,初始化了幾個重要的類,如 WebApplicationType 、ApplicationContextInitializer、ApplicationListener。其中 WebApplicationType 存儲的是當前應用類型,如 Servlet Web 、Reactive Web; ApplicationContextInitializer 和 ApplicationListener 則是 SpringBoot 通過擴展 Spring 特性創建的初始化器及監聽器。

3.1 SpringApplication 配置

SpringApplication 準備階段結束後,按道理應該進入運行階段,但運行階段之前還有一個操作,就是可以修改 SpringApplication 默認配置。開頭的代碼示例可以看到,應用程序主類中的main方法中寫的都是SpringApplication.run(xx.class),可能這種寫法不滿足我們的需求,我們可以對SpringApplication進行一些配置,例如關閉Banner,設置一些默認的屬性等。下面則是利用 SpringApplicationBuilder 的方式來添加配置:

@SpringBootApplication
public class DiveInSpringBootApplication {
	public static void main(String[] args) {
		new SpringApplicationBuilder(DiveInSpringBootApplication.class)
				// 設置當前應用類型
				.web(WebApplicationType.SERVLET)
				// 設置 banner 橫幅打印方式、有關閉、日誌、控制檯
				.bannerMode(Banner.Mode.OFF)
				// 設置自定義的 banner
				.banner()
				// 追加自定義的 initializer 到集合中
				.initializers()
				// 追加自定義的 listeners 到集合中
				.listeners()
				.run(args);
	}
}

使用該方式實現的SpringApplication可以對其添加自定義的配置。

4、SpringApplication啓動類運行階段

在 SpringApplication 運行階段中,先是通過擴展 Spring 監聽機制,在 SpringBoot 各個階段發佈不同事件,執行多個事件監聽器;然後創建 Environment 類,這是外部化配置的核心類;最後啓動 Spring 容器,通過 WebApplicationType 判定當前應用類型,創建應用對應 ApplicationContext 應用上下文,再調用 ApplicationContext#refresh 方法啓動容器。

5、外部化配置之Environment

外部化配置的幾種資源類型,如 properties、YAML、環境變量、系統屬性、啓動參數等。還詳細介紹了 Environment 類,該類是外部化配置核心類,所有外部化配置數據,都保存在該類中,並和大家討論了整個存儲流程。

6、外部化配置之@ConfigurationProperties

@ConfigurationProperties 是 SpringBoot 實現外部化配置的重要註解,配合 SprinBoot 自動裝配特性來達到快速開發的目的。主要將 properties 配置文件和 Properties 配置類中的屬性進行映射,同樣也和大家討論了整個映射流程。

6.1 @ConfigurationProperties

當 Spring Boot 集成外部組件後,就可在 properties 或 YAML 配置文件中定義組件需要的屬性,如 Redis 組件:

spring.redis.url=redis://user:[email protected]:6379
spring.redis.host=localhost
spring.redis.password=123456
spring.redis.port=6379

其中都是以 spring.redis 爲前綴。這其實是 Spring Boot 爲每個組件提供了對應的 Properties 配置類,並將配置文件中的屬性值給映射到配置類中,而且它們有個特點,都是以 Properties 結尾,如 Redis 對應的配置類是 RedisProperties:

@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
    private String url;

	private String host = "localhost";

	private String password;

	private int port = 6379;
	...
}

@ConfigurationProperties 的註解,它的 prefix 參數就是約定好的前綴。該註解的功能就是將配置文件中的屬性和 Properties 配置類中的屬性進行映射,來達到自動配置的目的。這個過程分爲兩步,第一步是註冊 Properties 配置類,第二步是綁定配置屬性,過程中還涉及到一個註解,它就是 @EnableConfigurationProperties ,該註解是用來觸發那兩步操作的。

Spring Boot 外部化配置做一個整體的總結:

  1. 首先,外部化配置是 Spring Boot 的一個特性,主要是通過外部的配置資源實現與代碼的相互配合,來避免硬編碼,提供應用數據或行爲變化的靈活性。
  2. 然後介紹了幾種外部化配置的資源類型,如 properties 和 YAML 配置文件類型,並介紹了獲取外部化配置資源的幾種方式。
  3. 其次,介紹了 Environment 類的加載流程,以及所有外部化配置加載到 Environment 中的底層是實現。Environment 是 Spring Boot 外部化配置的核心類,該類存儲了所有的外部化配置資源,且其它獲取外部化配置資源的方式也都依賴於該類。
  4. 最後,介紹了 Spring Boot 框架中核心的 @ConfigurationProperties 註解,該註解是將 application 配置文件中的屬性值和 Properties 配置類中的屬性進行映射,來達到自動配置的目的,並帶大家探討了這一過程的底層實現。

7、嵌入式Web容器

傳統 Spring 應用需手動創建和啓動 Web 容器,在 SpringBoot 中,則是嵌入式的方式自動創建和啓動。SpringBoot 支持的 Web 容器類型有 Servlet Web 容器和 Reactive Web 容器,它們都有具體容器實現,Sevlet Web 對應的是 Tomcat、Jetty、Undertow,默認實現是 Tomcat;Reactive Web 對應的是 Netty 。

8、Starter機制之自定義Starter

在 Spring 時代,搭建一個 Web 應用通常需要在 pom 文件中引入多個 Web 模塊相關的 Maven 依賴,如 SpringMvc、Tomcat 等依賴,而 SpringBoot 則只需引入 spring-boot-starter-web 依賴即可。這就是 SpringBoot 的 Starter 特性,用來簡化項目初始搭建以及開發過程,它是一個功能模塊的所有 Maven 依賴集合體。

8.1 SpringBoot Starter 原理

SpringBoot 提供了非常多的 Starter,下面列出常用的幾個:

名稱 功能
spring-boot-starter-web 支持 Web 開發,包括 Tomcat 和 spring-webmvc
spring-boot-starter-redis 支持 Redis 鍵值存儲數據庫,包括 spring-redis
spring-boot-starter-test 支持常規的測試依賴,包括 JUnit、Hamcrest、Mockito 以及 spring-test 模塊
spring-boot-starter-aop 支持面向切面的編程即 AOP,包括 spring-aop 和 AspectJ
spring-boot-starter-jdbc 支持JDBC數據庫
spring-boot-starter-data-jpa 支持 JPA ,包括 spring-data-jpa、spring-orm、Hibernate
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章