文章目录
SpringBoot启动时做了什么?
【基于2.2.2.RELEASE】
在引入了SprinBoot依赖后,我们通常只需要如下一个简单的启动类,就可以完成整个SpringBoot项目的启动使用:
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class,args);
}
}
SpringBoot实际就是基于Spring,只是帮我们进行了一些大量的自动化配置,使我们无须关心太多就可以轻松上手使用,因此我们在阅读源码的时候要关注如下几个问题:
- 在如上的启动类中,SpringBoot是如何与Spring集成的?
- 又是如何启动内嵌的web服务器的?
- 以及大量的类是如何进行收集加载到Spring容器中的?springboot如何完成这些自动化装配?
- AOP啊、缓存啊、事务啊等等功能是怎么加到Springboot的?
如果要把类加载到Spring中,有哪些方式呢?其实在前面文章讲解Spring的IOC容器和实例化的源码时也介绍了,无非以下几种:
- 使用
@Component
注解使之被@ComponentScan
注解扫描到(当然@Configuration相关注解也可以) - 通过xml配置文件的方式,在xml中配置bean标签;
- 基于SPI服务发现的机制,在
spring.factories
文件中配置接口和要实现类的关系,Spring在启动时会加载文件中的类到容器中管理,该方式通常用于管理第三方包中的bean,因为第三方包的命名和当前工程肯定不一样,不好通过@ComponentScan管理。
一、SpringApplication构造【准备】
从run方法点进去,最终会来到SpringApplication#ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args)
位置,大概在1257行。
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这里对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.1
this.webApplicationType = WebApplicationType.deduceFromClasspath();
//1.2
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
说明:
1.1 服务启动类型判断
webApplicationType:服务的启动类型,共有三个类型:
static WebApplicationType deduceFromClasspath() {
if (ClassUtils.isPresent(“org.springframework.web.reactive.DispatcherHandler”, null) && !ClassUtils.isPresent(“org.springframework.web.servlet.DispatcherServlet”, null)
&& !ClassUtils.isPresent(“org.glassfish.jersey.servlet.ServletContainer”, null)) {
return WebApplicationType.REACTIVE;
}
for (String className : ["javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext"]) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
return WebApplicationType.SERVLET;
}
REACTIVE
:Reactive web是响应式编程的一种新的编程风格,要使用Reactive的嵌入式web服务器SERVLET
:默认的servlet web类型,可以使用内嵌的web服务器NONE
:非web应用,不能使用内嵌的web服务器
这里会通过ClassUtils.isPresent判断当前classpath下某些class是否存在来返回具体的web类型,默认都会是ServletWeb
1.2 加载应用上下文初始器 ApplicationContextInitializer
这里会通过SPI的方式从spring.factories
文件中加载:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
ClassLoader classLoader = getClassLoader();
// 1.2.1Use names and ensure unique to protect against duplicates
Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 1.2.2 加载上来后反射实例化
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
1.2.1 SPI服务加载
Spring中的SPI基本都是通过SpringFactoriesLoader.loadFactoryNames
方法进行加载的:
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
这里的factoryTypeName就是org.springframework.context.ApplicationContextInitializer
。
继续:
这里就是具体收集文件中所有接口的实现类:
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
//从缓存中取,第一次没有,加载完毕后会将结果放到该缓存中
MultiValueMap<String, String> result = cache.get(classLoader);
if (result != null) {
return result;
}
try {
//拿到spring下所有项目的spring.factories文件
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources("META-INF/spring.factories") :
ClassLoader.getSystemResources("META-INF/spring.factories"));
result = new LinkedMultiValueMap<>();
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
//遍历当前配置文件的所有key-value
for (Map.Entry<?, ?> entry : properties.entrySet()) {
String factoryTypeName = ((String) entry.getKey()).trim();
//因为一个key下可以有多个实现类(value),因此要将每个实现类都添加进去
for (String factoryImplementationName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) {
result.add(factoryTypeName, factoryImplementationName.trim());
}
}
}
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
最后会通过getOrDefault(factoryTypeName, Collections.emptyList());
根据当前的factoryTypeName(ApplicationContextInitializer
)拿到其下的所有实现类:
1.2.2 实例化实现类
拿到所有的实现类Set<String>names后,就调用createSpringFactoriesInstances
方法,内部遍历names通过反射实例化添加到集合中返回。
最后在构造函数中将集合保存到List<ApplicationContextInitializer<?>> initializers;
中。
1.3 加载应用事件监听器ApplicationListener
这里同样的方式从spring.factories
加载以ApplicationListener
为key的监听器实现类,由于上边已经加载过了,因此这里可直接从缓存中获取。
1.4 获取启动类
在构造的最后,会通过``getStackTrace()`方法获取当前线程的执行栈,再遍历判断方法名是否为"main"来获取main方法所在的类,对其进行实例化返回。
二、SpringApplication运行
在准备工作做好后,就来到了具体的run()方法,大概在298行。
2.1 加载SpringApplicationRunListener
public ConfigurableApplicationContext run(String... args) {
// 统计时间的计时器
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
configureHeadlessProperty();
// 获取实现了SpringApplicationRunListener接口的实现类,通过SPI机制加载
SpringApplicationRunListeners listeners = getRunListeners(args);
// 首先调用SpringApplicationRunListener的starting方法
listeners.starting();
}
在这里同样以SPI的方式获取所有实现了SpringApplicationRunListener
接口的类,放到SpringApplicationRunListeners
中的SpringApplicationRunListener
集合,并通过listeners.starting();
遍历所有的listener调用其starting方法。
# Run Listeners
org.springframework.boot.SpringApplicationRunListener=\
org.springframework.boot.context.event.EventPublishingRunListener
这里SpringApplicationRunListener
主要负责在springboot的各个阶段广播对应的事件:
SpringApplicationRunListeners
:
private final Log log;
private final List<SpringApplicationRunListener> listeners;
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners) {
this.log = log;
this.listeners = new ArrayList<>(listeners);
}
void starting() {
for (SpringApplicationRunListener listener : this.listeners) {
listener.starting();
}
}
void environmentPrepared(ConfigurableEnvironment environment) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.environmentPrepared(environment);
}
}
void contextPrepared(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextPrepared(context);
}
}
void contextLoaded(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.contextLoaded(context);
}
}
void started(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.started(context);
}
}
void running(ConfigurableApplicationContext context) {
for (SpringApplicationRunListener listener : this.listeners) {
listener.running(context);
}
}
void failed(ConfigurableApplicationContext context, Throwable exception) {
for (SpringApplicationRunListener listener : this.listeners) {
callFailedListener(listener, context, exception);
}
}
根据方法名可以很容易推断出对应的事件阶段,具体会调用SpringApplicationRunListener
的实现类EventPublishingRunListener
。
2.2 封装启动参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
args就是启动传入的参数,这里会将其封装成ApplicationArguments
。我们可以直接通过@Autowired注入ApplicationArguments
调用其方法,获取相应的参数信息。
2.3 加载外部化配置
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
configureIgnoreBeanInfo(environment);
///
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) {
// 1.通过前面的webApplicationType返回不同的ConfigurableEnvironment
//通常是servlet的StandardServletEnvironment
ConfigurableEnvironment environment = getOrCreateEnvironment();
configureEnvironment(environment, applicationArguments.getSourceArgs());
ConfigurationPropertySources.attach(environment);
//2. 调用SpringApplicationRunListener的environmentPrepared方法
listeners.environmentPrepared(environment);
bindToSpringApplication(environment);
if (!this.isCustomEnvironment) {
environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
deduceEnvironmentClass());
}
ConfigurationPropertySources.attach(environment);
return environment;
}
这里会发布ApplicationEnvironmentPreparedEvent
事件,涉及的监听器如下:
会加载哪些外部化配置呢?
如:
- properties或yaml配置文件
- 环境变量
- 一些系统属性和启动参数等
如上面第一个监听器ConfigFileApplicationListener
在onApplicationEvent
方法中会监听到该事件,监听到后就会进行加载properties或yaml配置文件,这些配置信息包括其他外部化配置都会加载到Environment
类中,因此我们可以直接注入该类获取相关信息。
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ApplicationEnvironmentPreparedEvent) {
onApplicationEnvironmentPreparedEvent((ApplicationEnvironmentPreparedEvent) event);
}
if (event instanceof ApplicationPreparedEvent) {
onApplicationPreparedEvent(event);
}
}
所有的外部化配置都对应一个PropertySource
,并保存到MutabledPropertySources
类的如下集合中:
public class MutablePropertySources implements PropertySources {
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<>();
}
PropertySource
数据结构如下:
public abstract class PropertySource<T> {
protected final String name;
protected final T source;
}
name为配置配置的路径,source为该配置中的所有配置信息。
可以看下执行完prepareEnvironment
后ConfigurableEnvironment
的propertySources
属性中该集合中有哪些配置:
最后一个就是我们的yaml配置的具体内容。
【接着会打印banner内容,不重要】
2.4 创建上下文对象
ConfigurableApplicationContext content=null;
context = createApplicationContext();
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
switch (this.webApplicationType) {
case SERVLET:
//AnnotationConfigServletWebServerApplicationContext
contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
break;
case REACTIVE:
contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
break;
default:
contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
}
}
catch (ClassNotFoundException ex) {
throw new IllegalStateException(
"Unable create a default ApplicationContext, please specify an ApplicationContextClass", ex);
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}
这里也是根据准备阶段确定的webApplicationType
类型返回不同的上下文对象。默认下对返回servlet类型的上下文实例:
org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext
public AnnotationConfigServletWebServerApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
在该类中会实例化如上两个对象。
AnnotatedBeanDefinitionReader
:会将ConfigurationClassPostProcessor
、AutowiredAnnotationBeanPostProcessor
等变成BeanDefinition
对象并加入到BeanDefinitionRegistry
中。
ClassPathBeanDefinitionScanner
:在前面文章将Spring的时候,讲过在IOC初始化阶段会采用该scanner扫描包下所有类将符合条件的加载到容器中。
会发现该构造和讲Spring的时候使用AnnotationConfigApplicationContext
创建上下文对象作为程序入口的构造是一样的:
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(Test.class);
///
public AnnotationConfigApplicationContext() {
this.reader = new AnnotatedBeanDefinitionReader(this);
this.scanner = new ClassPathBeanDefinitionScanner(this);
}
2.5 准备上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
applyInitializers(context);
// 1.调用contextPrepared,上下文准备完成
listeners.contextPrepared(context);
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// Add boot specific singleton beans
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
//2.将封装的参数注册到容器中(单例的),因此我们可以直接注入使用
beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
if (printedBanner != null) {
beanFactory.registerSingleton("springBootBanner", printedBanner);
}
if (beanFactory instanceof DefaultListableBeanFactory) {
((DefaultListableBeanFactory) beanFactory)
.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
}
if (this.lazyInitialization) {
context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
}
// Load the sources
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0]));
//3.上下文加载完成,但尚未启动
listeners.contextLoaded(context);
}
这里主要进行上下文的准备,并发布SpringApplicationRunListener
的两个事件。
2.6 启动Spring容器
refreshContext(context);
/
private void refreshContext(ConfigurableApplicationContext context) {
refresh(context);
if (this.registerShutdownHook) {
try {
context.registerShutdownHook();
}
catch (AccessControlException ex) {
// Not allowed in some environments.
}
}
}
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
OK,到这里是不是很熟悉了,就是调用AbstractApplicationContext
的refresh
方法:
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
........................
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
try {
.....
// Initialize other special beans in specific context subclasses.
onRefresh();
.....
}
..........
}
该方法是Spring启动的核心方法,也就搞清楚了Springboot如何与spring进行对接,这里在前面Spring源码文章讲的很清楚了,不再赘述。
主要关注钩子方法onRefresh
:
该方法会调用ServletWebServerApplicationContext
的onRefresh
方法:
@Override
protected void onRefresh() {
super.onRefresh();
try {
//看这里
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
在createWebServer
方法中就会创建web服务器。
2.7 启动内置Tomcat
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
// 主要看这个方法
this.webServer = factory.getWebServer(getSelfInitializer());
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
这里通过ServletWebServerFactory
获取webServer
,工厂类有三个实现类,我们主要看Tomcat的:
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//创建Tomcat容器
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);
}
- 创建Tomcat对象
- 配置Tomcat信息
- 创建
TomcatWebServer
返回
protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
return new TomcatWebServer(tomcat, getPort() >= 0);
}
在TomcatWebServer
的构造中会调用initialize
方法
public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
Assert.notNull(tomcat, "Tomcat Server must not be null");
this.tomcat = tomcat;
this.autoStart = autoStart;
initialize();
}
private void initialize() throws WebServerException {
logger.info("Tomcat initialized with port(s): " + getPortsDescription(false));
synchronized (this.monitor) {
try {
// Start the server to trigger initialization listeners
this.tomcat.start();
.......
// Unlike Jetty, all Tomcat threads are daemon threads. We create a
// blocking non-daemon to stop immediate shutdown
startDaemonAwaitThread();
}
catch (Exception ex) {
....
}
}
}
在该方法中主要做了两件事:
- 启动Tomcat
- 创建一个守护线程防监听请求
private void startDaemonAwaitThread() {
Thread awaitThread = new Thread("container-" + (containerCounter.get())) {
@Override
public void run() {
TomcatWebServer.this.tomcat.getServer().await();
}
};
awaitThread.setContextClassLoader(getClass().getClassLoader());
awaitThread.setDaemon(false);
awaitThread.start();
}
【TIP】啥时候创建Web容器工厂的?
其实它也是在Springboot自动装配【具体收集、装配时机看第三部分】的时候【在调用spring的refresh()
中进行的,而启动web服务器实在refresh()
后的createWebServer()
开始的】,所有的spring.factories
文件,而在/spring-boot-autoconfigure-2.2.2.RELEASE
的/META-INF/spring.factories
的文件中定义了这么个类:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
.... org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
....
当然还有好多没有列出来,如AOP的、Rabbit的、缓存的,还有我们常见的中间件:ES、Mongo等等的自动化配置类都在这里定义好了。
这里就看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 {
....
}
看到了啥?@Import
导入了三个``ServletWebServerFactory`内容类,对应三个类型的,点进去看看:
@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Tomcat.class, UpgradeProtocol.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedTomcat {
@Bean
public 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;
}
}
/**
* Nested configuration if Jetty is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Server.class, Loader.class, WebAppContext.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedJetty {
@Bean
public JettyServletWebServerFactory JettyServletWebServerFactory(
ObjectProvider<JettyServerCustomizer> serverCustomizers) {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.getServerCustomizers().addAll(serverCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
/**
* Nested configuration if Undertow is being used.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
@ConditionalOnMissingBean(value = ServletWebServerFactory.class, search = SearchStrategy.CURRENT)
public static class EmbeddedUndertow {
@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory(
ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers,
ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
factory.getDeploymentInfoCustomizers()
.addAll(deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
factory.getBuilderCustomizers().addAll(builderCustomizers.orderedStream().collect(Collectors.toList()));
return factory;
}
}
}
这里使用ConditionalOnClass
对不同条件的情况创建不同的容器工厂类,默认会使用内嵌的Tomcat容器,而如果我们在依赖中移除内嵌的Tomcat,引入Jetty,这里就会创建一个jetty工厂JettyServletWebServerFactory
.
运行阶段总结
可以看到,整个springboot的run的核心,就做了如下几件大事:
- 加载SpringApplicationRunListener
- 加载外部配置
- 创建上下文对象
- 启动Spring容器
- 创建和部署内置Tomcat服务器
三、自动化配置源码
在这里我们就需要关注:
@EnableAutoConfiguration
何时被加载?- 基于SPI加载了
spring.factories
文件中的类后,在哪里,又是如何被装配到容器中的?
回到启动类:
@SpringBootApplication
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class,args);
}
}
也就剩了个注解,点进去看看:
@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 {
}
看到了么?
EnableAutoConfiguration
:开启自动化配置,是不是很熟悉?前面讲 什么时候创建web容器工厂时在自动化配置项目的spring.factories文件中定义了该接口,并且提供了约125个自动配置类。
在该注解中,又导入了AutoConfigurationImportSelector
类
...........
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
该类很长,我们先看其中的process
方法:
3.1 收集自动化配置类
@Override
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)
//1.主要看该方法
.getAutoConfigurationEntry(getAutoConfigurationMetadata(), annotationMetadata);
this.autoConfigurationEntries.add(autoConfigurationEntry);
for (String importClassName : autoConfigurationEntry.getConfigurations()) {
this.entries.putIfAbsent(importClassName, annotationMetadata);
}
}
这里主要看getAutoConfigurationEntry
方法:
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//1.getAttributes
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 2.SPI获取EnableAutoConfiguration为key的所有实现类
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
//去重
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
//3.
configurations.removeAll(exclusions);
// 4.把某些自动配置类过滤掉
configurations = filter(configurations, autoConfigurationMetadata);
//5.
fireAutoConfigurationImportEvents(configurations, exclusions);
// 包装成自动配置实体类
return new AutoConfigurationEntry(configurations, exclusions);
}
getAttributes
方法:
protected AnnotationAttributes getAttributes(AnnotationMetadata metadata) {
String name = getAnnotationClass().getName();
AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(name, true));
return attributes;
}
protected Class<?> getAnnotationClass() {
return EnableAutoConfiguration.class;
}
从该方法可以看到就是获取了EnableAutoConfiguration
注解的属性
-
getCandidateConfigurations
该类就是基于SPI获取所有以
EnableAutoConfiguration
为key的实现类名称 -
移除使用
exclude
定义的要排除的类; -
过滤掉不需要的的自动配置类
-
发布AutoConfigurationImportEvents事件,基于SPI获取对应的监听类调用对应的事件。【不重要】
private void fireAutoConfigurationImportEvents(List<String> configurations, Set<String> exclusions) { // SPI获取所有AutoConfigurationImportListener接口实现类 List<AutoConfigurationImportListener> listeners = getAutoConfigurationImportListeners(); if (!listeners.isEmpty()) { AutoConfigurationImportEvent event = new AutoConfigurationImportEvent(this, configurations, exclusions); for (AutoConfigurationImportListener listener : listeners) { invokeAwareMethods(listener); listener.onAutoConfigurationImportEvent(event); } } }
-
最后将收集好的要配置的类和要排除的类包装成
AutoConfigurationEntry
返回
有个疑问:该方法是在何时被调用的呢?
在前面运行阶段讲创建上下文对象的构造中提到:AnnotatedBeanDefinitionReader
:会将ConfigurationClassPostProcessor
、AutowiredAnnotationBeanPostProcessor
等变成BeanDefinition
对象并加入到BeanDefinitionRegistry
中。
该方法就是在调用ConfigurationClassPostProcessor
时进来的,(之前讲Spring容器初始化已经讲过了,这里简单过一下),而ConfigurationClassPostProcessor
又是在spring的核心方法refresh
方法【即springboot调用refreshContext()
刷新上下文的时候回调到】中的invokeBeanFactoryPostProcessors(beanFactory);
调用的,该方法会遍历所有的BeanDefinitionRegistryPostProcessor
接口进行处理。
ConfigurationClassPostProcessor
是用于处理配置类的,其实现了BeanDefinitionRegistryPostProcessor
,因此就会调用到该接口的postProcessBeanDefinitionRegistry
方法;
接着会调用该方法中的processConfigBeanDefinitions()
进行具体的处理,在processConfigBeanDefinitions
中,会调用配置文件解析器进行解析所有被@Configuration
注解标注的类:
一直往下跟,会来到ConfigurationClassParser
类的以个processImports
方法:
注意这里,如果是DeferredImportSelector
类型的selector
就会进入下面的handle
方法,回去看下EnableAutoConfiguration
上面导入的AutoConfigurationImportSelector
类:
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
}
也实现了DeferredImportSelector
接口,因此就会从该handle
方法处理这个类:
点进入会来到一个内部类DeferredImportSelectorGroupingHandler
的processGroupImports
方法:
进入getImports方法:
这里就会调用process()
方法,其中第一个实现类就是3.1讲的那个类,这个AutoConfigurationGroup
是AutoConfigurationImportSelector
的内部类,也实现了DeferredImportSelector
接口。
3.2 加入到Spring容器
该收集的收集好了,下一步就是要将这些类加载到spring容器中,上图中调用完process
后,紧接着就调用了selectImports
方法,该方法也是AutoConfigurationImportSelector
中的:
@Override
public Iterable<Entry> selectImports() {
if (this.autoConfigurationEntries.isEmpty()) {
return Collections.emptyList();
}
Set<String> allExclusions = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getExclusions).flatMap(Collection::stream).collect(Collectors.toSet());
Set<String> processedConfigurations = this.autoConfigurationEntries.stream()
.map(AutoConfigurationEntry::getConfigurations).flatMap(Collection::stream)
.collect(Collectors.toCollection(LinkedHashSet::new));
processedConfigurations.removeAll(allExclusions);
return sortAutoConfigurations(processedConfigurations, getAutoConfigurationMetadata()).stream()
.map((importClassName) -> new Entry(this.entries.get(importClassName), importClassName))
.collect(Collectors.toList());
}
该类对收集好的要自动配置的类做简单的筛选排序等操作后返回,接着就会回到上面的processGroupImports
方法的foreach中,剩下的就交给ConfigurationClassPostProcessor
按照Spring的流程将其添加到Spring容器中。
到这我们就搞懂了Springboot加载@EnableAutoConfiguration
、收集自动化配置类、和将配置类加载到Spring容器的各个时机。
接下来我们来看下一开始提到的第4个问题:AOP、事务这些事怎么加入到springboot中的?
这里以AOP功能为例,其他类似。
四、AOP的自动配置类
这些自动配置类肯定是在前面自动化配置时通过SPI加载到的。
在普通的Spring工程中,如果我们要使用AOP,就需要使用 @EnableAspectJAutoProxy
注解开启,接着就会将AOP相关配置注册进来,详情请看:Spring的AOP调用流程源码分析~
OK,我们再来看看SpringBoot中怎么做的:
从自动配置的包的spring.factories
中找到AOP的配置类:AopAutoConfiguration
:
@Configuration(proxyBeanMethods = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
public class AopAutoConfiguration {
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(Advice.class)
static class AspectJAutoProxyingConfiguration {
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = false)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false",
matchIfMissing = false)
static class JdkDynamicAutoProxyConfiguration {
}
@Configuration(proxyBeanMethods = false)
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class CglibAutoProxyConfiguration {
}
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.aspectj.weaver.Advice")
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true",
matchIfMissing = true)
static class ClassProxyingConfiguration {
ClassProxyingConfiguration(BeanFactory beanFactory) {
if (beanFactory instanceof BeanDefinitionRegistry) {
BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
AopConfigUtils.registerAutoProxyCreatorIfNecessary(registry);
AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
}
}
}
}
1.@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
:
先加载配置文件,看看我们的配置文件中是否配置了spring.aop.auto=true
这个选项,如果没有,则默认配置为true
.
-
在内部类
AspectJAutoProxyingConfiguration
中有个@ConditionalOnClass(Advice.class)
,即当有Advice
类时当前类才生效,因此当我们引入AOP的依赖后,当前类就会自动生效; -
在内部又有两个类,对应使用JDK动态代理还是CGLIB代理,两个类上都有
@EnableAspectJAutoProxy
注解,OK到这就知道了,原来在这开启了AOP功能; -
两个代理配置类上都有个
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
注解,即如果我们配置了:spring: aop: proxy-target-class: false
则启用JDK代理,如果没有配置该属性,则给默认值false,即默认不使用JDK代理;
对应下面的,如果该属性值为true则开启CGLIG代理,如果没有的话就给true,即默认开启CGLIB代理。
就这些了,代码写的很明显。
总结。。也没啥好总结的,看一下文章目录就挺清晰: