SpringBoot 启动原理
启动类#
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
@SpringBootApplication#
@SpringBootApplication
标注在某个类,说明这个类是 SpringBoot 的主配置类。
查看 @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 {
......
}
@SpringBootConfiguration#
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
就是一个 @Configuration
@EnableAutoConfiguration#
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
......
}
@EnableAutoConfiguration
由下面两个注解组成
@AutoConfigurationPackage#
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
用来自动配置包,通过 @Import
注解,指定使用 AutoConfigurationPackages.Registrar.class
往容器中注册组件。
// AutoConfigurationPackages
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
......
// AnnotationMetadata metadata为主配置类的注解信息
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 该方法主要是往容器中注入了一个组件, 组件保存了主配置类所在的包名, 这个包名为自动扫描的包名
register(registry, new PackageImport(metadata).getPackageName());
}
......
}
@AutoConfigurationImportSelector#
@AutoConfigurationImportSelector
注解也是往容器中注册组件。
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
/**
* 返回值就是导入到容器中的全类名, 这些类就会被注册到容器中
*/
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 这一步会获取到所有的自动配置类
// 它会从类路径中拿到所有名为META-INF/spring.factories的配置文件,获取EnableAutoConfiguration指定的值。
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
// 返回获取到的自动配置类
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
例如,查看 spring-boot-autoconfigure 包下的 META-INF/spring.factories
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
......
此时 SpringApplicationAdminJmxAutoConfiguration
和 AopAutoConfiguration
等组件就会被注册到容器中。
这些 XxxAutoConfiguration 是用来自动往容器中注入组件的。
自动配置类#
从上面的分析我们可以知道,SpringBoot 会收集 META-INF/spring.factories 中的自动配置类,下面来分析一下自动配置类的作用。
自动配置常用注解
@Configuration :指定这个类是一个配置类
@ConditionalOnClass :当类路径 classpath 下有指定的类的情况下进行自动配置
@ConditionalOnMissingClass:当类路径下没有指定的类的条件下
@ConditionalOnBean:当容器 (Spring Context) 中有指定的 Bean 的条件下
@ConditionalOnMissingBean:当容器 (Spring Context) 中没有指定 Bean 的情况下进行自动配置
@ConfigurationProperties :结合相关 XxxProperties 来绑定相关配置,主要用来把 properties 配置文件转化为对应的 XxxProperties 来使用的,并不会把该类放入到 IOC 容器中
@EnableConfigurationProperties(XxxProperties.class) :注解的作用是使 @ConfigurationProperties 注解生效并且将 XxxProperties 注入到 IOC 容器中。
如果在每个 Properties 上都使用 @Component 来标注,那么也不需要使用 @EnableConfigurationProperties ({XxxProperties.class}) 来标注了,同样也可以在 Spring 上下文容器中也能获取到 XxxProperties 对应的 Bean。
@ConditionalOnProperty(prefix = “spring.person”, value = “enabled”, matchIfMissing = true)
当配置文件中 spring.person.enabled=true 时进行自动配置,如果没有设置此值就默认使用 matchIfMissing 对应的值。如果不设置 matchIfMissing = true, 默认为 false。
以 HttpEncodingAutoConfiguration
为例,当满足一些条件时,它会自动往 ioc 容器中注入组件。
// 表示这是一个配置类
@Configuration(proxyBeanMethods = false)
// 将HttpProperties中的属性与配置文件进行绑定, 并注入ioc容器中
@EnableConfigurationProperties(HttpProperties.class)
// web应用类型是SERVLET
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
// 当前类路径不存在CharacterEncodingFilter.class
@ConditionalOnClass(CharacterEncodingFilter.class)
// 当配置文件中spring.http.encoding.enabled=true时进行自动配置,如果没有设置此值就默认使用matchIfMissing对应的值。如果不设置matchIfMissing = true, 默认为false。
@ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true)
public class HttpEncodingAutoConfiguration {
private final HttpProperties.Encoding properties;
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
@Bean
// 容器中不存在CharacterEncodingFilter类型的bean时,这个bean才会生效
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE));
return filter;
}
}
run()#
准备环境
- 执行 ApplicationContextInitializer.initialize () 方法
- 监听器回调 SpringApplicationRunListener.contextPrepared () 方法
- 加载主配置类定义信息
- 监听器回调 SpringApplicationRunListener.contextLoaded () 方法
刷新 IOC 容器
- 扫描加载所有容器中的组件
- 包括 META-INF/spring.factories 下的 EnableAutoConfiguration 组件
回调容器中所有的 ApplicationRunner、CommandLineRunner 的 run 方法
回调所有 SpringApplicationRunListener.running (context) 方法
创建 SpringApplication 对象#
查看启动类的 run 方法,将会看到新建一个 SpringApplication
, 并将配置类传递进去
// SpringApplication.java
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return (new SpringApplication(primarySources)).run(args);
}
查看创建 SpringApplication
对象过程
// SpringApplication.java
public SpringApplication(Class... primarySources) {
this((ResourceLoader)null, primarySources);
}
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
// 保存配置类
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
// 获取当前应用类型: NONE, SERVLET, REACTIVE;
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 从类路径下寻找META-INF/spring.factories中的所有ApplicationContextInitializer并保存起来
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 从类路径下寻找META-INF/spring.factories中的所有ApplicationListener并保存起来
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
// 从多个配置类中找到有main方法的主配置类
this.mainApplicationClass = this.deduceMainApplicationClass();
}
执行 SpringApplication.run ()#
// SpringApplication.java
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList();
this.configureHeadlessProperty();
// 获取SpringApplicationRunListener
// 从类路径下寻找META-INF/spring.factories中的所有SpringApplicationRunListener并保存起来
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 回调所有的SpringApplicationRunListener.starting()方法
listeners.starting();
Collection exceptionReporters;
try {
// 封装命令行参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 根绝应用类型创建不同的ConfigurableEnvironment
// 创建ConfigurableEnvironment后会回调
// SpringApplicationRunListener.environmentPrepared(ConfigurableEnvironment environment), 表示环境准备完成
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, applicationArguments);
this.configureIgnoreBeanInfo(environment);
// 打印控制台Banner
Banner printedBanner = this.printBanner(environment);
// 创建ApplicationContext, 根据NONE,SERVLET,REACTIVE类型创建不同的ApplicationContext
// 如果使用默认的Tomcat内嵌服务器, web类型为SERVLET
// SERVLET => AnnotationConfigServletWebServerApplicationContext
// REACTIVE => AnnotationConfigReactiveWebServerApplicationContext
// NONE => AnnotationConfigApplicationContext
context = this.createApplicationContext();
exceptionReporters = this.getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[]{ConfigurableApplicationContext.class}, context);
// 1. 准备上下文环境
this.prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 2. 刷新IOC容器,
// 加载所有容器中的组件,
// 包括META-INF/spring.factories下的EnableAutoConfiguration组件
// 如果是web应用, 还会启动web容器,后面将会分析web容器是如何启动的
this.refreshContext(context);
// 这是一个空方法
this.afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), stopWatch);
}
// 回调所有SpringApplicationRunListener.started(context)方法
listeners.started(context);
// 从ioc容器中获取ApplicationRunner、CommandLineRunner, 并回调相关方法
this.callRunners(context, applicationArguments);
} catch (Throwable var10) {
this.handleRunFailure(context, var10, exceptionReporters, listeners);
throw new IllegalStateException(var10);
}
try {
// 回调所有SpringApplicationRunListener.running(context)方法
listeners.running(context);
return context;
} catch (Throwable var9) {
this.handleRunFailure(context, var9, exceptionReporters, (SpringApplicationRunListeners)null);
throw new IllegalStateException(var9);
}
}
prepareContext#
// SpringApplication.java
private void prepareContext(ConfigurableApplicationContext context, ConfigurableEnvironment environment,
SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {’
// 为ioc容器设置环境信息
context.setEnvironment(environment);
postProcessApplicationContext(context);
// 回调所有 ApplicationContextInitializer.initialize(context)方法
applyInitializers(context);
// 回调所有 SpringApplicationRunListener.contextPrepared(context)方法
listeners.contextPrepared(context);
// 打印一些日志信息
if (this.logStartupInfo) {
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context);
}
// 往ioc容器中注册一些组件
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
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());
}
// 获取配置类信息
Set<Object> sources = getAllSources();
Assert.notEmpty(sources, "Sources must not be empty");
load(context, sources.toArray(new Object[0])); // 将配置类信息注册到容器中
// 回调所有SpringApplicationRunListener.contextLoaded(context)方法
listeners.contextLoaded(context);
}
refreshContext#
refreshContext 方法最终会调用 AbstractApplicationContext.refresh()
方法,分析 Spring 源码的时候已经分析过该方法了,这个方法会加载所有组件到容器中并实例化,需要注意的是这个方法里面还调用了 onRefresh()
方法,这个方法是留给子类实现的。根据前面的分析我们可以知道,若 web 应用类型为 SERVLET,此时的应用上下文为 AnnotationConfigServletWebServerApplicationContext
,AnnotationConfigServletWebServerApplicationContext
继承了 ServletWebServerApplicationContext
,
ServletWebServerApplicationContext
实现了 AbstractApplicationContext.refresh()
方法,web 容器就是在这里启动的。
启动 web 容器#
容器自动配置#
SpringBoot 默认使用 Tomcat 容器作为内嵌容器。查看 web 容器的自动配置类 ServletWebServerFactoryAutoConfiguration
。
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
// 仅在当前应用是 Servlet Web 应用时才生效
@ConditionalOnWebApplication(type = Type.SERVLET)
// 将配置文件中前缀为server的配置参数绑定到ServerProperties, 并将其导入到容器中
@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) {
return new ServletWebServerFactoryCustomizer(serverProperties);
}
// Tomcat 定制器,
// WebServerFactoryCustomizerBeanPostProcessor会获取TomcatServletWebServerFactoryCustomizer对 TomcatServletWebServerFactor 进行定制
@Bean
@ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
public TomcatServletWebServerFactoryCustomizer tomcatServletWebServerFactoryCustomizer(
ServerProperties serverProperties) {
return new TomcatServletWebServerFactoryCustomizer(serverProperties);
}
......
}
ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class
往容器中注册了两个 BeanPostProcessor
,分别是 WebServerFactoryCustomizerBeanPostProcessor
和 ErrorPageRegistrarBeanPostProcessor
,这两个组件用来对 WebServerFactory
进行定制化。
1.ServletWebServerFactoryConfiguration.EmbeddedTomcat.class |
---|
往容器中注入 TomcatServletWebServerFactory |
2.ServletWebServerFactoryConfiguration.EmbeddedJetty.class |
往容器中注入 JettyServletWebServerFactory |
3.ServletWebServerFactoryConfiguration.EmbeddedUndertow.class |
往容器中注入 UndertowServletWebServerFactory |
TomcatServletWebServerFactory
、JettyServletWebServerFactory
、UndertowServletWebServerFactory
继承自抽象基类 AbstractServletWebServerFactory
,实现了接口 WebServerFactory
,ErrorPageRegistry
。
这些类将分别会被 WebServerFactoryCustomizerBeanPostProcessor
和 ErrorPageRegistrarBeanPostProcessor
使用。由于代码比较简单,只看 WebServerFactoryCustomizerBeanPostProcessor
。
// WebServerFactoryCustomizerBeanPostProcessor.java
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
private ListableBeanFactory beanFactory;
private List<WebServerFactoryCustomizer<?>> customizers;
@Override
public void setBeanFactory(BeanFactory beanFactory) {
Assert.isInstanceOf(ListableBeanFactory.class, beanFactory,
"WebServerCustomizerBeanPostProcessor can only be used with a ListableBeanFactory");
this.beanFactory = (ListableBeanFactory) beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// 判断bean的类型是否为WebServerFactory
// 主要对 TomcatServletWebServerFactory、JettyServletWebServerFactory、UndertowServletWebServerFactory进行处理定制化处理
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@SuppressWarnings("unchecked")
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
// 对WebServerFactory进行定制化操作
LambdaSafe.callbacks(WebServerFactoryCustomizer.class, getCustomizers(), webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
// 获取类型为WebServerFactoryCustomizer的组件
private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
if (this.customizers == null) {
// Look up does not include the parent context
this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
this.customizers = Collections.unmodifiableList(this.customizers);
}
return this.customizers;
}
// 从容器中获取所有类型为WebServerFactoryCustomizer的组件
@SuppressWarnings({ "unchecked", "rawtypes" })
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
}
创建容器#
前面已经分析过,web 容器的创建是在 ServletWebServerApplicationContext.refresh()
方法里面。
// ServletWebServerApplicationContext.java
// AnnotationConfigServletWebServerApplicationContext继承了ServletWebServerApplicationContext,ServletWebServerApplicationContext实现了AbstractApplicationContext.refresh()方法
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
implements ConfigurableWebServerApplicationContext {
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
// 创建web服务
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
// 1. 获取 ServletWebServerFactory
// 如果使用的是Tomcat, 那么返回的是 TomcatServletWebServerFactory
ServletWebServerFactory factory = getWebServerFactory();
// 2. 创建web服务, 启动Tomcat
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();
}
}
1 - 获取 ServletWebServerFactory#
// ServletWebServerApplicationContext.java
// 获取 ServletWebServerFactory
protected ServletWebServerFactory getWebServerFactory() {
// 从容器中获取类型为ServletWebServerFactory的组件名
// 常见的ServletWebServerFactory的实现类有TomcatServletWebServerFactory、UndertowServletWebServerFactory、JettyServletWebServerFactory
String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
if (beanNames.length == 0) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to missing "
+ "ServletWebServerFactory bean.");
}
// 不能同时存在多个ServletWebServerFactory
if (beanNames.length > 1) {
throw new ApplicationContextException("Unable to start ServletWebServerApplicationContext due to multiple "
+ "ServletWebServerFactory beans : " + StringUtils.arrayToCommaDelimitedString(beanNames));
}
// 从容器中获取ServletWebServerFactory实例
return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}
2 - 创建 web 服务#
// ServletWebServerApplicationContext.java
this.webServer = factory.getWebServer(getSelfInitializer());
getSelfInitializer()#
这里需要注意的是,getSelfInitializer()
这个方法返回了一个 ServletContextInitializer
,这里使用了回调的方式。
// ServletWebServerApplicationContext.java
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
ServletContainerInitializer
接口只有一个方法
// ServletContainerInitializer.java
public interface ServletContainerInitializer {
void onStartup(Set<Class<?>> var1, ServletContext var2) throws ServletException;
}
接着往下看
// 当ServletContextInitializer 类被 TomcatStarter 的#onStartup()方法调用时才会触发这里
private void selfInitialize(ServletContext servletContext) throws ServletException {
// 1. 判断 ServletContext 里面是否存在 WebApplicationContext.class.getName() + ".ROOT"
// 2. 如果不存在则往 ServletContext 里面注册一个根容器, 如果web类型为SERVLET,那就是注册了一个AnnotationConfigServletWebServerApplicationContext
prepareWebApplicationContext(servletContext);
// 设置作用域
registerApplicationScope(servletContext);
// 往环境中注册bean
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
// 从容器中获取类型为ServletContextInitializer组件并执行他们的onStartup方法
// 一般使用这个给容器动态地添加Servlet、Filter、Listener
// 注意 ServletContextInitializer 和 servlet3.0 中的 ServletContainerInitializer长得特别像,因为SpringBoot使用内嵌web容器启动的时候并没有遵守 servlet 的规范,所以我们不能使用ServletContainerInitializer的来实现动态添加组件,但是可以通过实现ServletContextInitializer接口来动态添加Servlet、Filter、Listener
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
这里主要做了两件事情:
- 往
ServletContext中
注入当前的ApplicationContext
,这个就相当于是一个 ROOT 容器。学习 SpringMVC 的时候我们知道,SpringMVC 共有两个 ioc 容器,一个是ServletContextListener
中的 ROOT 容器,一个是在DispatcherServlet
中的 WEB 容器,WEB 容器的 parent 为 ROOT 容器。 - 从 ioc 容器中获取所有类型为
ServletContextInitializer
类型的组件,并执行他们的onStartup(ServletContext servletContext)
方法。在 SpringBoot 中,如果想要实现动态添加Servlet
、Filter
、Listener
组件,需要实现ServletContextInitializer
接口来动态添加,例如:
@Component
public class MyServletContextInitializer implements ServletContextInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
ServletRegistration.Dynamic dynamic = servletContext.addServlet("MyServlet",new MyServlet());
dynamic.addMapping("/myServlet");
}
}
跳过这里继续往后看就会发现,这里返回的 ServletContextInitializer 会在 TomcatStarter 的 #onStartup()
方法里面调用它的 ServletContextInitializer#onStartup(ServletContext servletContext)
,那时才会触发这里的 selfInitialize(ServletContext servletContext)
方法。
getWebServer()#
创建 Tomcat
// TomcatServletWebServerFactory.java
// 这里就是Tomcat的启动
@Override
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);
}
// 这里传入了 initializers(getSelfInitializer())
prepareContext(tomcat.getHost(), initializers);
return getTomcatWebServer(tomcat);
}
// 这里只要为了看清楚 initializers 参数被 TomcatStarter 调用,因此省略了很多代码
protected void prepareContext(Host host, ServletContextInitializer[] initializers) {
// 省略很多代码........
ServletContextInitializer[] initializersToUse = mergeInitializers(initializers);
// 省略......
configureContext(context, initializersToUse);
// 省略......
}
protected void configureContext(Context context, ServletContextInitializer[] initializers) {
// 创建 TomcatStarter 实例并传入了 initializers
TomcatStarter starter = new TomcatStarter(initializers);
}
查看 TomcatStarter.onStartup () 方法
// TomcatStarter.java
@Override
public void onStartup(Set<Class<?>> classes, ServletContext servletContext) throws ServletException {
try {
for (ServletContextInitializer initializer : this.initializers) {
// 这里会触发 前面提到的selfInitialize(ServletContext servletContext) 方法
initializer.onStartup(servletContext);
}
}
catch (Exception ex) {
// ......
}
}
创建 DispatcherServlet#
DispatcherServlet
是在自动配置类 DispatcherServletAutoConfiguration
中被创建的。
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
// The bean name for a DispatcherServlet that will be mapped to the root URL "/"
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "dispatcherServlet";
// The bean name for a ServletRegistrationBean for the DispatcherServlet "/"
public static final String DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME = "dispatcherServletRegistration";
@Configuration(proxyBeanMethods = false)
@Conditional(DefaultDispatcherServletCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties({ HttpProperties.class, WebMvcProperties.class })
protected static class DispatcherServletConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServlet dispatcherServlet(HttpProperties httpProperties, WebMvcProperties webMvcProperties) {
DispatcherServlet dispatcherServlet = new DispatcherServlet();
dispatcherServlet.setDispatchOptionsRequest(webMvcProperties.isDispatchOptionsRequest());
dispatcherServlet.setDispatchTraceRequest(webMvcProperties.isDispatchTraceRequest());
dispatcherServlet.setThrowExceptionIfNoHandlerFound(webMvcProperties.isThrowExceptionIfNoHandlerFound());
dispatcherServlet.setPublishEvents(webMvcProperties.isPublishRequestHandledEvents());
dispatcherServlet.setEnableLoggingRequestDetails(httpProperties.isLogRequestDetails());
return dispatcherServlet;
}
}
@Configuration(proxyBeanMethods = false)
@Conditional(DispatcherServletRegistrationCondition.class)
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
@Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
@ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet,
WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet,
webMvcProperties.getServlet().getPath());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
multipartConfig.ifAvailable(registration::setMultipartConfig);
return registration;
}
}
}
从上面的代码可以看出 DispatcherServlet
实例被封装成 DispatcherServletRegistrationBean
注入到 ioc 容器的,而 DispatcherServletRegistrationBean
实现了 ServletContextInitializer
接口,因此最终这个 DispatcherServlet
会被注入到 ServletContext
中。
本作品采用《CC 协议》,转载必须注明作者和本文链接
推荐文章: