SpringBoot中WEB的启动流程分析

网友投稿 416 2022-08-17


SpringBoot中WEB的启动流程分析

目录一、DispatcherServlet的注册1.1 把DispatcherServlet注入IOC容器1.2 把DispatcherServlet注入Servlet容器

想必大家都体验过springboot的便捷,以前想要运行web项目,我们首先需要将项目打成war包,然后再运行Tomcat启动项目,不过自从有了springboot,我们可以像启动jar包一样简单的启动一个web项目,今天我们就来分析下springboot启动web项目整个流程。

老规矩,我们从spring.factories文件开始。

spring-boot-starter-web下没有spring.factories文件

所以我们从spring-boot-autoconfigure下的spring.factories开始

一、DispatcherServlet的注册

1.1 把DispatcherServlet注入IOC容器

DispatcherServlet是通过DispatcherServletAutoConfiguration注册的

@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 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 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 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 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容器是同一个的原因

Spring把DispatcherServlet放到容器中后,在DispatcherServlet的初始化中会执行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是一个ApplicationContextAware,所以会执行其setApplicationContext方法,设置其属性webApplicationContext

@Override

public void setApplicationContext(ApplicationContext applicationContext) {

//传入ioc容器

if (this.webApplicationContext == null && applicationContext instanceof WebApplicationContext) {

this.webApplicationContext = (WebApplicationContext) applicationContext;

this.webApplicationContextInjected = true;

}

}

所以在web容器启动过程会把web容器设置成和ioc容器一样,springMVC容器创建代码如下,参考文章springMVC全注解启动和容器的初始化

protected WebApplicationContext initWebApplicationContext() {

WebApplicationContext rootContext =

WebApplicationContextUtils.getWebApplicationContext(getServletContext());

WebApplicationContext wac = null;

//因为webApplicationContext这里有值了,所以会进入这里

if (this.webApplicationContext != null) {

//把web容器设置成和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;

}

这里可能要有人问了,为什么在springMVC环境中,this.webApplicationContext为null,因为在springMVC中DispatcherServlet没有通过spring容器管理

protected void registerDispatcherServlet(ServletContext servletContext) {

String servletName = getServletName();

Assert.hasLength(servletName, "getServletName() must not return null or empty");

//创建web容器

WebApplicationContext servletAppContext = createServletApplicationContext();

Assert.notNull(servletAppContext, "createServletApplicationContext() must not return null");

//创建DispatcherServlet对象

FrameworkServlet dispatcherServlet = createDispatcherServlet(servletAppContext);

Assert.notNull(dispatcherServlet, "createDispatcherServlet(WebApplicationContext) must not return null");

dispatcherServlet.setContextInitializers(getServletApplicationContextInitializers());

//把dispatcherServlet作为Servlet注册到上下文中

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.");

}

//容器在启动的时候加载这个servlet,其优先级为1(正数的值越小,该servlet的优先级越高,应用启动时就越先加载)

registration.setLoadOnStartup(1);

//设置Servlet映射mapping路径

//getServletMappings()是模版方法,需要我们自己配置

registration.addMapping(getServletMappings());

//设置是否支持异步请求

//isAsyncSupported默认是true

registration.setAsyncSupported(isAsyncSupported());

//处理自定义的Filter进来,一般我们Filter不这么加进来,而是自己@WebFilter,或者借助Spring,

//备注:这里添加进来的Filter都仅仅只拦截过滤上面注册的dispatchServlet

Filter[] filters = getServletFilters();

if (!ObjectUtils.isEmpty(filters)) {

for (Filter filter : filters) {

registerServletFilter(servletContext, filter);

}

}

//这个很清楚:调用者若相对dispatcherServlet有自己更个性化的参数设置,复写此方法即可

customizeRegistration(registration);

}

1.2 把DispatcherServlet注入Servlet容器

SpringBoot中容器是AnnotationConfigServletWebServerApplicationContext,其onRefresh()方法如下

@Override

protected void onRefresh() {

super.onRefresh();

try {

createWebServer(); //创建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());//创建容器,并执行所有ServletContextInitializer的onStartup

}

else if (servletContext != null) {

try {

getSelfInitializer().onStartup(servletContext);

}

catch (ServletException ex) {

throw new ApplicationContextException("Cannot initialize servlet context", ex);

}

}

initPropertySources();

}

注意,这里他不会执行SpringServletContainerInitializer。

流程如下1、通过getSelfInitializer()方法执行容器中所有的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有个子类ServletRegistrationBean,通过其addRegistration方法注入Servlet容器中

@Override

protected ServletRegistration.Dynamic addRegistration(String deschttp://ription, ServletContext servletContext) {

String name = getServletName();

return servletContext.addServlet(name, this.servlet);

}


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:SpringBoot动态修改yml配置文件的方法详解
下一篇:SpringBoot学习之基于注解的缓存
相关文章

 发表评论

暂时没有评论,来抢沙发吧~