SpringBoot運(yùn)作原理【原創(chuàng)】

運(yùn)作原理

調(diào)整配置可以打印出日志

通過(guò)啟用 debug=true屬性;來(lái)讓控制臺(tái)打印自動(dòng)配置報(bào)告


原作原理-consol-未啟用的自動(dòng)配置.png
原作原理-consol-已啟用的自動(dòng)配置.png

SpringBoot啟動(dòng)的時(shí)候加載主配置類(lèi),開(kāi)啟了自動(dòng)配置功能 @EnableAutoConfiguration

@EnableAutoConfiguration注解的源碼

一旦加上此注解,那么將會(huì)開(kāi)啟自動(dòng)裝配功能,簡(jiǎn)單點(diǎn)講,Spring會(huì)試圖在你的classpath下找到所有配置的Bean然后進(jìn)行裝配。當(dāng)然裝配Bean時(shí),會(huì)根據(jù)若干個(gè)(Conditional)定制規(guī)則來(lái)進(jìn)行初始化。

?
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Import;
?
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import({AutoConfigurationImportSelector.class})
public @interface EnableAutoConfiguration {
 String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
?
 Class<?>[] exclude() default {};
?
 String[] excludeName() default {};
}
AutoConfigurationImportSelector
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {

}
?
public interface DeferredImportSelector extends ImportSelector {}

該類(lèi)實(shí)現(xiàn)了DeferredImportSelector接口,這個(gè)接口繼承了ImportSelector:

該接口主要是為了導(dǎo)入@Configuration的配置項(xiàng),而DeferredImportSelector是延期導(dǎo)入,當(dāng)所有的@Configuration都處理過(guò)后才會(huì)執(zhí)行。

EnableAutoConfiguration中的AutoConfigurationImportSelector類(lèi)掃描spring.factorise文件

AutoConfigurationImportSelector的selectImports方法
 public String[] selectImports(AnnotationMetadata annotationMetadata) {
 if (!this.isEnabled(annotationMetadata)) {
 return NO_IMPORTS;
 } else {
 AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this.beanClassLoader);
 AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata);
 return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
 }
 }

該方法剛開(kāi)始會(huì)先判斷是否進(jìn)行自動(dòng)裝配,而后會(huì)從META-INF/spring-autoconfigure-metadata.properties讀取元數(shù)據(jù)與元數(shù)據(jù)的相關(guān)屬性,緊接著會(huì)調(diào)用getCandidateConfigurations方法:

原作原理-自動(dòng)裝配-spring.factories.png
 protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) {
 if (!this.isEnabled(annotationMetadata)) {
 return EMPTY_ENTRY;
 } else {
 AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
 List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
 configurations = this.removeDuplicates(configurations);
 Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
 this.checkExcludedClasses(configurations, exclusions);
 configurations.removeAll(exclusions);
 configurations = this.filter(configurations, autoConfigurationMetadata);
 this.fireAutoConfigurationImportEvents(configurations, exclusions);
 return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);
 }
 }
?
ConditionalOnWebApplication類(lèi)分析
package org.springframework.boot.autoconfigure.condition;
?
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
?
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnWebApplicationCondition.class})
public @interface ConditionalOnWebApplication {
 ConditionalOnWebApplication.Type type() default ConditionalOnWebApplication.Type.ANY;
?
 public static enum Type {
 ANY,
 SERVLET,
 REACTIVE;
?
 private Type() {
 }
 }
}
OnWebApplicationCondition.class
 private ConditionOutcome isWebApplication(ConditionContext context, AnnotatedTypeMetadata metadata, boolean required) {
 switch(this.deduceType(metadata)) {
 case SERVLET:
 return this.isServletWebApplication(context);
 case REACTIVE:
 return this.isReactiveWebApplication(context);
 default:
 return this.isAnyWebApplication(context, required);
 }
 }

isWebApplication方法可以看出,判斷條件是:

springboot內(nèi)置的自動(dòng)裝配功能
http的編碼配置

常規(guī)web項(xiàng)目的web.xml里配置一個(gè)filter

<filter>
 <filter-name>encodingFilter</filter-name>
 <filter-class>org.springframework.web.filter.CharacterEncodingFilter
 </filter-class>
 <async-supported>true</async-supported>
 <init-param>
 <param-name>encoding</param-name>
 <param-value>UTF-8</param-value>
 </init-param>
 <init-param>
 <param-name>forceEncoding</param-name>
 <param-value>true</param-value>
 </init-param>
 </filter>

自動(dòng)配置需要滿(mǎn)足兩個(gè)條件

1、能配置CharacterEncodingFilter這個(gè)bean

2、能配置encoding和forceEncoding這兩個(gè)參數(shù)

HttpEncodingAutoConfiguration 類(lèi)

根據(jù)條件配置characterEncodingFilter的bean

package org.springframework.boot.autoconfigure.web.servlet;
?
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication.Type;
import org.springframework.boot.autoconfigure.http.HttpProperties;
import org.springframework.boot.autoconfigure.http.HttpProperties.Encoding;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.server.WebServerFactoryCustomizer;
import org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter;
import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.web.filter.CharacterEncodingFilter;
?
@Configuration
?
//  開(kāi)啟屬性注入  通過(guò) @EnableConfigurationProperties 生命,
@EnableConfigurationProperties({HttpProperties.class})
@ConditionalOnWebApplication(
 type = Type.SERVLET
)
?
// 當(dāng)CharacterEncodingFilter在類(lèi)路徑的條件下
@ConditionalOnClass({CharacterEncodingFilter.class})
?
// 當(dāng)設(shè)置spring.http.encoding=enabled的情況下,如果沒(méi)有設(shè)置則默認(rèn)為true,既條件符合
@ConditionalOnProperty(
 prefix = "spring.http.encoding",
 value = {"enabled"},
 matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
 private final Encoding properties;
?
 public HttpEncodingAutoConfiguration(HttpProperties properties) {
 this.properties = properties.getEncoding();
 }
?
 @Bean
 @ConditionalOnMissingBean
 public CharacterEncodingFilter characterEncodingFilter() {
 CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
 filter.setEncoding(this.properties.getCharset().name());
 filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
 filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
 return filter;
 }
?
 @Bean
 public HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer localeCharsetMappingsCustomizer() {
 return new HttpEncodingAutoConfiguration.LocaleCharsetMappingsCustomizer(this.properties);
 }
?
 private static class LocaleCharsetMappingsCustomizer implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
 private final Encoding properties;
?
 LocaleCharsetMappingsCustomizer(Encoding properties) {
 this.properties = properties;
 }
?
 public void customize(ConfigurableServletWebServerFactory factory) {
 if (this.properties.getMapping() != null) {
 factory.setLocaleCharsetMappings(this.properties.getMapping());
 }
?
 }
?
 public int getOrder() {
 return 0;
 }
 }
}
相關(guān)注釋
@ConfigurationPropertie注解
@Component
@ConfigurationProperties(prefix = "author")
@Data
public class AuthorSettings {
?
 private String name;
 private Long age;
}
author:
 name: SSH
 age: 18

使用該注解,可以將配置文件數(shù)據(jù)引入到類(lèi)中。把配置文件的信息,讀取并自動(dòng)封裝成實(shí)體類(lèi)

@ConditionalOnClass

被引用時(shí)@ConditionalOnClass(HelloService.class)

ConditionalOnClass源碼類(lèi)

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
?
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnClassCondition.class})
public @interface ConditionalOnClass {
 Class<?>[] value() default {};
?
 String[] name() default {};
}

@Conditional指定的條件成立,才給容器中添加組件,配置的所有內(nèi)容才生效;

點(diǎn)開(kāi)該注解 @Conditional({OnClassCondition.class})

OnClassCondition類(lèi)有繼承了FilteringSpringBootCondition

最終還是實(shí)現(xiàn)Condition 接口

即根據(jù)條件返回是否為true來(lái)決定支付注冊(cè)這個(gè)bean

類(lèi)似的:@ConditionalOnWebApplication、@ConditionalOnProperty 滿(mǎn)足一定條件才做什么

1、
@Conditional({OnClassCondition.class})
2、
OnClassCondition extends FilteringSpringBootCondition
3、
FilteringSpringBootCondition extends SpringBootCondition
4、
SpringBootCondition implements Condition
@ConditionalOnProperty

@ConditionalOnProperty(prefix = "hello", value = "enable", matchIfMissing = true)

前綴 hello嗎,matchIfMissing 為true 沒(méi)有配置時(shí)也會(huì)正常加載,在下邊的demo中會(huì)被引用

package org.springframework.boot.autoconfigure.condition;
?
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.context.annotation.Conditional;
?
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
 /**
 * 數(shù)組,獲取對(duì)應(yīng)property名稱(chēng)的值,與name不可同時(shí)使用
 *
 * @return
 */
 String[] value() default {};
?
 /**
 *  property名稱(chēng)的前綴,可有可無(wú)
 * @return
 */
 String prefix() default "";
?
 /**
 * 數(shù)組,property完整名稱(chēng)或部分名稱(chēng)(可與prefix組合使用,組成完整的property名稱(chēng)),與value不可同時(shí)使用
 * @return
 */
 String[] name() default {};
?
 /**
 * 可與name組合使用,比較獲取到的屬性值與havingValue給定的值是否相同,相同才加載配置
 * @return
 */
 String havingValue() default "";
?
 /**
 * 缺少該property時(shí)是否可以加載。如果為true,沒(méi)有該property也會(huì)正常加載;反之報(bào)錯(cuò)
 * @return
 */
 boolean matchIfMissing() default false;
}
?

打開(kāi)OnPropertyCondition.class類(lèi)

OnPropertyCondition 類(lèi)繼承 SpringBootCondition

SpringBootCondition 實(shí)現(xiàn)Condition 接口

自動(dòng)裝配Demo

不僅是自動(dòng)裝配,更是低耦合的配置

新建項(xiàng)目:

spring-boot-starter-hello

pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <parent>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-starter-parent</artifactId>
 <version>2.1.6.RELEASE</version>
 <relativePath/> <!-- lookup parent from repository -->
 </parent>
 <groupId>hand.shen</groupId>
 <artifactId>spring-boot-starter-hello</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <name>starter-hello</name>
 <description>starter-hello project for Spring Boot</description>
?
 <properties>
 <java.version>1.8</java.version>
 </properties>
?
 <dependencies>
 <dependency>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-autoconfigure</artifactId>
 </dependency>
 <dependency>
 <groupId>org.projectlombok</groupId>
 <artifactId>lombok</artifactId>
 <version>1.18.0</version>
 </dependency>
 </dependencies>
 <build>
 <plugins>
 <plugin>
 <groupId>org.springframework.boot</groupId>
 <artifactId>spring-boot-maven-plugin</artifactId>
 <configuration>
 <skip>true</skip>
 </configuration>
 </plugin>
 </plugins>
 </build>
?
</project>
?
HelloServiceProperties.java
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
?
/**
 * @description: spring-boot 自動(dòng)配置作為依賴(lài)
 *               類(lèi)型安全的屬性獲取。
 *               在yml文件中 通過(guò) hello.msg = 來(lái)設(shè)置,如果不設(shè)置默認(rèn) hello.meg = word
 *
 * @author: Shenshuaihu
 * @version: 1.0
 * @data: 2019-07-10 23:26
 */
@ConfigurationProperties(prefix = "hello")
@Data
public class HelloServiceProperties {
?
 private static final String MSG = "word";
?
 private String msg = MSG;
}
?
HelloService.java
import lombok.Data;
?
/**
 * @description: 判斷依據(jù)
 *
 * @author: Shenshuaihu
 * @version: 1.0
 * @data: 2019-07-10 23:30
 */
@Data
public class HelloService {
?
 private String msg;
?
 public String sayHello() {
 return "Hello" + msg;
 }
}
HelloServiceAutoConfiguration.java

@ConditionalXXX注釋上文有

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
?
/**
 * @description: 自動(dòng)配置類(lèi)
 *
 *      注釋 @ConditionalOnClass 會(huì)判斷HelloService這個(gè)類(lèi)的路徑是否存在,
 *          如果沒(méi)有了 則會(huì)自動(dòng)創(chuàng)建
 *
 * @author: Shenshuaihu
 * @version: 1.0
 * @data: 2019-07-10 23:35
 */
?
@Configuration
@EnableConfigurationProperties(HelloServiceProperties.class)
@ConditionalOnClass(HelloService.class)
@ConditionalOnProperty(prefix = "hello", value = "enable", matchIfMissing = true)
public class HelloServiceAutoConfiguration {
?
 @Autowired
 private HelloServiceProperties helloServiceProperties;
?
 @Bean
 @ConditionalOnMissingBean(HelloService.class)
 public HelloService helloService() {
 HelloService helloService = new HelloService();
 helloService.setMsg(helloServiceProperties.getMsg());
 return helloService;
 }
}
spring.factories

文件路徑 resource/MEFA-INF/spring.factories,指定文件裝配的類(lèi)

org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.starterhello.HelloServiceAutoConfiguration

mvn install 安裝到本地庫(kù)

在另外一個(gè)項(xiàng)目中開(kāi)啟

在pom.xml 中引入jar

 <dependency>
 <groupId>hand.shen</groupId>
 <artifactId>spring-boot-starter-hello</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 </dependency>
?

直接注入helloservice

import com.starterhello.HelloService;
?
 @Autowired
 private HelloService helloService;
?
 @RequestMapping("/")
 String index() {
 return "Hello Spring Boot," +
 " \n 自動(dòng)裝配:" + helloService.sayHello();
 }
?

yml配置

# 自動(dòng)裝配        
hello:
 msg: shenshauihu

如何使用了,如果yml文件上沒(méi)有配置hello:msg中,會(huì)使用默認(rèn)是helloword,沒(méi)有配置了hello:msg為shenshuaihu時(shí),則會(huì)使用了helloshenshuaihu

原作原理-自動(dòng)裝配-hello.png
參考文檔:

https://www.cnblogs.com/niechen/p/9027804.html?utm_source=tuicool&utm_medium=referral

http://www.itdecent.cn/p/83693d3d0a65
http://www.itdecent.cn/p/1d96508085ff

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容