運(yùn)作原理
調(diào)整配置可以打印出日志
通過(guò)啟用 debug=true屬性;來(lái)讓控制臺(tái)打印自動(dòng)配置報(bào)告


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方法:

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

參考文檔:
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