springboot的SpringMVC配置&Servlet启动原理

导读:本篇文章讲解 springboot的SpringMVC配置&Servlet启动原理,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

如何使用

  1. 创建springboot应用,选择我们需要的模块;
  2. springboot已经默认将这些场景配置好了,只需要在配置文件中指定少量配置就可以运行起来。
  3. 编写业务代码。

掌握自动配置原理

springboot的每个场景启动器帮我们配置了什么?能不能修改?能修改什么?xxxxxx

xxxAutoConfiguration:帮我们给容器自动配置组件
xxxProperties:配置类来分组配置文件的内容

静态资源映射

参考org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration代码。

springMVC自动配置原理

参考官方文档:https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-spring-application

springboot自动配置好了的SpringMVC。
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.
- 以下是Springboot对SpringMVC的默认配置:WebMvcAutoConfiguration
The auto-configuration adds the following features on top of Spring’s defaults:

Inclusion of ContentNegotiatingViewResolver and BeanNameViewResolver beans.
- 自动配置了ViewResolver(视图解析器:根据方法的返回值得到视图对象view,视图对象决定如何渲染(转发?重定向?))
- ContentNegotiatingViewResolver:组合所有的视图解析器;
- 如何定制:我们可以给容器中添加一个视图解析器,自动的将其组合进来。
Support for serving static resources, including support for WebJars (covered later in this document)).

Automatic registration of Converter, GenericConverter, and Formatter beans.
- 自己添加的格式器转换器,我们只需要放到容器中即可。
Support for HttpMessageConverters (covered later in this document).
- HttpMessageConverter:SpringMVC用来转换Http请求和相应的(User<->json);
- HttpMessageConverter:是从容器中获取所有的HttpMessageConverter;
自己给容器中添加HttpMessageConverter只需要将自己的组件注册容器中(通过@Bean,@Component等)
Automatic registration of MessageCodesResolver (covered later in this document).
- 定义错误码生成规则
Static index.html support.
- 支持访问首页
Automatic use of a ConfigurableWebBindingInitializer bean (covered later in this document).
- 我们可以配置一个ConfigurableWebBingdingInitializer来替换默认的(添加到容器中即可)
- 作用是把请求数据绑定到javabean中,会用到上面的消息转换器等功能。
==org.springframework.boot.autoconfigure.web:web的所有自动配置场景都在这个包下面==
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.

如何修改SpringBoot的默认配置

模式

  1. springboot在自动配置很多组件的时候,首先看容器中是否存在用户自定义的(@Bean,@Component),如果有就用用户配置,如果没有,才自动配置;
    如果有些组件可以有多个(比如ViewResolver)将用户配置的和自己默认的组合起来;
  2. 扩展SpringMVC
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.
If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.
  1. 全面接管SpringMVC
If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc

为什么添加@EnableWebMvc,所有的springMVC自动配置失效:
在这里插入图片描述

  • @EnableWebMvc将WebMvcConfigurationSupport组件导入进来;
  • 导入的WebMvcConfigurationSupport只是SpringMVC最基本的功能;

嵌入式Servlet启动原理

步骤:

  1. Springboot根据到的依赖情况,给容器中添加相应的EmbeddedServletContainerFactory。
  2. EmbeddedServletContainerFactory要创建容器对象就会调用后置处理器:EmbeddedServletContainerCustomizerBeanPostProcessor;
    只要是嵌入式的Servlet容器工厂,后置处理器就会工作。
  3. 后置处理器,从容器中获取所有的EmbeddedServletContainerCustomizer,调用定制器的定制方法;

原理

  • 获取嵌入式Servlet容器工厂
  1. Springboot应用启动run方法;
  2. refreshContext(context);Springboot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;如果是web应用创建AnnotationConfigServletWebServerApplicationContext容器。
  3. refresh(context):刷新刚创建好的IOC容器
public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                beanPostProcess.end();
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var10) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var10);
                }
                this.destroyBeans();
                this.cancelRefresh(var10);
                throw var10;
            } finally {
                this.resetCommonCaches();
                contextRefresh.end();
            }
        }
    }
  1. onRefresh():web的IOC容器重写了onRefresh方法
protected void onRefresh() {
        super.onRefresh();
        try {
            this.createWebServer();
        } catch (Throwable var2) {
            throw new ApplicationContextException("Unable to start web server", var2);
        }
    }
  1. web IOC容器会创建Servlet容器:createWebServer();
  2. 获取Servlet容器工厂:
    ServletWebServerFactory factory = this.getWebServerFactory();
    从IOC容器中获取ServletWebServerFactory ,容器工厂就会创建容器对象,接着就按照步骤里面的1,2,3执行(调用后置处理器,进行自动化配置)。
    在这里插入图片描述
 private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        ((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
            customizer.customize(webServerFactory);
        });
    }
  1. 使用容器工厂获取servlet容器
    org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext#selfInitialize
  2. 嵌入式的Servlet容器创建对象并启动Servlet容器。
 private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
        if (webServer == null && servletContext == null) {
            StartupStep createWebServer = this.getApplicationStartup().start("spring.boot.webserver.create");
            ServletWebServerFactory factory = this.getWebServerFactory();
            createWebServer.tag("factory", factory.getClass().toString());
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
            createWebServer.end();
            this.getBeanFactory().registerSingleton("webServerGracefulShutdown", new WebServerGracefulShutdownLifecycle(this.webServer));
            this.getBeanFactory().registerSingleton("webServerStartStop", new WebServerStartStopLifecycle(this, this.webServer));
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var5) {
                throw new ApplicationContextException("Cannot initialize servlet context", var5);
            }
        }

        this.initPropertySources();
    }

先启动servlet容器,再将ioc容器中剩下没有创建出的对象创建完成。
总之:IOC容器启动时创建servlet容器,servlet容器启动后,ioc容器还会完成一些扫尾工作

使用外置的Servlet容器

嵌入式Servlet容器:

  • 优点:简单,便携;
  • 缺点:默认不支持JSP,优化定制比较复杂(修改定制器【ServerProperties、自定义定制器】,自己编写嵌入Servlet容器的创建工程)

外置servlet容器

外置servlet容器需要打成war包。
在这里插入图片描述

外置servlet启动原理

  • jar包:执行Springboot主类的main方法,启动IOC容器的时候创建servlet容器;
  • war包:启动servlet容器,servlet启动springboot应用(依靠SpringBootServletInitializer类),然后启动IOC容器;
    在这里插入图片描述

流程

在这里插入图片描述

protected WebApplicationContext createRootApplicationContext(ServletContext servletContext) {
        SpringApplicationBuilder builder = this.createSpringApplicationBuilder();
        builder.main(this.getClass());
        ApplicationContext parent = this.getExistingRootWebApplicationContext(servletContext);
        if (parent != null) {
            this.logger.info("Root context already created (using as parent).");
            servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, (Object)null);
            builder.initializers(new ApplicationContextInitializer[]{new ParentContextApplicationContextInitializer(parent)});
        }

        builder.initializers(new ApplicationContextInitializer[]{new ServletContextApplicationContextInitializer(servletContext)});
        builder.contextFactory((webApplicationType) -> {
            return new AnnotationConfigServletWebServerApplicationContext();
        });
        // configure方法是我们重写的方法,调用了springboot的主类。
        builder = this.configure(builder);
        builder.listeners(new ApplicationListener[]{new SpringBootServletInitializer.WebEnvironmentPropertySourceInitializer(servletContext)});
        SpringApplication application = builder.build();
        if (application.getAllSources().isEmpty() && MergedAnnotations.from(this.getClass(), SearchStrategy.TYPE_HIERARCHY).isPresent(Configuration.class)) {
            application.addPrimarySources(Collections.singleton(this.getClass()));
        }

        Assert.state(!application.getAllSources().isEmpty(), "No SpringApplication sources have been defined. Either override the configure method or add an @Configuration annotation");
        if (this.registerErrorPageFilter) {
            application.addPrimarySources(Collections.singleton(ErrorPageFilterConfiguration.class));
        }

        application.setRegisterShutdownHook(false);
        return this.run(application);
    }
  1. springboot应用启动,并创建IOC容器。

参考

  1. https://www.bilibili.com/video/BV1gW411W76m?p=29&spm_id_from=pageDriver

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/100353.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!