深入解析SpringBoot自動(dòng)配置原理
前言
SpringBoot 的一大好處就是:大大簡(jiǎn)化了 Spring 和其他框架的整合配置。傳統(tǒng)的 SSM 套裝雖然很大程度地簡(jiǎn)化了 Web 開(kāi)發(fā),但是其的配置文件卻較為繁瑣,為了簡(jiǎn)化配置文件使開(kāi)發(fā)者更專注于業(yè)務(wù)編碼,可以使用 SpringBoot 來(lái)進(jìn)行 Web 開(kāi)發(fā),其精簡(jiǎn)的配置和龐大繁茂的生態(tài)圈絕對(duì)令人驚嘆
SpringBoot 之所以可以達(dá)到如此精簡(jiǎn)的配置,主要原因就是 SpringBoot自動(dòng)配置
自動(dòng)配置原理
本文的 SpringBoot 的版本為 2.0.6 RELEASE
SpringBoot 應(yīng)用的啟動(dòng)類
SpringBoot 應(yīng)用是從啟動(dòng)類的 main 方法中啟動(dòng),加載 SpringBoot 配置類
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
@SpringBootApplication 注解
由于 SpringBoot 應(yīng)用是從啟動(dòng)類的 main 方法中啟動(dòng)的,我們就有必要查看 @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 {
// 省略 ......
}
可以看到 @SpringBootApplication 這個(gè)注解是一個(gè)復(fù)合注解,它有三個(gè)核心注解,下面我們依次查看它們
- @SpringBootConfiguration
- @EnableAutoConfiguration
- @ComponentScan
@SpringBootConfiguration 注解
表明它是一個(gè)配置類
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
@ComponentScan 注解
它負(fù)責(zé)包的掃描,并排除一些特定的類型(不允許注冊(cè)進(jìn) Spring 容器)
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
@EnableAutoConfiguration 注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
// 省略 ......
}
可以看到 @EnableAutoConfiguration 注解是個(gè)復(fù)合注解,有兩個(gè)元注解,我們依次查看它們
- @AutoConfigurationPackage
- @Import(AutoConfigurationImportSelector.class)
@AutoConfigurationPackage
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
查看 AutoConfigurationPackages.Registrar
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
它其實(shí)是注冊(cè)了一個(gè) bean 的定義,返回了當(dāng)前主啟動(dòng)類包的同級(jí)以及子級(jí)的包組件
@Import(AutoConfigurationImportSelector.class)
在 AutoConfigurationImportSelector 類中查看 selectImports() 方法
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
AnnotationAttributes attributes = getAttributes(annotationMetadata);
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return StringUtils.toStringArray(configurations);
}
再查看 getCandidateConfigurations(annotationMetadata, attributes) 方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(
getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader());
Assert.notEmpty(configurations,
"No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
再查看 loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()) 方法
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
loadSpringFactories 方法調(diào)用如下
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
該方法的作用:掃描類路徑下所有具有 META-INF/spring.factories 的 jar 包,將掃描到的相應(yīng)類組件注冊(cè)進(jìn) Spring 容器中,不過(guò)這些掃描到的類組件注冊(cè)進(jìn) Spring 容器中需要生效也是有條件的,使用條件注解 @Conditional
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";

META-INF/spring.factories 的部分內(nèi)容如下
...... # Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\ org.springframework.boot.autoconfigure.cloud.CloudAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\ org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\ org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.couchbase.CouchbaseRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.elasticsearch.ElasticsearchRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoReactiveRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.mongo.MongoRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.data.redis.RedisRepositoriesAutoConfiguration,\ org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\ org.springframework.boot.autoconfigure.elasticsearch.jest.JestAutoConfiguration,\ org.springframework.boot.autoconfigure.flyway.FlywayAutoConfiguration,\ org.springframework.boot.autoconfigure.freemarker.FreeMarkerAutoConfiguration,\ org.springframework.boot.autoconfigure.jackson.JacksonAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration,\ org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JmsAutoConfiguration,\ org.springframework.boot.autoconfigure.jmx.JmxAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.JndiConnectionFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.activemq.ActiveMQAutoConfiguration,\ org.springframework.boot.autoconfigure.jms.artemis.ArtemisAutoConfiguration,\ org.springframework.boot.autoconfigure.kafka.KafkaAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.embedded.EmbeddedMongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoAutoConfiguration,\ org.springframework.boot.autoconfigure.mongo.MongoReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration,\ org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration,\ org.springframework.boot.autoconfigure.transaction.jta.JtaAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration,\ org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.reactive.WebSocketReactiveAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketServletAutoConfiguration,\ org.springframework.boot.autoconfigure.websocket.servlet.WebSocketMessagingAutoConfiguration,\ ......
這些自動(dòng)配置類都是以 AutoConfiguration 結(jié)尾來(lái)命名的,它實(shí)際上就是一個(gè) JavaConfig 形式的 Spring 容器配置類
在此我們以 RedisAutoConfiguration 類為例,舉例說(shuō)明
@Configuration
@ConditionalOnClass(RedisOperations.class)// 當(dāng)給定的類名在類路徑上存在時(shí),則實(shí)例化當(dāng)前 bean
@EnableConfigurationProperties(RedisProperties.class)// 將 RedisProperties 這個(gè)類注冊(cè)進(jìn) spring 容器中
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {
@Bean
@ConditionalOnMissingBean(name = "redisTemplate")
public RedisTemplate<Object, Object> redisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
@Bean
@ConditionalOnMissingBean
public StringRedisTemplate stringRedisTemplate(
RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
StringRedisTemplate template = new StringRedisTemplate();
template.setConnectionFactory(redisConnectionFactory);
return template;
}
}
@Conditional 條件注解
- @ConditionalOnBean:當(dāng)給定的在 bean 存在時(shí),則實(shí)例化當(dāng)前 bean
- @ConditionalOnExpression:當(dāng)表達(dá)式為 true 的時(shí)候,才會(huì)實(shí)例化一個(gè) bean
- @ConditionalOnMissingBean:當(dāng)給定的在 bean 不存在時(shí),則實(shí)例化當(dāng)前 bean
- @ConditionalOnMissingClass:當(dāng)給定的類名在類路徑上不存在時(shí),則實(shí)例化當(dāng)前 bean
- @ConditionalOnNotWebApplication:不是 web 應(yīng)用
- @ConditionalOnClass:當(dāng)給定的類名在類路徑上存在時(shí),則實(shí)例化當(dāng)前 bean
RedisProperties 類
該類負(fù)責(zé)讀取配置文件中具體配置,如 spring.redis.host 和 spring.redis.port
@ConfigurationProperties(prefix = "spring.redis")
public class RedisProperties {
private int database = 0;
private String url;
private String host = "localhost";
private String password;
private int port = 6379;
private boolean ssl;
private Duration timeout;
private Sentinel sentinel;
private Cluster cluster;
private final Jedis jedis = new Jedis();
private final Lettuce lettuce = new Lettuce();
......
}
當(dāng)我們?cè)谑褂?maven 引入了相關(guān)的 jar 包的時(shí)候,在類路徑下也就具有了 META-INF/spring.factories 這樣的文件,同時(shí)也就滿足了上面的某些條件注解。這時(shí),maven 引入了相關(guān)的 jar 包的類組件也就成功的注冊(cè)進(jìn) Spring 容器中了
自動(dòng)配置總結(jié)
@SpringBootApplication 它有三個(gè)核心注解:@SpringBootConfiguration,@EnableAutoConfiguration(重點(diǎn)關(guān)注),@ComponentScan
@EnableAutoConfiguration:核心作用是掃描類路徑下所有具有 META-INF/spring.factories 的類組件(配置類),這些類組件都是以 AutoConfiguration 結(jié)尾來(lái)命名的,他能讀取在配置文件中配置的屬性信息,如 server.port,redis.port 等
然后將上述掃描到的相應(yīng)類組件注冊(cè)進(jìn) Spring 容器中;不過(guò)這些掃描到的類組件注冊(cè)進(jìn) Spring 容器中需要生效也是有條件的,此時(shí)使用了條件注解 @ConditionOnxxx
當(dāng)我們?cè)谑褂?maven 或 gradle 引入了相關(guān)的 jar 包的時(shí)候,在類路徑下也就具有了 META-INF/spring.factories 這樣的文件;同時(shí)也就滿足了某些 @ConditionOnxxx 的條件注解。這時(shí),maven 或 gradle 引入了相關(guān)的 jar 包的類組件也就成功的注冊(cè)進(jìn) Spring 容器中了
到此這篇關(guān)于深入解析SpringBoot自動(dòng)配置原理的文章就介紹到這了,更多相關(guān)SpringBoot自動(dòng)配置原理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Docker環(huán)境下Spring Boot應(yīng)用內(nèi)存飆升分析與解決場(chǎng)景分析
當(dāng)運(yùn)行一個(gè)Spring Boot項(xiàng)目時(shí),如果未設(shè)置JVM內(nèi)存參數(shù),Spring Boot默認(rèn)會(huì)采用JVM自身默認(rèn)的配置策略,接下來(lái)通過(guò)本文給大家介紹Docker環(huán)境下Spring Boot應(yīng)用內(nèi)存飆升分析與解決方法,需要的朋友參考下吧2021-08-08
關(guān)于Map的遍歷以及轉(zhuǎn)JsonArray存儲(chǔ)方式
在Java開(kāi)發(fā)過(guò)程中,經(jīng)常會(huì)遇到需要對(duì)復(fù)雜數(shù)據(jù)結(jié)構(gòu)進(jìn)行處理的情況,本案例以List<Map<String,Object>>為例,介紹了如何遍歷該數(shù)據(jù)結(jié)構(gòu),并根據(jù)特定條件篩選出符合要求的元素,通過(guò)自定義一個(gè)Edit類來(lái)模擬形成一個(gè)新的Map對(duì)象,實(shí)現(xiàn)了數(shù)據(jù)的有序存儲(chǔ)2024-11-11
java單機(jī)滑動(dòng)窗口限流器的實(shí)現(xiàn)
本文主要介紹了java單機(jī)滑動(dòng)窗口限流器的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2026-03-03
java實(shí)現(xiàn)區(qū)域內(nèi)屏幕截圖示例
這篇文章主要介紹了java截圖示例,需要的朋友可以參考下2014-04-04
SpringMVC文件上傳請(qǐng)求問(wèn)題分析
這篇文章主要介紹了SpringMVC文件上傳請(qǐng)求,我們發(fā)的請(qǐng)求默認(rèn)都是由DispatcherServlet類的doDispatch()來(lái)處理,這個(gè)方法的邏輯處理的第一步就是處理文件上傳的請(qǐng)求,我們一起來(lái)看看是怎么處理的吧2024-07-07
Java JDK1.7對(duì)字符串的BASE64編碼解碼方法
今天小編就為大家分享一篇Java JDK1.7對(duì)字符串的BASE64編碼解碼方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-10-10
Spring框架 引入@Resource注解報(bào)空指針的解決
這篇文章主要介紹了Spring框架 引入@Resource注解報(bào)空指針的解決,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-11-11

