Spring Boot 初始化过程
本文包含两个部分: 一个是配置文件加载顺序, 一个是SpringBoot的自动装配过程.
一. 我们先来看看"配置文件加载顺序"
- 配置文件扫描规则
springboot配置文件的扫描顺序:
工程根目录:./config/
工程根目录:./
classpath:/config/
classpath:/
加载的优先级顺序是从上向下加载,并且所有的文件都会被加载,高优先级的内容会覆盖底优先级的内容,形成互补配置
也可以通过指定配置spring.config.location来改变默认配置,我们可以通过指令:
java -jar xxxx.jar --spring.config.location=D:/kawa/application.yml # 该命令指定的配置文件,会使默认互补覆盖配置失效,建议用additional-location java -jar xxxx.jar --spring.config.additional-location=D:/kawa/ # –spring.config.additional-location后面接的必须是一个目录
2. 先加载指定profile配置, 再加载默认配置
jar包外部的application-{profile}.propertie或application.yml(带spring.profile)配置文件
jar包内部的application-{profile}.propertie或application.yml(带spring.profile)配置文件
jar包外部的application.propertie或application.yml(不带spring.profile)配置文件
jar包内部的application.propertie或application.yml(不带spring.profile)配置文件
3. 命令行参数
java -jar xxxx.jar --server.port=8087 --server.context-path=/show
这些配置的加载顺序: 命令行参数 > profile文件 > 默认配置文件
1. 先加载命令行参数
java -jar xxxx.jar --server.port=8087 --server.context-path=/show
2. 加载带profile的配置文件
2.1 jar包外部./config/下的application-{profile}.propertie或application-{profile}.yml
2.2 jar包外部./下的application-{profile}.propertie或application-{profile}.yml
2.3 jar包内部./config/下的application-{profile}.propertie或application-{profile}.yml
2.4 jar包内部./下的application-{profile}.propertie或application-{profile}.yml
3. 再来加载不带profile
3.1 jar包外部./config/下的application.propertie或application.yml
3.2 jar包外部./下的application.propertie或application.yml
3.3 jar包内部./config/下的application.propertie或application.yml
3.4 jar包内部./下的application.propertie或application.yml
二. Spring Boot给我们带来最大的便捷就是很多组件都是默认装配好的, 那么SpringBoot的自动装配过程是啥样子?
看了<<SpringBoot实战>>, 了解到大概过程如下:
1. springboot项目入口类的注解就是@SpringBootApplication, 这个注解主要包含了其他三个注解:@SpringBootConfiguration, @ComponentScan,@EnableAutoConfiguration;
其中自动装配的核心注解是@EnableAutoConfiguration
@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 {
//TODO:
}
2. @EnableAutoConfiguration引入了一个装配选择器: AutoConfigurationImportSelector(实现了ImportSelector接口), 通过 selectImports方法加载其他的初始化入口.
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//TODO:
}
AutoConfigurationImportSelector.java
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//获取自动装配的注解, 扫描路径:META-INF/spring-autoconfigure-metadata.properties(用于下一步的排除操作)
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//获取待装配的类, 扫描路径:META-INF/spring-factories(会排除掉autoConfigurationMetadata中的类)
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
-- META-INF/spring-autoconfigure-metadata.properties
org.springframework.boot.autoconfigure.security.servlet.SecurityFilterAutoConfiguration.AutoConfigureAfter=org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration.Configuration=
-- META-INF/spring-factories
# PropertySource Loaders
org.springframework.boot.env.PropertySourceLoader=\
org.springframework.boot.env.PropertiesPropertySourceLoader,\
org.springframework.boot.env.YamlPropertySourceLoader
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
# Error Reporters
org.springframework.boot.SpringBootExceptionReporter=\
org.springframework.boot.diagnostics.FailureAnalyzers
3. 配置文件中的每一项都是模块的初始化入口, 这里举CacheAutoConfiguration为例:
@Configuration
@ConditionalOnClass(CacheManager.class)
@ConditionalOnBean(CacheAspectSupport.class)
@ConditionalOnMissingBean(value = CacheManager.class, name = "cacheResolver")
@EnableConfigurationProperties(CacheProperties.class)
@AutoConfigureBefore(HibernateJpaAutoConfiguration.class)
@AutoConfigureAfter({ CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class,
RedisAutoConfiguration.class })
@Import(CacheConfigurationImportSelector.class)
public class CacheAutoConfiguration {
//TODO:
}
同样的, 它也引入了一个装配选择器:CacheConfigurationImportSelector, 通过 selectImports方法, 加载不同cache框架的初始化配置.
final class CacheConfigurations {
private static final Map<CacheType, Class<?>> MAPPINGS;
static {
Map<CacheType, Class<?>> mappings = new EnumMap<>(CacheType.class);
mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
MAPPINGS = Collections.unmodifiableMap(mappings);
}
......
}
这里以redis为例, 加载了RedisCacheConfiguration配置
@Configuration
@AutoConfigureAfter(RedisAutoConfiguration.class)
@ConditionalOnBean(RedisConnectionFactory.class)
@ConditionalOnMissingBean(CacheManager.class)
@Conditional(CacheCondition.class)
class RedisCacheConfiguration {
//TODO:
}
4. 初始化配置一般都含有@Conditional 注解(ConditionalOnBean, ConditionalOnMissingBean都是基于Conditional), 用于判断当前类是否需要初始化到spring容器中.
这里举CacheCondition为例:
class CacheCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String sourceClass = "";
if (metadata instanceof ClassMetadata) {
sourceClass = ((ClassMetadata) metadata).getClassName();
}
ConditionMessage.Builder message = ConditionMessage.forCondition("Cache",
sourceClass);
Environment environment = context.getEnvironment();
.........
}
CacheCondition继承自SpringBootCondition(实现了Condition接口), 实现getMatchOutcome方法, 返回ConditionOutcome, 用于判断是否初始化.
public class ConditionOutcome {
private final boolean match;
private final ConditionMessage message;
...........
}
match为true, 则初始化; 否则, springboot不会初始化RedisCacheConfiguration.
5. @Conditional由ConditionEvaluator处理, 而ConditionEvaluator和ImportSelector, 最终被ConfigurationClassParser调用
(processConfigurationClass->doProcessConfigurationClass->processImports)
不对之处, 敬请指点.