SpringBoot中WEB的啟動(dòng)流程分析
想必大家都體驗(yàn)過(guò)springboot的便捷,以前想要運(yùn)行web項(xiàng)目,我們首先需要將項(xiàng)目打成war包,然后再運(yùn)行Tomcat啟動(dòng)項(xiàng)目,不過(guò)自從有了springboot,我們可以像啟動(dòng)jar包一樣簡(jiǎn)單的啟動(dòng)一個(gè)web項(xiàng)目,今天我們就來(lái)分析下springboot啟動(dòng)web項(xiàng)目整個(gè)流程。
老規(guī)矩,我們從spring.factories文件開(kāi)始。
spring-boot-starter-web下沒(méi)有spring.factories文件

所以我們從spring-boot-autoconfigure下的spring.factories開(kāi)始

一、DispatcherServlet的注冊(cè)
1.1 把DispatcherServlet注入IOC容器
DispatcherServlet是通過(guò)DispatcherServletAutoConfiguration注冊(cè)的
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass(DispatcherServlet.class)
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
public class DispatcherServletAutoConfiguration {
public static final String DEFAULT_DISPATCHER_SERVLET_BEAN_NAME = "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;
}
@Bean
@ConditionalOnBean(MultipartResolver.class)
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
}
@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;
}
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DefaultDispatcherServletCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConditionMessage.Builder message = ConditionMessage.forCondition("Default DispatcherServlet");
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
List<String> dispatchServletBeans = Arrays
.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
if (dispatchServletBeans.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome
.noMatch(message.found("dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
if (beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome.noMatch(
message.found("non dispatcher servlet bean").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
if (dispatchServletBeans.isEmpty()) {
return ConditionOutcome.match(message.didNotFind("dispatcher servlet beans").atAll());
}
return ConditionOutcome.match(message.found("dispatcher servlet bean", "dispatcher servlet beans")
.items(Style.QUOTE, dispatchServletBeans)
.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
}
@Order(Ordered.LOWEST_PRECEDENCE - 10)
private static class DispatcherServletRegistrationCondition extends SpringBootCondition {
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
ConditionOutcome outcome = checkDefaultDispatcherName(beanFactory);
if (!outcome.isMatch()) {
return outcome;
}
return checkServletRegistration(beanFactory);
}
private ConditionOutcome checkDefaultDispatcherName(ConfigurableListableBeanFactory beanFactory) {
List<String> servlets = Arrays
.asList(beanFactory.getBeanNamesForType(DispatcherServlet.class, false, false));
boolean containsDispatcherBean = beanFactory.containsBean(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
if (containsDispatcherBean && !servlets.contains(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)) {
return ConditionOutcome.noMatch(
startMessage().found("non dispatcher servlet").items(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME));
}
return ConditionOutcome.match();
}
private ConditionOutcome checkServletRegistration(ConfigurableListableBeanFactory beanFactory) {
ConditionMessage.Builder message = startMessage();
List<String> registrations = Arrays
.asList(beanFactory.getBeanNamesForType(ServletRegistrationBean.class, false, false));
boolean containsDispatcherRegistrationBean = beanFactory
.containsBean(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME);
if (registrations.isEmpty()) {
if (containsDispatcherRegistrationBean) {
return ConditionOutcome.noMatch(message.found("non servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
return ConditionOutcome.match(message.didNotFind("servlet registration bean").atAll());
}
if (registrations.contains(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)) {
return ConditionOutcome.noMatch(message.found("servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
if (containsDispatcherRegistrationBean) {
return ConditionOutcome.noMatch(message.found("non servlet registration bean")
.items(DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
return ConditionOutcome.match(message.found("servlet registration beans").items(Style.QUOTE, registrations)
.append("and none is named " + DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME));
}
private ConditionMessage.Builder startMessage() {
return ConditionMessage.forCondition("DispatcherServlet Registration");
}
}
}
這也是SpringBoot中IOC容器和WEB容器是同一個(gè)的原因
Spring把DispatcherServlet放到容器中后,在DispatcherServlet的初始化中會(huì)執(zhí)行ApplicationContextAwareProcessor的postProcessBeforeInitialization方法,而其postProcessBeforeInitialization底層如下
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware) {
((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware) {
((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware) {
((MessageSourceAware) bean).setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
而DispatcherServlet是一個(gè)ApplicationContextAware,所以會(huì)執(zhí)行其setApplicationContext方法,設(shè)置其屬性webApplicationContext
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
//傳入ioc容器
if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {
this.webApplicationContext = (WebApplicationContext) applicationContext;
this.webApplicationContextInjected = true;
}
}
所以在web容器啟動(dòng)過(guò)程會(huì)把web容器設(shè)置成和ioc容器一樣,springMVC容器創(chuàng)建代碼如下,參考文章springMVC全注解啟動(dòng)和容器的初始化
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext());
WebApplicationContext wac = null;
//因?yàn)閣ebApplicationContext這里有值了,所以會(huì)進(jìn)入這里
if (this.webApplicationContext != null) {
//把web容器設(shè)置成和ioc容器一樣
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null)
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext();
wac = createWebApplicationContext(rootContext);
if (!this.refreshEventReceived) {
synchronized (this.onRefreshMonitor) {
onRefresh(wac);
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
return wac;
}
這里可能要有人問(wèn)了,為什么在springMVC環(huán)境中,this.webApplicationContext為null,因?yàn)樵趕pringMVC中DispatcherServlet沒(méi)有通過(guò)spring容器管理
protected void registerDispatcherServlet(ServletContext servletContext) {
String servletName = getServletName();
Assert.hasLength(servletName, "getServletName() must not return null or empty");
//創(chuàng)建web容器
WebApplicationContext servletAppContext = createServletApplicationContext();
Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");
//創(chuàng)建DispatcherServlet對(duì)象
FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);
Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");
dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());
//把dispatcherServlet作為Servlet注冊(cè)到上下文中
ServletRegistration.Dynamic registration = servletContext.addServlet(servletName, dispatcherServlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + servletName + "'. " +
"Check if there is another servlet registered under the same name.");
}
//容器在啟動(dòng)的時(shí)候加載這個(gè)servlet,其優(yōu)先級(jí)為1(正數(shù)的值越小,該servlet的優(yōu)先級(jí)越高,應(yīng)用啟動(dòng)時(shí)就越先加載)
registration.setLoadOnStartup(1);
//設(shè)置Servlet映射mapping路徑
//getServletMappings()是模版方法,需要我們自己配置
registration.addMapping(getServletMappings());
//設(shè)置是否支持異步請(qǐng)求
//isAsyncSupported默認(rèn)是true
registration.setAsyncSupported(isAsyncSupported());
//處理自定義的Filter進(jìn)來(lái),一般我們Filter不這么加進(jìn)來(lái),而是自己@WebFilter,或者借助Spring,
//備注:這里添加進(jìn)來(lái)的Filter都僅僅只攔截過(guò)濾上面注冊(cè)的dispatchServlet
Filter[] filters = getServletFilters();
if (!ObjectUtils.isEmpty(filters)) {
for (Filter filter : filters) {
registerServletFilter(servletContext, filter);
}
}
//這個(gè)很清楚:調(diào)用者若相對(duì)dispatcherServlet有自己更個(gè)性化的參數(shù)設(shè)置,復(fù)寫(xiě)此方法即可
customizeRegistration(registration);
}
1.2 把DispatcherServlet注入Servlet容器
SpringBoot中容器是AnnotationConfigServletWebServerApplicationContext,其onRefresh()方法如下
@Override
protected void onRefresh() {
super.onRefresh();
try {
createWebServer(); //創(chuàng)建Servlet容器
}
catch (Throwable ex) {
throw new ApplicationContextException("Unable to start web server", ex);
}
}
private void createWebServer() {
WebServer webServer = this.webServer;
ServletContext servletContext = getServletContext();
if (webServer == null && servletContext == null) {
ServletWebServerFactory factory = getWebServerFactory();
this.webServer = factory.getWebServer(getSelfInitializer());//創(chuàng)建容器,并執(zhí)行所有ServletContextInitializer的onStartup
}
else if (servletContext != null) {
try {
getSelfInitializer().onStartup(servletContext);
}
catch (ServletException ex) {
throw new ApplicationContextException("Cannot initialize servlet context", ex);
}
}
initPropertySources();
}
注意,這里他不會(huì)執(zhí)行SpringServletContainerInitializer。
流程如下
1、通過(guò)getSelfInitializer()方法執(zhí)行容器中所有的ServletContextInitializer
private org.springframework.boot.web.servlet.ServletContextInitializer getSelfInitializer() {
return this::selfInitialize;
}
private void selfInitialize(ServletContext servletContext) throws ServletException {
prepareWebApplicationContext(servletContext);
registerApplicationScope(servletContext);
WebApplicationContextUtils.registerEnvironmentBeans(getBeanFactory(), servletContext);
for (ServletContextInitializer beans : getServletContextInitializerBeans()) {
beans.onStartup(servletContext);
}
}
而ServletContextInitializer有個(gè)子類ServletRegistrationBean,通過(guò)其addRegistration方法注入Servlet容器中
@Override
protected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {
String name = getServletName();
return servletContext.addServlet(name, this.servlet);
}
到此這篇關(guān)于SpringBoot中WEB的啟動(dòng)的文章就介紹到這了,更多相關(guān)SpringBoot WEB啟動(dòng)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java獲取服務(wù)器IP及端口的方法實(shí)例分析
這篇文章主要介紹了Java獲取服務(wù)器IP及端口的方法,結(jié)合實(shí)例形式分析了java針對(duì)客戶端及服務(wù)器端各種常見(jiàn)的信息操作技巧與注意事項(xiàng),需要的朋友可以參考下2018-12-12
struts2標(biāo)簽總結(jié)_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要為大家詳細(xì)總結(jié)了struts2標(biāo)簽的使用方法,和學(xué)習(xí)資料,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
詳解spring Boot 集成 Thymeleaf模板引擎實(shí)例
本篇文章主要介紹了spring Boot 集成 Thymeleaf模板引擎實(shí)例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-09-09
Java BeanMap實(shí)現(xiàn)Bean與Map的相互轉(zhuǎn)換
這篇文章主要介紹了利用BeanMap進(jìn)行對(duì)象與Map的相互轉(zhuǎn)換,通過(guò)net.sf.cglib.beans.BeanMap類中的方法來(lái)轉(zhuǎn)換,效率極高,本文給大家分享實(shí)現(xiàn)代碼,感興趣的朋友一起看看吧2022-11-11
MyBatis啟動(dòng)時(shí)控制臺(tái)無(wú)限輸出日志的原因及解決辦法
這篇文章主要介紹了MyBatis啟動(dòng)時(shí)控制臺(tái)無(wú)限輸出日志的原因及解決辦法的相關(guān)資料,需要的朋友可以參考下2016-07-07
Springboot2.x結(jié)合Mabatis3.x下Hikari連接數(shù)據(jù)庫(kù)報(bào)超時(shí)錯(cuò)誤
本文針對(duì)Springboot2.x與Mybatis3.x結(jié)合使用時(shí),Hikari連接數(shù)據(jù)庫(kù)出現(xiàn)超時(shí)錯(cuò)誤的問(wèn)題進(jìn)行了深入分析,并提供了一系列有效的解決方法,感興趣的可以了解一下2023-11-11
java基于quasar實(shí)現(xiàn)協(xié)程池的方法示例
本文主要介紹了java基于quasar實(shí)現(xiàn)協(xié)程池的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧<BR>2022-06-06
spring cloud hystrix 超時(shí)時(shí)間使用方式詳解
這篇文章主要介紹了spring cloud hystrix 超時(shí)時(shí)間使用方式,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-01-01

