千家信息网

SpringBoot中WEB的启动流程是什么

发表于:2024-09-22 作者:千家信息网编辑
千家信息网最后更新 2024年09月22日,这篇文章主要介绍"SpringBoot中WEB的启动流程是什么",在日常操作中,相信很多人在SpringBoot中WEB的启动流程是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希
千家信息网最后更新 2024年09月22日SpringBoot中WEB的启动流程是什么

这篇文章主要介绍"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 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()方法如下

@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的启动流程是什么"的学习就结束了,希望能够解决大家的疑惑。理论与实践的搭配能更好的帮助大家学习,快去试试吧!若想继续学习更多相关知识,请继续关注网站,小编会继续努力为大家带来更多实用的文章!

0