1.手写模拟SpringBoot核心流程
手写模拟SpringBoot关键点:
- 1)创建一个Spring容器,将启动类作为配置类传入;并且加上@ComponentScan默认将启动类所在的包路径作为扫描路径;
- 2)创建Servlet容器,比如Tomcat,并且配置DispatcherServlet
- 3)根据依赖启动相应容器,比如引入Tomcat依赖就启动Tomcat容器,引入Jetty依赖就启动Jetty容器。实现自动装配。
3-1)改为面向接口编程,先获取getWebServer(),然后调用webServer.start()启动;、
3-2)自动配置类WebServerAutoConfiguration,条件注解@Conditional加在TomcatWebServer和JettyWebServer的Bean上面,判断条件注解上的类是否存在,不存在则不加载相应的bean;
3-3)自己工程要导入自动配置类@Import(WebServerAutoConfiguration.class);
3-4)如果有很多自动配置类,每一个自动配置类都要Import,不仅需要改代码,并且很麻烦。此时可以通过Java SPI(META-INF/services)或者Spring SPI(META-INF/spring.factories)机制从classpath下面工程及所有jar包的固定目录进行加载。方便第三方软件整合SpringBoot,提供对应的自动配置类。
3-5)自动装配注解中的Import需要实现DeferredImprotSelector,这个的执行时机是在每一批次的配置类解析完之后才解析DeferredImprotSelector,此时DeferredImprotSelector才会通过SPI机制加载所有的自动配置类。是为了让程序员定义Bean优先级更好,自动配置类的Bean的条件注解会根据程序员是否定义了某个bean来决定当前bean是否注册到Spring容器。
3-6)引入依赖时,是按照starter进行引入,starter会将某个组件需要的相关依赖一起进行引入。
自动配置类对于没有引入依赖的那些条件注解里面的class是怎么处理的,为什么不报错?
- 比如我们依赖的springboot-web.jar(这里面引入了springboot-tomcat.jar),这些里面的类都是class格式,都是编译好的
- 编译不报错:我们自己工程编译时,是不会报错的,只需要编译我们自己写的代码,依赖的jar包都是已经编译好的,不用编译
- 运行不报错(跳过类加载机制获取注解上类的字符串信息,后面再通过类加载机制进行加载,如果抛异常则catch住并返回false):ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()中在(ConfigurationClassParser#processConfigurationClass中的this.conditionEvaluator.shouldSkip())处理配置类条件注解时,使用的是ASM技术进行处理。不是使用反射,使用反射会加载类,就会报错。ASM技术处理的是字节码文件,拿到字符串(类的全名),再用类加载器去加载(OnClassCondition#getMatchOutcom),如果能加载则返回true,不能加载(抛异常)则返回false。
2.SpringBoot创建Servlet容器的地方
2.1 创建WebServer
AbstractApplicationContext#refresh
调用ServletWebServerApplicationContext#onRefresh
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
ServletWebServerApplicationContext#createWebServer
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
ServletWebServerFactory factory = getWebServerFactory();
createWebServer.tag("factory", factory.getClass().toString());
this.webServer = factory.getWebServer(getSelfInitializer());
createWebServer.end();
getBeanFactory().registerSingleton("webServerGracefulShutdown",
new WebServerGracefulShutdownLifecycle(this.webServer));
getBeanFactory().registerSingleton("webServerStartStop",
new WebServerStartStopLifecycle(this, this.webServer));
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
来看看ServletWebServerApplicationContext#getWebServerFactory,这里表示Spring容器里面只能有一个ServletWebServerFactory类型的BD。
protected ServletWebServerFactory getWebServerFactory() {
// Use bean names so that we don't consider the hierarchy
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
2.2 ServletWebServerFactoryAutoConfiguration自动配置类
现在来着重看一下ServletWebServerFactory相关的自动配置类ServletWebServerFactoryAutoConfiguration:
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
return new ServletWebServerFactoryCustomizer(serverProperties,
webListenerRegistrars.orderedStream().collect(Collectors.toList()));
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
@Bean
@ConditionalOnMissingFilterBean(ForwardedHeaderFilter.class)
@ConditionalOnProperty(value = "server.forward-headers-strategy", havingValue = "framework")
public FilterRegistrationBean<ForwardedHeaderFilter> forwardedHeaderFilter() {
ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
FilterRegistrationBean<ForwardedHeaderFilter> registration = new FilterRegistrationBean<>(filter);
registration.setDispatcherTypes(DispatcherType.REQUEST, DispatcherType.ASYNC, DispatcherType.ERROR);
registration.setOrder(Ordered.HIGHEST_PRECEDENCE);
return registration;
}
/**
* Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
* {@link ImportBeanDefinitionRegistrar} for early registration.
*/
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class,
WebServerFactoryCustomizerBeanPostProcessor::new);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class, ErrorPageRegistrarBeanPostProcessor::new);
}
private <T> void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name,
Class<T> beanClass, Supplier<T> instanceSupplier) {
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass, instanceSupplier);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}
}
Import引入的EmbeddedTomcat、EmbeddedJetty和EmbeddedUndertow就是创建ServletWebServerFactory的配置类,比如EmbeddedTomcat,只有存在Tomcat依赖,并且Spring容器里面没有ServletWebServerFactory时才会创建TomcatServletWebServerFactory :
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
static class EmbeddedTomcat {
@Bean
TomcatServletWebServerFactory tomcatServletWebServerFactory(
ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers,
ObjectProvider<TomcatContextCustomizer> contextCustomizers,
ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
factory.getTomcatConnectorCustomizers()
.addAll(connectorCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatContextCustomizers()
.addAll(contextCustomizers.orderedStream().collect(Collectors.toList()));
factory.getTomcatProtocolHandlerCustomizers()
.addAll(protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
2.3 TomcatConnectorCustomizer对connector进行配置
@Bean方法的入参会进行依赖查找,Spring会去容器里面查找并注入,ObjectProvider表示可以为空。TomcatConnectorCustomizer可以对Tomcat的connector进行配置。这个在哪里使用?
详情请看TomcatServletWebServerFactory#getWebServer,其中customizeConnector就会调用之前设置到factory里面的TomcatConnectorCustomizer对connector进行配置:
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
Tomcat tomcat = new Tomcat();
File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
configureEngine(tomcat.getEngine());
for (Connector additionalConnector : this.additionalTomcatConnectors) {
tomcat.getService().addConnector(additionalConnector);
}
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
protected void customizeConnector(Connector connector) {
int port = Math.max(getPort(), 0);
connector.setPort(port);
if (StringUtils.hasText(getServerHeader())) {
connector.setProperty("server", getServerHeader());
}
if (connector.getProtocolHandler() instanceof AbstractProtocol) {
customizeProtocol((AbstractProtocol<?>) connector.getProtocolHandler());
}
invokeProtocolHandlerCustomizers(connector.getProtocolHandler());
if (getUriEncoding() != null) {
connector.setURIEncoding(getUriEncoding().name());
}
// Don't bind to the socket prematurely if ApplicationContext is slow to start
connector.setProperty("bindOnInit", "false");
if (getSsl() != null && getSsl().isEnabled()) {
customizeSsl(connector);
}
TomcatConnectorCustomizer compression = new CompressionConnectorCustomizer(getCompression());
compression.customize(connector);
for (TomcatConnectorCustomizer customizer : this.tomcatConnectorCustomizers) {
customizer.customize(connector);
}
}
2.4 application.properties中的属性是怎么处理的?
另一种配置属性的方法(比如配置server.port=8084),是在属性配置文件比如application.properties里面进行配置。那这个是怎么获取到配置文件中的属性?
这就涉及到ServletWebServerFactoryAutoConfiguration 自动配置Import导入的另外一个类ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class。BeanPostProcessorsRegistrar是个ImportBeanDefinitionRegistrar,其registerBeanDefinitions()方法注册了WebServerFactoryCustomizerBeanPostProcessor。
WebServerFactoryCustomizerBeanPostProcessor是个BeanPostProcessor,它主要重写了postProcessBeforeInitialization()方法,也就是在初始化Bean之前调用的。
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
该BeanPostProcessor主要处理的是WebServerFactory的初始化,比如TomcatServletWebServerFactory。
这里主要调用的WebServerFactoryCustomizer对WebServerFactory进行定制,WebServerFactoryCustomizer在哪个地方定义了?就在ServletWebServerFactoryAutoConfiguration自动配置类里面:
@Bean
public ServletWebServerFactoryCustomizer servletWebServerFactoryCustomizer(ServerProperties serverProperties,
ObjectProvider<WebListenerRegistrar> webListenerRegistrars) {
return new ServletWebServerFactoryCustomizer(serverProperties,
webListenerRegistrars.orderedStream().collect(Collectors.toList()));
}
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
来看一下ServletWebServerFactoryCustomizer#customize,这里面就会从配置文件获取server.port设置到factory对于的port属性里面去。
public class ServletWebServerFactoryCustomizer
implements WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>, Ordered {
private final ServerProperties serverProperties;
public void customize(ConfigurableServletWebServerFactory factory) {
PropertyMapper map = PropertyMapper.get().alwaysApplyingWhenNonNull();
map.from(this.serverProperties::getPort).to(factory::setPort);
map.from(this.serverProperties::getAddress).to(factory::setAddress);
map.from(this.serverProperties.getServlet()::getContextPath).to(factory::setContextPath);
map.from(this.serverProperties.getServlet()::getApplicationDisplayName).to(factory::setDisplayName);
map.from(this.serverProperties.getServlet()::isRegisterDefaultServlet).to(factory::setRegisterDefaultServlet);
map.from(this.serverProperties.getServlet()::getSession).to(factory::setSession);
map.from(this.serverProperties::getSsl).to(factory::setSsl);
map.from(this.serverProperties.getServlet()::getJsp).to(factory::setJsp);
map.from(this.serverProperties::getCompression).to(factory::setCompression);
map.from(this.serverProperties::getHttp2).to(factory::setHttp2);
map.from(this.serverProperties::getServerHeader).to(factory::setServerHeader);
map.from(this.serverProperties.getServlet()::getContextParameters).to(factory::setInitParameters);
map.from(this.serverProperties.getShutdown()).to(factory::setShutdown);
for (WebListenerRegistrar registrar : this.webListenerRegistrars) {
registrar.register(factory);
}
}
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
3.条件注解
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
来看看该自动配置类上面的两个条件注解:
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnWebApplicationCondition.class)
public @interface ConditionalOnWebApplication {
重点看SpringBootCondition#matches()
public final boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String classOrMethodName = getClassOrMethodName(metadata);
try {
ConditionOutcome outcome = getMatchOutcome(context, metadata);
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException("Could not evaluate condition on " + classOrMethodName + " due to "
+ ex.getMessage() + " not found. Make sure your own configuration does not rely on "
+ "that class. This can also happen if you are "
+ "@ComponentScanning a springframework package (e.g. if you "
+ "put a @ComponentScan in the default package by mistake)", ex);
}
catch (RuntimeException ex) {
throw new IllegalStateException("Error processing condition on " + getName(metadata), ex);
}
}
该方法执行步骤:
- 1)获取条件注解写在哪个类上或者哪个方法上
- 2)获取条件的判断结果getMatchOutcome(),结果放在ConditionOutcome
- 3)两种日志打印,一种是即时打印,一种是容器启动完成后打印(通过ConditionEvaluationReportLoggingListener监听器监听ContextRefreshedEvent事件进行统一打印)
3.1 ConditionalOnClass、ConditionalOnMissingClass
现在来看子类的实现OnClassCondition#getMatchOutcome:
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ClassLoader classLoader = context.getClassLoader();
ConditionMessage matchMessage = ConditionMessage.empty();
List<String> onClasses = getCandidates(metadata, ConditionalOnClass.class);
if (onClasses != null) {
List<String> missing = filter(onClasses, ClassNameFilter.MISSING, classLoader);
if (!missing.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class)
.didNotFind("required class", "required classes").items(Style.QUOTE, missing));
}
matchMessage = matchMessage.andCondition(ConditionalOnClass.class)
.found("required class", "required classes")
.items(Style.QUOTE, filter(onClasses, ClassNameFilter.PRESENT, classLoader));
}
List<String> onMissingClasses = getCandidates(metadata, ConditionalOnMissingClass.class);
if (onMissingClasses != null) {
List<String> present = filter(onMissingClasses, ClassNameFilter.PRESENT, classLoader);
if (!present.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnMissingClass.class)
.found("unwanted class", "unwanted classes").items(Style.QUOTE, present));
}
matchMessage = matchMessage.andCondition(ConditionalOnMissingClass.class)
.didNotFind("unwanted class", "unwanted classes")
.items(Style.QUOTE, filter(onMissingClasses, ClassNameFilter.MISSING, classLoader));
}
return ConditionOutcome.match(matchMessage);
}
该方法执行步骤(ConditionalOnClass、ConditionalOnMissingClass都是走@Conditional(OnClassCondition.class),都是在OnClassCondition中进行统一处理):
- 1)getCandidates()拿到ConditionOnClass注解中的value值,也就是要判断是否存在的类名
- 2)filter()将onClasses中不存在的类找出来,ClassNameFilter.MISSING就是通过类加载器去加载这些类,看这些类是否存在,如果不存在则记录下这些类
- 3)这里会同时处理ConditionalOnMissingClass,就是看该自动配置类上面有没有该注解,如果有则拿到该注解的value值,也就是要判断是否存在的类名
- 4)filter()将onMissingClasses中存在的类找出来,ClassNameFilter.PRESENT就是通过类加载器去加载这些类,看这些类是否存在,如果存在则记录下这些类
具体的filter方法是FilteringSpringBootCondition#filter:
protected final List<String> filter(Collection<String> classNames, ClassNameFilter classNameFilter,
ClassLoader classLoader) {
if (CollectionUtils.isEmpty(classNames)) {
return Collections.emptyList();
}
List<String> matches = new ArrayList<>(classNames.size());
for (String candidate : classNames) {
if (classNameFilter.matches(candidate, classLoader)) {
matches.add(candidate);
}
}
return matches;
}
传入的classNameFilter是ClassNameFilter.MISSING:
MISSING {
@Override
public boolean matches(String className, ClassLoader classLoader) {
return !isPresent(className, classLoader);
}
};
static boolean isPresent(String className, ClassLoader classLoader) {
if (classLoader == null) {
classLoader = ClassUtils.getDefaultClassLoader();
}
try {
resolve(className, classLoader);
return true;
}
catch (Throwable ex) {
return false;
}
}
3.2 ConditionalOnBean、ConditionalOnSingleCandidate和ConditionalOnMissingBean
这三个条件注解都走的是@Conditional(OnBeanCondition.class),也即OnBeanCondition。核心在OnBeanCondition#getMatchOutcome方法中。
3.3 ConditionalOnJava
@Conditional(OnJavaCondition.class)
public @interface ConditionalOnJava {
OnJavaCondition#getMatchOutcome去判断JDK版本时,是通过下面的特征类的特征方法进行判定的,就是看存不存在,判定时是逆序的(从高版本开始判定)。
public enum JavaVersion {
/**
* Java 1.8.
*/
EIGHT("1.8", Optional.class, "empty"),
/**
* Java 9.
*/
NINE("9", Optional.class, "stream"),
/**
* Java 10.
*/
TEN("10", Optional.class, "orElseThrow"),
/**
* Java 11.
*/
ELEVEN("11", String.class, "strip"),
/**
* Java 12.
*/
TWELVE("12", String.class, "describeConstable"),
/**
* Java 13.
*/
THIRTEEN("13", String.class, "stripIndent"),
/**
* Java 14.
*/
FOURTEEN("14", MethodHandles.Lookup.class, "hasFullPrivilegeAccess"),
/**
* Java 15.
*/
FIFTEEN("15", CharSequence.class, "isEmpty"),
/**
* Java 16.
*/
SIXTEEN("16", Stream.class, "toList");
3.4 ConditionalOnWebApplication和ConditionalOnNotWebApplication
这两个都是基于OnWebApplicationCondition
3.5 ConditionalOnProperty
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
核心就是看Environment对象里面有没有配置该属性。
OnPropertyCondition#getMatchOutcome
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
List<AnnotationAttributes> allAnnotationAttributes = annotationAttributesFromMultiValueMap(
metadata.getAllAnnotationAttributes(ConditionalOnProperty.class.getName()));
List<ConditionMessage> noMatch = new ArrayList<>();
List<ConditionMessage> match = new ArrayList<>();
for (AnnotationAttributes annotationAttributes : allAnnotationAttributes) {
ConditionOutcome outcome = determineOutcome(annotationAttributes, context.getEnvironment());
(outcome.isMatch() ? match : noMatch).add(outcome.getConditionMessage());
}
if (!noMatch.isEmpty()) {
return ConditionOutcome.noMatch(ConditionMessage.of(noMatch));
}
return ConditionOutcome.match(ConditionMessage.of(match));
}
context.getEnvironment()就是获取Environment对象,Environment实现了PropertyResolver接口。
4.@SpringBootApplication
4.1 SpringBoot自动装配原理
- 1.核心是@SpringBootApplication,包含三个注解:@SpringBootConfiguration、@EnableAutoConfiguration和@ComponentScan;
- 2.@EnableAutoConfiguration表明启动类是个配置类,@ComponentScan默认情况下指定启动类所在的包路径为扫描路径;
- 3.重点是@EnableAutoConfiguration,它导入了一个类@Import(AutoConfigurationImportSelector.class),AutoConfigurationImportSelector是个DeferredImportSelector。
3-1)ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry()会在每一批次的配置类解析完才处理DeferredImportSelector,这样可以让程序员自定义的Bean优先级更高;
3-2)AutoConfigurationImportSelector.AutoConfigurationGroup#process会通过Spring SPI机制读取类路径和jar包中META-INF/spring.factories文件中的自动配置类(key为org.springframework.boot.autoconfigure.EnableAutoConfiguration),这里会根据三个Conditional注解进行初步筛选
3-3)完整的条件注解筛选会在ConfigurationClassParser#processConfigurationClass中的this.conditionEvaluator.shouldSkip()方法里面处理。这里就会根据Starter引入依赖、程序员是否定义某个Bean等各种条件注解判定某个Bean是否需要注册到Spring容器。
4.2 AutoConfigurationImportSelector源码剖析
AutoConfigurationImportSelector.AutoConfigurationGroup#process
public void process(AnnotationMetadata annotationMetadata, DeferredImportSelector deferredImportSelector) {
Assert.state(deferredImportSelector instanceof AutoConfigurationImportSelector,
() -> String.format("Only %s implementations are supported, got %s",
AutoConfigurationImportSelector.class.getSimpleName(),
deferredImportSelector.getClass().getName()));
AutoConfigurationEntry autoConfigurationEntry = ((AutoConfigurationImportSelector) deferredImportSelector)
.getAutoConfigurationEntry(annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
该方法执行步骤:
- 1)getAttributes()获取@EnableAutoConfiguration的属性(exclude、excludeName)
- 2)getCandidateConfigurations()通过Spring SPI机制获取META-INF/spring.factories中所有的AutoConfiguration自动配置类
- 3)removeDuplicates()去重
- 4)获取需要排除的自动配置类,@EnableAutoConfiguration的属性exclude、excludeName或者通过配置属性spring.autoconfigure.exclude进行排除
- 5)filter()对自动配置类进行初步过滤,使用三个条件过滤器OnBeanCondition、OnClassCondition、OnWebApplciationCondition(包含的注解:ConditionOnClass、ConditionOnBean、ConditionOnSingleCandidate、ConditionalOnWebApplication),spring-boot-autoconfigure jar包里面有一个文件spring-autoconfigure-metadata.properties(编译生成的)。这里是直接读取该文件,然后根据该文件里面对各个类的条件注解(特别注意:这里的条件注解是不全的)进行过滤。
protected List<AutoConfigurationImportFilter> getAutoConfigurationImportFilters() {
return SpringFactoriesLoader.loadFactories(AutoConfigurationImportFilter.class, this.beanClassLoader);
}
# Auto Configuration Import Filters
org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\
org.springframework.boot.autoconfigure.condition.OnBeanCondition,\
org.springframework.boot.autoconfigure.condition.OnClassCondition,\
org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition
来追踪一下filter()方法:
List<String> filter(List<String> configurations) {
long startTime = System.nanoTime();
String[] candidates = StringUtils.toStringArray(configurations);
boolean skipped = false;
for (AutoConfigurationImportFilter filter : this.filters) {
boolean[] match = filter.match(candidates, this.autoConfigurationMetadata);
for (int i = 0; i < match.length; i++) {
if (!match[i]) {
candidates[i] = null;
skipped = true;
}
}
}
if (!skipped) {
return configurations;
}
List<String> result = new ArrayList<>(candidates.length);
for (String candidate : candidates) {
if (candidate != null) {
result.add(candidate);
}
}
if (logger.isTraceEnabled()) {
int numberFiltered = configurations.size() - result.size();
logger.trace("Filtered " + numberFiltered + " auto configuration class in "
+ TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startTime) + " ms");
}
return result;
}
FilteringSpringBootCondition#match
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
ConditionEvaluationReport report = ConditionEvaluationReport.find(this.beanFactory);
ConditionOutcome[] outcomes = getOutcomes(autoConfigurationClasses, autoConfigurationMetadata);
boolean[] match = new boolean[outcomes.length];
for (int i = 0; i < outcomes.length; i++) {
match[i] = (outcomes[i] == null || outcomes[i].isMatch());
if (!match[i] && outcomes[i] != null) {
logOutcome(autoConfigurationClasses[i], outcomes[i]);
if (report != null) {
report.recordConditionEvaluation(autoConfigurationClasses[i], this, outcomes[i]);
}
}
}
return match;
}
这里getOutcomes()就只在上面三个filter里面重写了,这里过滤方法就是根据条件注解进行过滤,没有本质区别,只不过这里使用的条件注解只是局限于某几个(ConditionOnClass、ConditionOnBean、ConditionOnSingleCandidate、ConditionalOnWebApplication):
4.3 @AutoConfigurationPackage注解
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
会执行ImportBeanDefinitionRegistrar#registerBeanDefinitions
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
PackageImports(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
List<String> packageNames = new ArrayList<>(Arrays.asList(attributes.getStringArray("basePackages")));
for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
packageNames.add(basePackageClass.getPackage().getName());
}
if (packageNames.isEmpty()) {
packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
}
this.packageNames = Collections.unmodifiableList(packageNames);
}
AutoConfigurationPackage该注解单独使用时,可以指定包路径basePackages。在这里是没有值的。
然后就会获取启动类所在的包路径。
最后会将该包路径封装成BasePackagesBeanDefinition,名字为AutoConfigurationPackages.class.getName(),注册到Spring容器。
那这个路径谁用呢?Mybatis可以用,让Mybatis少一些配置,直接使用这个包路径就可以。Mybatis的扫描器就会去获取该包路径的Bean:
public static List<String> get(BeanFactory beanFactory) {
try {
return beanFactory.getBean(BEAN, BasePackages.class).get();
}
catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("Unable to retrieve @EnableAutoConfiguration base packages");
}
}
4.4 @ComponentScan里面的两个exclueFilter
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
public class AutoConfigurationExcludeFilter implements TypeFilter, BeanClassLoaderAware {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
return isConfiguration(metadataReader) && isAutoConfiguration(metadataReader);
}
private boolean isConfiguration(MetadataReader metadataReader) {
return metadataReader.getAnnotationMetadata().isAnnotated(Configuration.class.getName());
}
private boolean isAutoConfiguration(MetadataReader metadataReader) {
return getAutoConfigurations().contains(metadataReader.getClassMetadata().getClassName());
}
AutoConfigurationExcludeFilter会过滤掉是自动配置类的配置类,首先判断是否有@Configuration注解,然后判断自动配置类(通过SPI机制加载的key为EnableAutoConfiguration)是否包含该配置类。
为什么要这么做?
因为自动配置类要在每一轮配置类扫描完成后才进行处理,不需要在扫描的时候就处理。
public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
throws IOException {
if (this.beanFactory instanceof ListableBeanFactory && getClass() == TypeExcludeFilter.class) {
for (TypeExcludeFilter delegate : getDelegates()) {
if (delegate.match(metadataReader, metadataReaderFactory)) {
return true;
}
}
}
return false;
}
private Collection<TypeExcludeFilter> getDelegates() {
Collection<TypeExcludeFilter> delegates = this.delegates;
if (delegates == null) {
delegates = ((ListableBeanFactory) this.beanFactory).getBeansOfType(TypeExcludeFilter.class).values();
this.delegates = delegates;
}
return delegates;
}
会从Spring容器中获取所有TypeExcludeFilter,然后进行匹配,匹配成功的则不进行解析。
特别注意:
- 1)如果这里先定义一个TypeExcludeFilter作为@Bean注册到Spring容器,并过滤某个@Component,是不会生效的
- 2)为什么,跟顺序有关,因为@Component、@ComponentScan会先进行处理,@Bean后进行处理,@ComponentScan进行扫描时,@Bean注解TypeExcludeFilter在容器里面还不存在,所以不起作用。
- 3)@Component注解TypeExcludeFilter也不一定生效,也有个顺序问题
那怎么解决这个问题?
- 关键就是要将TypeExcludeFilter在扫描之前就加入到Spring容器
- 可以定义ApplicationContextInitializer,并配置到spring.factories文件里面
- 然后在ApplicationContextInitializer#initialize中applicationContext.getBeanFactory().registerSingleton()向Spring容器注册TypeExcludeFilter
- 当然也可以用BeanFactoryRegistryPostProcessor,并且是要程序手动向Spring容器添加的,这样就会在ConfigurationClassPostProcessor之前执行,但是这样做不太规范,没法实现。
5.starter包
Starter 其实就是一个 jar 包,在 pom 中引入一个 starter 其实就是引入了一个 jar 包。而且很多时候,这个 starter 对应的 jar 包是个空的,里面并没有任何类和接口。
那这个空的 jar 包有何意义呢?它的意义就在于为了引入有意义的其它 jar 包,因为这些 jar 包都是基于 Maven 的,因此 jar 包的 pom 文件中包含了其它依赖。而且SpringBoot会把常用的依赖及其适合的版本号都通过依赖管理的方式包含在 pom 文件中。
一般SpringBoot已经整合的组件,也就是SpringBoot提供的starter包里面只会有相关依赖(自动配置类已经写好了);而对于第三方组件想要整合SpringBoot,还需要加入自动配置的jar包(比如mybatis-spring-boot-starter里面还包括mybatis-spring-boot-autoconfigure的jar包)。
6.SpringBoot启动过程
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
// 构造SpringApplication对象
return new SpringApplication(primarySources).run(args);
}
6.1 构造SpringApplication对象
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1. 推测web应用类型(NONE、REACTIVE、SERVLET)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 2. 从spring.factories中获取BootstrapRegistryInitializer对象
this.bootstrapRegistryInitializers = new ArrayList<>(
getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 3. 从spring.factories中获取ApplicationContextInitializer对象
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 4. 从spring.factories中获取ApplicationListener对象
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 5. 推测出Main类(main()方法所在的类)
this.mainApplicationClass = deduceMainApplicationClass();
}
xxx\spring-boot-2.6.6-master\spring-boot-project\spring-boot\src\main\resources\META-INF\spring.factories
# Application Context Initializers
org.springframework.context.ApplicationContextInitializer=\
org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer,\
org.springframework.boot.context.ContextIdApplicationContextInitializer,\
org.springframework.boot.context.config.DelegatingApplicationContextInitializer,\
org.springframework.boot.rsocket.context.RSocketPortInfoApplicationContextInitializer,\
org.springframework.boot.web.context.ServerPortInfoApplicationContextInitializer
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.env.EnvironmentPostProcessorApplicationListener
6.2 核心启动流程在run()方法里面
public ConfigurableApplicationContext run(String... args) {
long startTime = System.nanoTime();
// 1、创建引导启动器,类似一个ApplicationContext,可以往里面添加一些对象
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 2、从spring.factories中获取SpringApplicationRunListener对象
// 默认会拿到一个EventPublishingRunListener,它会启动过程的各个阶段发布对应的ApplicationEvent事件
SpringApplicationRunListeners listeners = getRunListeners(args);
// 3、发布ApplicationStartingEvent
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 4、将run()的参数封装为DefaultApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 5、准备Environment
// 包括操作系统,JVM、ServletContext、properties、yaml等等配置
// 会发布一个ApplicationEnvironmentPreparedEvent
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 默认spring.beaninfo.ignore=true,表示不需要jdk缓存beanInfo信息,Spring自己会缓存
configureIgnoreBeanInfo(environment);
Banner printedBanner = printBanner(environment);
// 6、根据应用类型创建Spring容器
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 7、利用ApplicationContextInitializer初始化Spring容器
// 8、发布ApplicationContextInitializedEvent
// 9、关闭DefaultBootstrapContext
// 10、注册primarySources类,就是run方法存入进来的配置类
// 11、发布ApplicationPreparedEvent事件
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 12、刷新Spring容器,会解析配置类、扫描、启动WebServer
refreshContext(context);
// 空方法
afterRefresh(context, applicationArguments);
// 启动时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
}
// 13、发布ApplicationStartedEvent事件,表示Spring容器已经启动
listeners.started(context, timeTakenToStartup);
// 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
// 15、发布ApplicationFailedEvent事件
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
// 16、发布ApplicationReadyEvent事件,表示Spring容器已经准备好了
listeners.ready(context, timeTakenToReady);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
6.2.1 createBootstrapContext()
private DefaultBootstrapContext createBootstrapContext() {
DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext();
// 利用bootstrapRegistryInitializers初始化DefaultBootstrapContext
this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext));
return bootstrapContext;
}
6.2.2 SpringApplicationRunListener
SpringApplicationRunListeners#starting
void starting(ConfigurableBootstrapContext bootstrapContext, Class<?> mainApplicationClass) {
doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
(step) -> {
if (mainApplicationClass != null) {
step.tag("mainApplicationClass", mainApplicationClass.getName());
}
});
}
private void doWithListeners(String stepName, Consumer<SpringApplicationRunListener> listenerAction,
Consumer<StartupStep> stepAction) {
StartupStep step = this.applicationStartup.start(stepName);
this.listeners.forEach(listenerAction);
if (stepAction != null) {
stepAction.accept(step);
}
step.end();
}
其中listeners只有一个,就是EventPublishingRunListener
class SpringApplicationRunListeners {
private final List<SpringApplicationRunListener> listeners;
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
public void starting(ConfigurableBootstrapContext bootstrapContext) {
this.initialMulticaster
.multicastEvent(new ApplicationStartingEvent(bootstrapContext, this.application, this.args));
}
EventPublishingRunListener#starting就是发布事件,然后调用ApplicationListener进行处理。这里可以使用SimpleApplicationEventMulticaster的taskExecutor线程池进行异步调用监听器进行处理(如果有的话)。
6.3 prepareEnvironment()
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 创建ApplicationServletEnvironment,里面添加了四个PropertySource
// 1. StubPropertySource {name='servletConfigInitParams'}
// 2. StubPropertySource {name='servletContextInitParams'}
// 3. PropertiesPropertySource {name='systemProperties'}
// 4. SystemEnvironmentPropertySource {name='systemEnvironment'}
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 添加SimpleCommandLinePropertySource {name='commandLineArgs'},放在首位
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 把所有的PropertySources封装为一个ConfigurationPropertySourcesPropertySource
// 然后添加到environment中,放在首位
ConfigurationPropertySources.attach(environment);
// 发布ApplicationEnvironmentPreparedEvent事件,表示环境已经准备好了
// 默认EnvironmentPostProcessorApplicationListener会处理这个事件,会从spring.factories中拿出EnvironmentPostProcessor进一步处理Environment
listeners.environmentPrepared(bootstrapContext, environment);
// 最后,把defaultProperties移到最后
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = convertEnvironment(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
6.4 createApplicationContext()
ApplicationContextFactory中的DEFAULT里面进行创建:
ApplicationContextFactory DEFAULT = (webApplicationType) -> {
try {
switch (webApplicationType) {
case SERVLET:
return new AnnotationConfigServletWebServerApplicationContext();
case REACTIVE:
return new AnnotationConfigReactiveWebServerApplicationContext();
default:
return new AnnotationConfigApplicationContext();
}
}
catch (Exception ex) {
throw new IllegalStateException("Unable create a default ApplicationContext instance, "
+ "you may need a custom ApplicationContextFactory", ex);
}
};
6.5 prepareContext()
主要是做几件事:
- 1)利用ApplicationContextInitializer初始化Spring容器
- 2)发布ApplicationContextInitializedEvent
- 3)关闭DefaultBootstrapContext
- 4)注册primarySources类,就是run方法存入进来的配置类
- 5)发布ApplicationPreparedEvent事件
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
// 将前面生成的Environment设置到Spring容器中
context.setEnvironment(environment);
// 将设置在SpringApplication上的beanNameGenerator、resourceLoader设置到Spring容器中
postProcessApplicationContext(context);
// 利用ApplicationContextInitializer初始化Spring容器
applyInitializers(context);
// 发布ApplicationContextInitializedEvent事件,表示Spring容器初始化完成
listeners.contextPrepared(context);
// Spring容器初始化好了,就关闭DefaultBootstrapContext
bootstrapContext.close(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 注册一些单例Bean
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
// 设置allowCircularReferences和allowBeanDefinitionOverriding给Spring容器
if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {
((AbstractAutowireCapableBeanFactory) beanFactory).setAllowCircularReferences(this.allowCircularReferences);
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
// 拿到启动配置类(run方法传进来的类)
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
// 将启动配置类解析为BeanDefinition注册到Spring容器中
load(context, sources.toArray(new Object[0]));
// 发布ApplicationPreparedEvent事件,表示Spring容器已经准备好
listeners.contextLoaded(context);
}
6.6 入参
// 4、将run()的参数封装为DefaultApplicationArguments对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 14、从Spring容器中获取ApplicationRunner和CommandLineRunner,并执行其run()
callRunners(context, applicationArguments);
callRunners()会去Spring容器查找ApplicationRunner和CommandLineRunner执行,执行时会传入入参applicationArguments。
7.重点分析一下各种配置的解析prepareEnvironment()
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
// Create and configure the environment
// 创建ApplicationServletEnvironment,里面添加了四个PropertySource
// 1. StubPropertySource {name='servletConfigInitParams'}
// 2. StubPropertySource {name='servletContextInitParams'}
// 3. PropertiesPropertySource {name='systemProperties'}
// 4. SystemEnvironmentPropertySource {name='systemEnvironment'}
ConfigurableEnvironment environment = getOrCreateEnvironment();
// 添加SimpleCommandLinePropertySource {name='commandLineArgs'},放在首位
configureEnvironment(environment, applicationArguments.getSourceArgs());
// 把所有的PropertySources封装为一个ConfigurationPropertySourcesPropertySource
// 然后添加到environment中,放在首位
ConfigurationPropertySources.attach(environment);
// 发布ApplicationEnvironmentPreparedEvent事件,表示环境已经准备好了
// 默认EnvironmentPostProcessorApplicationListener会处理这个事件,会从spring.factories中拿出EnvironmentPostProcessor进一步处理Environment
listeners.environmentPrepared(bootstrapContext, environment);
// 最后,把defaultProperties移到最后
DefaultPropertiesPropertySource.moveToEnd(environment);
Assert.state(!environment.containsProperty("spring.main.environment-prefix"),
"Environment prefix cannot be set via properties.");
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = convertEnvironment(environment);
}
ConfigurationPropertySources.attach(environment);
return environment;
}
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
一个PropertySource就代表一个配置文件(解析完后的内容)。
private void onApplicationEnvironmentPreparedEvent(ApplicationEnvironmentPreparedEvent event) {
ConfigurableEnvironment environment = event.getEnvironment();
SpringApplication application = event.getSpringApplication();
// 从spring.factories中拿出EnvironmentPostProcessor进一步处理Environment
// RandomValuePropertySourceEnvironmentPostProcessor
// SystemEnvironmentPropertySourceEnvironmentPostProcessor
// SpringApplicationJsonEnvironmentPostProcessor
// CloudFoundryVcapEnvironmentPostProcessor
// ConfigDataEnvironmentPostProcessor
// IntegrationPropertiesEnvironmentPostProcessor
// DebugAgentEnvironmentPostProcessor
for (EnvironmentPostProcessor postProcessor : getEnvironmentPostProcessors(application.getResourceLoader(),
event.getBootstrapContext())) {
postProcessor.postProcessEnvironment(environment, application);
}
}
来看看这些EnvironmentPostProcessor的作用
- RandomValuePropertySourceEnvironmentPostProcessor添加了一个random
- SystemEnvironmentPropertySourceEnvironmentPostProcessor有前缀的情况下会替换systemEnvironment(操作系统的环境变量)。Environment variables中进行配置。这个前缀可用于区分环境,操作系统级别的环境。比如如果配置了前缀zhouyu,必须配置成zhouyu.k5=v5才能获取到k5的值。
- SpringApplicationJsonEnvironmentPostProcessor会去获取spring.application.json里面配置的json字符串,然后解析出来后放到PropertySource。只能配置在VM options里面或者Program arguments里面,也就是只能配置在已经解析完成的属性文件里面。此时properties还没解析,配置无用。
- CloudFoundryVcapEnvironmentPostProcessor
- ConfigDataEnvironmentPostProcessor在三个参数指定的路径下面去找properties或者yaml文件
整体流程:
SpringBoot完整的配置优先级
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config
网友评论