SpringBoot_配置嵌入式Servlet容器

前言:

以前部署web项目,需要把web项目打包成war包,然后放在外部配置好的中间件环境中,比如Tomcat容器,将web项目war包放在 \Tomcat_Home\webapps目录下,然后运行 \Tomcat_Home\bin目录下的startup.bat或者startup.sh来启动项目;

SpringBoot中,默认使用Tomcat作为嵌入式的Servlet容器
在这里插入图片描述


修改嵌入式Servlet容器的相关配置:

方式一:在配置文件中修改和server有关的配置:

server.port=8083
server.context‐path=/Restful
server.tomcat.uri‐encoding=UTF‐8

//通用的Servlet容器设置
server.xxx

//Tomcat的设置
server.tomcat.xxx

方式二:在自定义配置类中,编写嵌入式的Servlet容器的定制器,来修改Servlet容器配置:

SpringBoot1.5.x 版本: EmbeddedServletContainerCustomizer

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

SpringBoot2.x.x 版本: WebServerFactoryCustomizer

@Bean //要将这个定制器加入到容器中
public WebServerFactoryCustomizer webServerFactoryCustomizer() {
        
     return new WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>() {

             //定制嵌入式Servlet容器的相关规则
             @Override
             public void customize(ConfigurableServletWebServerFactory factory) {
                      factory.setPort(8083);
             }
      };
 }

注册Servlet三大组件:

SpringBoot默认以jar包的方式启动嵌入式Servlet容器来启动SpringBootweb应用,没有web.xml文件,所以我们在自定义配置类中注册三大组件;

注册 ServletServletRegistrationBean

 @Bean
 public ServletRegistrationBean myServlet(){
        ServletRegistrationBean registrationBean = new ServletRegistrationBean(new MyServlet(),"/MyServlet");
        registrationBean.setLoadOnStartup(1);
        return registrationBean;
 }

注册 FilterFilterRegistrationBean

@Bean
public FilterRegistrationBean myFilter(){
        FilterRegistrationBean registrationBean = new FilterRegistrationBean();
        registrationBean.setFilter(new MyFilter());
        //设置拦截
        registrationBean.setUrlPatterns(Arrays.asList("/HELLO/hello","/MyServlet"));
        return registrationBean;
}

注册 ListenerServletListenerRegistrationBean

 @Bean
 public ServletListenerRegistrationBean myListener(){
        ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
        return registrationBean;
 }

举例:
SpringBoot自动配置SpringMVC的时候,自动注册SpringMVC前端控制器:DIspatcherServlet

DispatcherServletAutoConfiguration中:

 @Bean(name = {"dispatcherServletRegistration"})
 @ConditionalOnBean(value = {DispatcherServlet.class},name = {"dispatcherServlet"})
 public DispatcherServletRegistrationBean dispatcherServletRegistration(DispatcherServlet dispatcherServlet, WebMvcProperties webMvcProperties, ObjectProvider<MultipartConfigElement> multipartConfig) {
            DispatcherServletRegistrationBean registration = new DispatcherServletRegistrationBean(dispatcherServlet, webMvcProperties.getServlet().getPath());
          	//默认拦截: / 所有请求;包静态资源,但是不拦截jsp请求; /*会拦截jsp
			//可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径
            registration.setName("dispatcherServlet");
            registration.setLoadOnStartup(webMvcProperties.getServlet().getLoadOnStartup());
            multipartConfig.ifAvailable(registration::setMultipartConfig);
            return registration;
        }

替换为其他嵌入式Servlet容器:

Tomcat: 默认使用

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

Jetty: 适用于长连接应用

   <!‐‐ 引入web模块 ‐‐>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring‐boot‐starter‐web</artifactId>
         <!--排除Tomcat-->
       <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: 高性能,非阻塞, 不支持JSP

  <!‐‐ 引入web模块 ‐‐>
   <dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring‐boot‐starter‐web</artifactId>
         <!--排除Tomcat-->
       <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容器自动配置原理:

SpringBoot1.5.x 版本: EmbeddedServletContainerAutoConfiguration:


SpringBoot2.x.x 版本: ServletWebServerFactoryConfiguration:

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
    ServletWebServerFactoryConfiguration() {}

    @Configuration(proxyBeanMethods = false)
   // 判断classpath下有没有Servlet, Undertow, SslClientAuthMode这三个类,即看maven有没有引入对应的依赖
    @ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
    //容器中没有ServletWebServerFactory这个组件,下面的配置就生效
    @ConditionalOnMissingBean(value = {ServletWebServerFactory.class},search = SearchStrategy.CURRENT)
    static class EmbeddedUndertow {   //Undertow配置类
        EmbeddedUndertow() {
        }

        @Bean
        UndertowServletWebServerFactory undertowServletWebServerFactory(ObjectProvider<UndertowDeploymentInfoCustomizer> deploymentInfoCustomizers, ObjectProvider<UndertowBuilderCustomizer> builderCustomizers) {
            UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
            factory.getDeploymentInfoCustomizers().addAll((Collection)deploymentInfoCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getBuilderCustomizers().addAll((Collection)builderCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }
    }

    @Configuration(proxyBeanMethods = false)
   // 判断classpath下有没有Servlet, Server, Loader,WebAppContext这四个类,即看maven有没有引入对应的依赖
    @ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})
     //容器中没有ServletWebServerFactory这个组件,下面的配置就生效
    @ConditionalOnMissingBean(value = {ServletWebServerFactory.class},search = SearchStrategy.CURRENT)
    static class EmbeddedJetty {  //Jetty配置类
        EmbeddedJetty() {
        }

        @Bean
        JettyServletWebServerFactory JettyServletWebServerFactory(ObjectProvider<JettyServerCustomizer> serverCustomizers) {
            JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
            factory.getServerCustomizers().addAll((Collection)serverCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }
    }

    @Configuration(proxyBeanMethods = false)
       // 判断classpath下有没有Servlet, Tomcat, UpgradeProtocol这三个类,即看maven有没有引入对应的依赖
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
     //容器中没有ServletWebServerFactory这个组件,下面的配置就生效
    @ConditionalOnMissingBean(value = {ServletWebServerFactory.class},search = SearchStrategy.CURRENT)
    static class EmbeddedTomcat { //Tomcat配置类
        EmbeddedTomcat() {
        }

        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            factory.getTomcatConnectorCustomizers().addAll((Collection)connectorCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatContextCustomizers().addAll((Collection)contextCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatProtocolHandlerCustomizers().addAll((Collection)protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }
    }
}

TomcatServletWebServerFactory为例:类中有 getWebServer 方法;

 public WebServer getWebServer(ServletContextInitializer... initializers) {
        if (this.disableMBeanRegistry) {
            Registry.disableRegistry();
        }
		//创建一个Tomcat
        Tomcat tomcat = new Tomcat();
        //配置Tomcat的基本环节
        File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        tomcat.getService().addConnector(connector);
        this.customizeConnector(connector);
        tomcat.setConnector(connector);
        tomcat.getHost().setAutoDeploy(false);
        this.configureEngine(tomcat.getEngine());
        Iterator var5 = this.additionalTomcatConnectors.iterator();

        while(var5.hasNext()) {
            Connector additionalConnector = (Connector)var5.next();
            tomcat.getService().addConnector(additionalConnector);
        }

        this.prepareContext(tomcat.getHost(), initializers);
        //将配置好的Tomcat传入进去,返回一个TomcatWebServer;并且启动Tomcat服务器
        return this.getTomcatWebServer(tomcat);
    }

getTomcatWebServer方法中,返回TomcatWebServer构造方法;

protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
        return new TomcatWebServer(tomcat, this.getPort() >= 0);
    }

进入构造方法TomcatWebServer,在构造方法中又调用了this.initialize() 方法;

public TomcatWebServer(Tomcat tomcat, boolean autoStart) {
        this.monitor = new Object();
        this.serviceConnectors = new HashMap();
        Assert.notNull(tomcat, "Tomcat Server must not be null");
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        
        this.initialize();
    }

进入initialize方法,该方法就会启动tomcat

private void initialize() throws WebServerException {
        logger.info("Tomcat initialized with port(s): " + this.getPortsDescription(false));
          ...
          
         //启动Tomcat
         this.tomcat.start();
                
         ...
    }

修改嵌入式Servlet容器配置的原理:

SpringBoot2.x.x 版本: ServletWebServerFactoryAutoConfiguration

//把ServerProperties注册到容器中,并读取全局配置文件中的数据赋值给类中的属性
@EnableConfigurationProperties({ServerProperties.class})
使用@Import注解将对应组件加载到容器
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,//后置处理类
		 EmbeddedTomcat.class, //Tomcat配置类
         EmbeddedJetty.class, //Jetty配置类
         EmbeddedUndertow.class}) //Undertow配置类
public class ServletWebServerFactoryAutoConfiguration {
    public ServletWebServerFactoryAutoConfiguration() {
    }
}

BeanPostProcessorsRegistrar

/**
   * 后置处理器:Bean初始化前后(创建好对象,还没赋值)执行初始化工作
   * 容器中某个组件要创建bean,就会惊动后置处理器,容器中要创建bean这个才会触发,下面的方法才会被调用
   * 当前这个bean就是嵌入式的Servlet容器
   */
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
        private ConfigurableListableBeanFactory beanFactory;

        public BeanPostProcessorsRegistrar() {
        }

   		//设置BeanFactory
        public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
            if (beanFactory instanceof ConfigurableListableBeanFactory) {
                this.beanFactory = (ConfigurableListableBeanFactory)beanFactory;
            }

        }
			
		//注册bean的定义
        public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
            if (this.beanFactory != null) {
            //注意 WebServerFactoryCustomizerBeanPostProcessor类
                this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
                this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
            }
        }

        private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
            if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
                RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
                beanDefinition.setSynthetic(true);
                registry.registerBeanDefinition(name, beanDefinition);
            }

        }
    }

WebServerFactoryCustomizerBeanPostProcessorpostProcessBeforeInitialization 方法;

//如果当前初始化的是一个WebServerFactory类型的组件,就调用postProcessBeforeInitialization方法
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof WebServerFactory) {
            this.postProcessBeforeInitialization((WebServerFactory)bean);
        }

        return bean;
    }
    
// 获得所有的定制器,然后遍历,调用每个定制器的customize方法,并把嵌入式的Servlet容器传进去
 private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        ((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
            customizer.customize(webServerFactory);
        });
    }
    

 private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
        if (this.customizers == null) {
         	//从容器中获取所有这种类型的组件:WebServerFactoryCustomizer
            this.customizers = new ArrayList(this.getWebServerFactoryCustomizerBeans());
            this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
            this.customizers = Collections.unmodifiableList(this.customizers);
        }

        return this.customizers;
    }

定制Servlet容器,给容器中可以添加一个WebServerFactoryCustomizer类型的组件
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
        return this.beanFactory.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
    }
    

步骤:
1,SpringBoot根据导入的依赖情况,给容器中添加相应的ServletWebServerFactory(嵌入式Servlet容器工厂);
2,容器中某个组件要创建对象就会惊动后置处理器 BeanPostProcessorsRegistrar,只要是嵌入式的Servlet容器工厂,后置处理器就工作;
3,后置处理器,从容器中获取所有的 WebServerFactoryCustomizer,调用定制器的定制方法;


嵌入式Servlet容器启动原理:

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

2,this.refreshContext(context):SpringBoot刷新IOC容器:创建IOC容器对象,并初始化容器,创建容器中的每一个组件;如果是Web应用,创建AnnotationConfigServletWebServerApplicationContext
或者 AnnotationConfigReactiveWebServerApplicationContext
否则创建 AnnotationConfigApplicationContext


 protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                switch(this.webApplicationType) {
                case SERVLET:
                    contextClass = Class.forName("org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext");
                    break;
                case REACTIVE:
                    contextClass = Class.forName("org.springframework.boot.web.reactive.context.AnnotationConfigReactiveWebServerApplicationContext");
                    break;
                default:
                    contextClass = Class.forName("org.springframework.context.annotation.AnnotationConfigApplicationContext");
                }
            } catch (ClassNotFoundException var3) {
                throw new IllegalStateException("Unable create a default ApplicationContext, please specify an ApplicationContextClass", var3);
            }
        }

        return (ConfigurableApplicationContext)BeanUtils.instantiateClass(contextClass);
    }

3, this.refresh(context):刷新刚才创建好的ioc容器;


 public void refresh() throws BeansException, IllegalStateException {
        Object var1 = this.startupShutdownMonitor;
        synchronized(this.startupShutdownMonitor) {
            this.prepareRefresh();
            ConfigurableListableBeanFactory beanFactory = this.obtainFreshBeanFactory();
            this.prepareBeanFactory(beanFactory);

            try {
                this.postProcessBeanFactory(beanFactory);
                this.invokeBeanFactoryPostProcessors(beanFactory);
                this.registerBeanPostProcessors(beanFactory);
                this.initMessageSource();
                this.initApplicationEventMulticaster();
                this.onRefresh();
                this.registerListeners();
                this.finishBeanFactoryInitialization(beanFactory);
                this.finishRefresh();
            } catch (BeansException var9) {
                if (this.logger.isWarnEnabled()) {
                    this.logger.warn("Exception encountered during context initialization - cancelling refresh attempt: " + var9);
                }

                this.destroyBeans();
                this.cancelRefresh(var9);
                throw var9;
            } finally {
                this.resetCommonCaches();
            }

        }
    }

4,onRefresh():Web的IOC容器重写了onRefresh方法;


 protected void onRefresh() {
        super.onRefresh();

        try {
            this.createWebServer();
        } catch (Throwable var2) {
            throw new ApplicationContextException("Unable to start web server", var2);
        }
    }

5,createWebServer():Web的IOC容器会创建嵌入式的Servlet容器;


private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = this.getServletContext();
        if (webServer == null && servletContext == null) {
            ServletWebServerFactory factory = this.getWebServerFactory();
            this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});
        } else if (servletContext != null) {
            try {
                this.getSelfInitializer().onStartup(servletContext);
            } catch (ServletException var4) {
                throw new ApplicationContextException("Cannot initialize servlet context", var4);
            }
        }

        this.initPropertySources();
    }

6,获取嵌入式的Servlet容器工厂:从IOC容器中获取ServletWebServerFactory组件;例如:TomcatServletWebServerFactory 创建对象,后置处理器发现是这个对象,就获取所有的定制器来先定制Servlet容器的相关配置;

  ServletWebServerFactory factory = this.getWebServerFactory();

7,使用容器工厂获取嵌入式的Servlet容器

 this.webServer = factory.getWebServer(new ServletContextInitializer[]{this.getSelfInitializer()});

8,嵌入式的Servlet容器创建对象并启动Servlet容器:先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创建出的对象获取出来;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值