这篇文章主要介绍“SpringBoot中WEB的启动流程是什么”,在日常操作中,相信很多人在SpringBoot中WEB的启动流程是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”SpringBoot中WEB的启动流程是什么”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
想必大家都体验过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 incorrectlyreturn 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 {@Overridepublic 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 {@Overridepublic 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容器是同一个的原因
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
@Overridepublic 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默认是trueregistration.setAsyncSupported(isAsyncSupported()); //处理自定义的Filter进来,一般我们Filter不这么加进来,而是自己@WebFilter,或者借助Spring, //备注:这里添加进来的Filter都仅仅只拦截过滤上面注册的dispatchServletFilter[] filters = getServletFilters();if (!ObjectUtils.isEmpty(filters)) {for (Filter filter : filters) {registerServletFilter(servletContext, filter);}}//这个很清楚:调用者若相对dispatcherServlet有自己更个性化的参数设置,复写此方法即可customizeRegistration(registration);}
1.2 把DispatcherServlet注入Servlet容器
SpringBoot中容器是AnnotationConfigServletWebServerApplicationContext,其onRefresh()方法如下
@Overrideprotected 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容器中
@Overrideprotected ServletRegistration.Dynamic addRegistration(String description, ServletContext servletContext) {String name = getServletName();return servletContext.addServlet(name, this.servlet);}
到此,关于“SpringBoot中WEB的启动流程是什么”的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注编程网网站,小编会继续努力为大家带来更多实用的文章!