• 10、SpringBoot的Web开发(配置嵌入式Servlet容器)


    SpringBoot默认使用Tomcat作为嵌入式的Servlet容器。

    定制和修改Servlet容器的相关配置

    1、修改和server有关的配置(ServerProperties【也是EmbeddedServletContainerCustomizer】)。

    server.port=8081
    server.servlet.context-path=/crud
    server.tomcat.uri-encoding=UTF-8
    
    //通用的Servlet容器设置
    server.xxx
    //Tomcat的设置
    server.tomcat.xxx
    

    2、编写一个EmbeddedServletContainerCustomizer:嵌入式的Servlet容器的定制器,来修改Servlet容器的配置。

    @Bean  //一定要将这个定制器加入到容器中
    public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
        return new EmbeddedServletContainerCustomizer() {
    
            //定制嵌入式的Servlet容器相关的规则
            @Override
            public void customize(ConfigurableEmbeddedServletContainer container) {
                container.setPort(8083);
            }
        };
    }
    

    注册Servlet三大组件

    由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件。

    注册三大组件【Servlet、Filter、Listener】用以下方式:

    • Servlet:ServletRegistrationBean
    //注册三大组件
    @Bean
    public ServletRegistrationBean myServlet(){
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
        return registrationBean;
    }
    
    • Filter:FilterRegistrationBean
    @Bean
    public FilterRegistrationBean myFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new MyFilter());
        registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
        return registrationBean;
    }
    
    • Listener:ServletListenerRegistrationBean
    @Bean
    public ServletListenerRegistrationBean myListener(){
        ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
        return registrationBean;
    }
    

    SpringBoot帮我们自动注册SpringMVC的时候,会自动注册SpringMVC的前端控制器:DispatcherServlet。

    在DispatcherServletAutoConfiguration中:

    @Bean(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
    @ConditionalOnBean(value = DispatcherServlet.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME)
    public ServletRegistrationBean dispatcherServletRegistration(
          DispatcherServlet dispatcherServlet) {
       ServletRegistrationBean registration = new ServletRegistrationBean(
             dispatcherServlet, this.serverProperties.getServletMapping());
        //默认拦截: /  所有请求;包静态资源,但是不拦截jsp请求;   /*会拦截jsp
        //可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径
        
       registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
       registration.setLoadOnStartup(
             this.webMvcProperties.getServlet().getLoadOnStartup());
       if (this.multipartConfig != null) {
          registration.setMultipartConfig(this.multipartConfig);
       }
       return registration;
    }
    

    SpringBoot支持其他的Servlet容器

    替换为其他嵌入式Servlet容器

    默认支持:Tomcat(默认使用)

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <!--引入web模块默认就是使用嵌入式的Tomcat作为Servlet容器-->
    </dependency>
    

    Jetty

    <!-- 引入web模块 -->
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
       <exclusions>
          <exclusion>
             <artifactId>spring-boot-starter-tomcat</artifactId>
             <groupId>org.springframework.boot</groupId>
          </exclusion>
       </exclusions>
    </dependency>
    
    <!--引入其他的Servlet容器-->
    <dependency>
       <artifactId>spring-boot-starter-jetty</artifactId>
       <groupId>org.springframework.boot</groupId>
    </dependency>
    

    Undertow

    <!-- 引入web模块 -->
    <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-web</artifactId>
       <exclusions>
          <exclusion>
             <artifactId>spring-boot-starter-tomcat</artifactId>
             <groupId>org.springframework.boot</groupId>
          </exclusion>
       </exclusions>
    </dependency>
    
    <!--引入其他的Servlet容器-->
    <dependency>
       <artifactId>spring-boot-starter-undertow</artifactId>
       <groupId>org.springframework.boot</groupId>
    </dependency>
    

    嵌入式Servlet容器自动配置原理

    EmbeddedServletContainerAutoConfiguration:嵌入式的Servlet容器自动配置。2.x版本为EmbeddedWebServerFactoryCustomizerAutoConfiguration。

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnWebApplication
    @EnableConfigurationProperties({ServerProperties.class})
    public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
    
    @AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
    @Configuration
    @ConditionalOnWebApplication
    @Import(BeanPostProcessorsRegistrar.class)
    //导入BeanPostProcessorsRegistrar:Spring注解版;给容器中导入一些组件
    //导入了EmbeddedServletContainerCustomizerBeanPostProcessor:
    //后置处理器:bean初始化前后(创建完对象,还没属性赋值)执行初始化工作
    public class EmbeddedServletContainerAutoConfiguration {
        
        @Configuration
    	@ConditionalOnClass({ Servlet.class, Tomcat.class })//判断当前是否引入了Tomcat依赖;
    	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)//判断当前容器没有用户自己定义EmbeddedServletContainerFactory:嵌入式的Servlet容器工厂;作用:创建嵌入式的Servlet容器
    	public static class EmbeddedTomcat {
    
    		@Bean
    		public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
    			return new TomcatEmbeddedServletContainerFactory();
    		}
    
    	}
        
        /**
    	 * Nested configuration if Jetty is being used.
    	 */
    	@Configuration
    	@ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
    			WebAppContext.class })
    	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
    	public static class EmbeddedJetty {
    
    		@Bean
    		public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
    			return new JettyEmbeddedServletContainerFactory();
    		}
    
    	}
    
    	/**
    	 * Nested configuration if Undertow is being used.
    	 */
    	@Configuration
    	@ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
    	@ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
    	public static class EmbeddedUndertow {
    
    		@Bean
    		public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
    			return new UndertowEmbeddedServletContainerFactory();
    		}
    
    	}
    

    1、EmbeddedServletContainerFactory(嵌入式Servlet容器工厂)

    public interface EmbeddedServletContainerFactory {
    
       //获取嵌入式的Servlet容器
       EmbeddedServletContainer getEmbeddedServletContainer(
             ServletContextInitializer... initializers);
    
    }
    

    2、EmbeddedServletContainer:(嵌入式的Servlet容器)

    springboot2.x

    3、以TomcatEmbeddedServletContainerFactory为例

    @Override
    public EmbeddedServletContainer getEmbeddedServletContainer(
          ServletContextInitializer... initializers) {
        //创建一个Tomcat
       Tomcat tomcat = new Tomcat();
        
        //配置Tomcat的基本环节
       File baseDir = (this.baseDirectory != null ? this.baseDirectory
             : createTempDir("tomcat"));
       tomcat.setBaseDir(baseDir.getAbsolutePath());
       Connector connector = new Connector(this.protocol);
       tomcat.getService().addConnector(connector);
       customizeConnector(connector);
       tomcat.setConnector(connector);
       tomcat.getHost().setAutoDeploy(false);
       configureEngine(tomcat.getEngine());
       for (Connector additionalConnector : this.additionalTomcatConnectors) {
          tomcat.getService().addConnector(additionalConnector);
       }
       prepareContext(tomcat.getHost(), initializers);
        
        //将配置好的Tomcat传入进去,返回一个EmbeddedServletContainer;并且启动Tomcat服务器
       return getTomcatEmbeddedServletContainer(tomcat);
    }
    

    4、我们对嵌入式容器的配置修改是通过ServerProperties、EmbeddedServletContainerCustomizer进行生效的。

    EmbeddedServletContainerCustomizer:定制器帮我们修改了Servlet容器的配置。

    修改的原理:

    容器中导入了EmbeddedServletContainerCustomizerBeanPostProcessor

    //初始化之前
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
          throws BeansException {
        //如果当前初始化的是一个ConfigurableEmbeddedServletContainer类型的组件
       if (bean instanceof ConfigurableEmbeddedServletContainer) {
           //
          postProcessBeforeInitialization((ConfigurableEmbeddedServletContainer) bean);
       }
       return bean;
    }
    
    private void postProcessBeforeInitialization(
    			ConfigurableEmbeddedServletContainer bean) {
        //获取所有的定制器,调用每一个定制器的customize方法来给Servlet容器进行属性赋值;
        for (EmbeddedServletContainerCustomizer customizer : getCustomizers()) {
            customizer.customize(bean);
        }
    }
    
    private Collection<EmbeddedServletContainerCustomizer> getCustomizers() {
        if (this.customizers == null) {
            // Look up does not include the parent context
            this.customizers = new ArrayList<EmbeddedServletContainerCustomizer>(
                this.beanFactory
                //从容器中获取所有这葛类型的组件:EmbeddedServletContainerCustomizer
                //定制Servlet容器,给容器中可以添加一个EmbeddedServletContainerCustomizer类型的组件
                .getBeansOfType(EmbeddedServletContainerCustomizer.class,
                                false, false)
                .values());
            Collections.sort(this.customizers, AnnotationAwareOrderComparator.INSTANCE);
            this.customizers = Collections.unmodifiableList(this.customizers);
        }
        return this.customizers;
    }
    
    //ServerProperties也是定制器
    

    总结:

    1、SpringBoot根据导入的依赖情况,给容器中添加相应的EmbeddedServletContainerFactory(TomcatEmbeddedServletContainerFactory)。

    2、容器中某个组件要创建对象就会惊动后置处理器:EmbeddedServletContainerCustomizerBeanPostProcessor。只要是嵌入式的Servlet容器工厂,后置处理器就工作。

    3、后置处理器从容器中获取所有的EmbeddedServletContainerCustomizer,调用定制器的定制方法。

    嵌入式Servlet容器启动原理

    什么时候创建嵌入式的Servlet容器工厂?什么时候获取嵌入式的Servlet容器并启动Tomcat?

    SpringBoot获取嵌入式的Servlet容器工厂的步骤:

    1、SpringBoot应用启动运行run方法

    2、refreshContext(context);SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;如果是web应用创建ServletWebServerApplicationContext容器。

    3、refresh(context)刷新刚才创建好的ioc容器

    4、 onRefresh(); web的ioc容器重写了onRefresh方法

    5、webioc容器会创建嵌入式的Servlet容器;createEmbeddedServletContainer();

    6、获取嵌入式的Servlet容器工厂:

    EmbeddedServletContainerFactory containerFactory = getEmbeddedServletContainerFactory();

    从ioc容器中获取EmbeddedServletContainerFactory 组件;TomcatEmbeddedServletContainerFactory创建对象,后置处理器一看是这个对象,就获取所有的定制器来先定制Servlet容器的相关配置;

    7、使用容器工厂获取嵌入式的Servlet容器:this.embeddedServletContainer = containerFactory.getEmbeddedServletContainer(getSelfInitializer());

    8、嵌入式的Servlet容器创建对象并启动Servlet容器。

    coydone的博客
  • 相关阅读:
    心理分析:爱挑刺的人是什么心理
    推荐几个最好用的CRM软件,本人亲测
    Xamarin安装和跳坑指南
    一个简单的游戏开发框架(七.动作Motion)
    一个简单的游戏开发框架(六.行为Action)
    一个简单的游戏开发框架(五.对象Object)
    一个简单的游戏开发框架(四.舞台Stage)
    一个简单的游戏开发框架(三.事件管理)
    一个简单的游戏开发框架(二.对外接口)
    一个简单的游戏开发框架(一.框架的意义)
  • 原文地址:https://www.cnblogs.com/coydone/p/13821251.html
Copyright © 2020-2023  润新知