想必大家都体验过springboot的便捷,以前想要运行web项目,我们首先需要将项目打成war包,然后再运行Tomcat启动项目,不过自从有了springboot,我们可以像启动jar包一样简单的启动一个web项目,今天我们就来分析下springboot启动web项目整个流程。
老规矩,我们从spring.factories文件开始。
spring-boot-starter-web下没有spring.factories文件
因此,我们可以从spring.factories文件在spring-boot-autoconfigure中开始
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<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容器是同一个的原因
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); }
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 description, ServletContext servletContext) { String name = getServletName(); return servletContext.addServlet(name, this.servlet); }
以上是SpringBoot中WEB的启动流程是什么的详细内容。更多信息请关注PHP中文网其他相关文章!