参考Spring Boot与Spring MVC集成启动过程源码分析
从SpringApplication.run
一路运行到refresh方法:
protected void refresh(ApplicationContext applicationContext) {
Assert.isInstanceOf(AbstractApplicationContext.class, applicationContext);
((AbstractApplicationContext) applicationContext).refresh();
}
在启动web服务时,此处的applicationContext
类型是AnnotationConfigServletWebServerApplicationContext
,原因省略。
此处AbstractApplicationContext执行refresh方法,进而调用onRefresh:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
...
try {
...
// Initialize other special beans in specific context subclasses.
onRefresh();
...
}
...
}
}
AbstractApplicationContext.refresh
->ServletWebServerApplicationContext.onRefresh
:
// ServletWebServerApplicationContext
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer();
}
catch (Throwable ex) {
...
}
}
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) {
...
}
initPropertySources();
}
调试到此处会进入第一个if语句:
ServletWebServerApplicationContext::lambda
,实际上调用ServletWebServerApplicationContext::selfInitialize
,它的作用很重要。ServletWebServerApplicationContext::lambda
的其它初始器注入TomcatStarter为防止读不懂上一小节,特此附上关于对接Servlet3.0的规范。(主要介绍ServletContainerInitializer,关于filter只是拓展阅读) servlet和filter的区别
servlet filter都是在容器启动时初始化,调用init
;都是在容器关闭时随着销毁,调用destroy
Servlet3.0后,定义servlet和filter有三种方式:
ServletContext#addServlet
等方法javax.servlet.ServletContextListener#contextInitialized(ServletContextEvent sce)
回调函数中调用javax.servlet.ServletContainerInitializer#onStartup(Set<Class<?>> c, ServletContext ctx)
回调函数中调用根据上一章,selfInitialize
会在Tomcat容器启动时间接被回调:
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
/**
* Returns {@link ServletContextInitializer}s that should be used with the embedded
* web server. By default this method will first attempt to find
* {@link ServletContextInitializer}, {@link Servlet}, {@link Filter} and certain
* {@link EventListener} beans.
* @return the servlet initializer beans
*/
protected Collection<ServletContextInitializer> getServletContextInitializerBeans() {
return new ServletContextInitializerBeans(getBeanFactory());
}
getServletContextInitializerBeans()
,获取类型为ServletContextInitializer
的Bean,并回调onStartup
方法。并此处我们不展开分析方法,有需要的可以看源码验证一下。DispatcherServletRegistrationBean
。其注入的地方,和它的效果,我们下文讲解spring-boot-autoconfigure/META-INF/spring.factories中有一段配置:
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
看下其实现
..
@Configuration(proxyBeanMethods = false)
..
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();
..
return dispatcherServlet;
}
..
}
@Configuration(proxyBeanMethods = false)
..
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;
}
}
利用Bean机制:
DispatcherServlet
为参数,创建DispatcherServletRegistrationBean
,Bean再作为Bean注入。DispatcherServletRegistrationBean
继承ServletContextInitializer
接口,因此也会被回调。其回调实现在RegistrationBean中:
@Override
public final void onStartup(ServletContext servletContext) throws ServletException {
String description = getDescription();
f (!isEnabled()) {
logger.info(StringUtils.capitalize(description) + " was not registered (disabled)");
return;
}
register(description, servletContext);
}
register方法是一个模板方法,调用子类DynamicRegistrationBean的实现:
@Override
protected final void register(String description, ServletContext servletContext) {
D registration = addRegistration(description, servletContext);
if (registration == null) {
logger.info(StringUtils.capitalize(description) + " was not registered " + "(possibly already registered?)");
return;
}
configure(registration);
}
addRegistration方法又是一个模板方法,调用子类ServletRegistrationBean的实现,
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
return servletContext.addServlet(name, this.servlet);
}
当前处于tomcat启动环境中,调用servletContext.addServlet
(javax.servlet.ServletContext)注入了this.servlet
,也就是在上文注入的DispatcherServlet
。
至此,DispatcherServlet已完成注入