十一、嵌入式Servlet容器自动配置原理&自动启动原理

一、配置嵌入式Servlet容器

SpringBoot默认使用Tomcat作为嵌入式servlet容器

产生的问题:

如果是外置的话可以直接找到tomcat中的server.xml进行修改。
那么如何定制和修改SpringBoot中的Servlet容器的相关配置?

1、修改和server有关的配置(ServerProperties):

server.port = 8080
server.context.path=/crud

server.tomcat.url-encoding=UTF-8

//通用的Servlet容器设置
server.xxx
//Tomcat相关的设置
server.tomcat.xxx

2、手动编写一个嵌入式的Servlet容器的定制器

编写一个EmbeddedServletContainerCustomizer:嵌入式的servlet容器的定制器,来修改servlet容器的配置(Spring Boot2.0以上版本EmbeddedServletContainerCustomizer被WebServerFactoryCustomizer替代)

 //配置嵌入式的servlet容器
 @Bean
 public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
     return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){
         //customize方法就是根据相关配置初始化Servlet容器
         @Override
         public void customize(ConfigurableWebServerFactory configurableWebServerFactory) {
         	//优先级高于配置文件
             configurableWebServerFactory.setPort(8083);
         }
     };
 }

补充一下前面提到的如何修改SpringBoot的默认配置?
模式:

  1. Springboot在自动配置组件的时候,会先看容器中是否有用户自己配置的(@Bean、@Component),如果有则优先使用用户配置的,如果没有再使用自动配置,有些组件如果允许配置多个(ViewResource)将会把用户配置的和默认配置的组合起来。
  2. 在SpringBoot中会有非常多的XXXConfigure帮助我们进行扩展配置
  3. 在SpringBoot中会有非常多的XXXCustomize帮助我们进行定制配置

二、在SpringBoot中注册三大组件(Servlet、Filter、Listener)

springboot是以jar包的方式启动嵌入式的tomcat,,而不是创建一个标准的web应用的目录结构。如果是一个web应用的目录结构,webapp/WEB-INF/web.xml三大组件将注册在web.xml中。然而springboot没有提供web.xml文件,怎么注册呢?

注册三大组件用以下方式:

1、注册Servlet

在容器中注册自定义的Servlet:

@Configuration
public class MyServerConfiguration{
      @Bean
      public ServletRegistrationBean myServlet(){
          ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new MyServlet(),"/myServlet");
          return servletRegistrationBean;
      }
}

自定义的Servlet:

public class MyServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter out = resp.getWriter();
        out.write("Hello,MyServlet");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

    }
}

2、注册过滤器Filter

在容器中注册自定义的过滤器:

@Configuration
public class MyServerConfiguration {
     /*注册过滤器Filter组件*/
     @Bean
      public FilterRegistrationBean myFilter(){
          FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
          filterRegistrationBean.setFilter(new MyFilter());
          filterRegistrationBean.setUrlPatterns(Arrays.asList("/myServlet","/hello"));
          return filterRegistrationBean;
      }
}

自定义的过滤器:

public class MyFilter implements Filter {
    /* *
     * @description //TODO  过滤器的初始化方法
     */
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }
    
    /* 
     * @description //TODO 过滤器的执行方法
     */
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("MyFiler is processing……");
        chain.doFilter(servletRequest,servletResponse);
    }
  /* 过滤器的销毁方法*/
    @Override
    public void destroy() {

    }
}

3、注册监听器(Listener)

在容器中注册监听器:

 @Bean
      public ServletListenerRegistrationBean myServletListener(){
          ServletListenerRegistrationBean<MyServletListener> servletRegistrationBean = new ServletListenerRegistrationBean<MyServletListener>(new MyServletListener());
          return servletRegistrationBean;
      }

自定义的监听器:

public class MyServletListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("web应用启动……");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("web容器销毁……");
    }
}

可以注册的监听器:

static {
        Set<Class<?>> types = new HashSet();
        types.add(ServletContextAttributeListener.class);
        types.add(ServletRequestListener.class);
        types.add(ServletRequestAttributeListener.class);
        types.add(HttpSessionAttributeListener.class);
        types.add(HttpSessionListener.class);
        types.add(ServletContextListener.class);
        SUPPORTED_TYPES = Collections.unmodifiableSet(types);
    }

SpringBoot会帮助我们自动注册Spring MVC的前端控制器 (DispatcherServlet)

默认拦截(/):除jsp请求外,拦截所有的请求,包括静态资源;(/*):会拦截jsp请求。
  可以通过server.servletPath来修改SpringMVC前端控制器默认拦截的请求路径。

@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());
	registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
	registration.setLoadOnStartup(
	this.webMvcProperties.getServlet().getLoadOnStartup());
	if (this.multipartConfig != null) {
		registration.setMultipartConfig(this.multipartConfig);
	}
	return registration;
}

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

springboot2.x。
回顾修改servlet容器的相关配置:从下面的代码片,可以看到我们要从容器中获取一个组件ConfigurableWebServerFactory ,然后利用configurableWebServerFactory进行属性的设置,设置完属性,把WebServerFactoryCustomizer注入到容器中。

//配置嵌入式的servlet容器
 @Bean
 public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
     return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){
         //customize方法就是根据相关配置初始化Servlet容器
         @Override
         public void customize(ConfigurableWebServerFactory configurableWebServerFactory) {
         	//优先级高于配置文件
             configurableWebServerFactory.setPort(8083);
         }
     };
 }

1.猜想自动配置时,要先在容器中注册WebServerFactory,看一下WebServerFactory继承结构:
在这里插入图片描述

看ServletWebServerFactoryConfiguration:这是一个配置类,Spring容器启动时,添加到容器中,并且如果导入了tomcat,jetty或者Undertow依赖,就会把对应的XXXServletWebServerFactory 导入到容器中。

class ServletWebServerFactoryConfiguration {
    ServletWebServerFactoryConfiguration() {
    }

    @Configuration(
        proxyBeanMethods = false
    )
    //如果有这三个类就生效,即如果依赖了tomcat,这个就生效,下面的也是如此
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    static class EmbeddedTomcat {
        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;
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Servlet.class, Undertow.class, SslClientAuthMode.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    static class EmbeddedUndertow {
        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;
        }

        @Bean
        UndertowServletWebServerFactoryCustomizer undertowServletWebServerFactoryCustomizer(ServerProperties serverProperties) {
            return new UndertowServletWebServerFactoryCustomizer(serverProperties);
        }
    }

    @Configuration(
        proxyBeanMethods = false
    )
    @ConditionalOnClass({Servlet.class, Server.class, Loader.class, WebAppContext.class})
    @ConditionalOnMissingBean(
        value = {ServletWebServerFactory.class},
        search = SearchStrategy.CURRENT
    )
    static class EmbeddedJetty {
        EmbeddedJetty() {
        }

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

2.继续猜想容器中有了WebServerFactory,那么WebServerFactoryCustomizer是在哪里被注入的呢
WebServerFactoryCustomizer的继承结构:

在这里插入图片描述
看EmbeddedWebServerFactoryCustomizerAutoConfiguration:我们导入了那个servlet容器依赖,哪个xxxWebServerFactoryCustomizer 就别注入到容器中。

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication
@EnableConfigurationProperties(ServerProperties.class)
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
	@Configuration(proxyBeanMethods = false)
	//当容器中存在Tomcat相关类就生效,下面几个也是如此,也就是说我们导入哪个依赖,哪个就生效
	@ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class })
	public static class TomcatWebServerFactoryCustomizerConfiguration {
		@Bean
		public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
		}
	}
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Server.class, Loader.class, WebAppContext.class })
	public static class JettyWebServerFactoryCustomizerConfiguration {
		@Bean
		public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new JettyWebServerFactoryCustomizer(environment, serverProperties);
		}
	}
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnClass({ Undertow.class, SslClientAuthMode.class })
	public static class UndertowWebServerFactoryCustomizerConfiguration {
		@Bean
		public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment,
				ServerProperties serverProperties) {
			return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
		}
	}
}

3.容器中WebServerFactory有了,WebServerFactoryCustomizer也有了,那么是什么时候初始化Servlet容器的呢(就是调用WebServerFactoryCustomizer的customize方法)
先看类ServletWebServerFactoryAutoConfiguration :

@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
		ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
		ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
		ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
	//...
	public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
		//...
		//这个类的作用是往容器中添加一些组件
		public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
				BeanDefinitionRegistry registry) {
//往容器中添加了WebServerFactoryCustomizerBeanPostProcessor组件(web服务工厂定制器的后置处理器)。
//这个类实现了BeanPostProcessor,属于bean的后置处理器。作用是在bean初始化前后加一些自己的逻辑处理
			registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
					WebServerFactoryCustomizerBeanPostProcessor.class);
			registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
					ErrorPageRegistrarBeanPostProcessor.class);
		}

再看bean的后置处理器WebServerFactoryCustomizerBeanPostProcessor:简单点说就是在bean(xxxServletWebServerFactory )的初始化之前,获取所有的定制器来先定制servlet相关配置。

public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
    private List<WebServerFactoryCustomizer<?>> customizers;
    ...
    //bean初始化前调用
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName)
			throws BeansException {
		//判断这个bean的类型是WebServerFactory
	    //TomcatServletWebServerFactory继承了WebServerFactory,所以它初始化时,会往下执行
		if (bean instanceof WebServerFactory) {
			postProcessBeforeInitialization((WebServerFactory) bean);
		}
		return bean;
	}
    //bean初始化后调用
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName)
			throws BeansException {
		return bean;
	}
	@SuppressWarnings("unchecked")
	private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
		LambdaSafe
		         //获取Web服务工厂定制器(WebServerFactoryCustomizer)
				.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),
						webServerFactory)
				.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
				//调用customizer的customize方法,定制嵌入式容器的servlet容器相关的规则,优先级高于配置文件
				.invoke((customizer) -> customizer.customize(webServerFactory));
	}
	private Collection<WebServerFactoryCustomizer<?>> getCustomizers() {
		if (this.customizers == null) {
			// Look up does not include the parent context
			this.customizers = new ArrayList<>(getWebServerFactoryCustomizerBeans());
			this.customizers.sort(AnnotationAwareOrderComparator.INSTANCE);
			this.customizers = Collections.unmodifiableList(this.customizers);
		}
		return this.customizers;
	}
	@SuppressWarnings({ "unchecked", "rawtypes" })
	private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
	    //返回WebServerFactoryCustomizer类型的Customizer(定制器)
	    //上面自动配置类注册的Web服务工厂定制器(xxxWebServerFactoryCustomizer)就是继承了WebServerFactoryCustomizer,
	    //所以这里将那些Customizer(定制器)返回
		return (Collection) this.beanFactory
				.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
	}
}

四、嵌入式Servlet自动启动原理

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

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

  1. Springboot应用启动运行run方法
  2. 、refreshContext(context);SpringBoot刷新IOC容器【创建IOC容器对象,并初始化容器,创建容器中的每一个组件】;如果是web应用创建AnnotationConfigEmbeddedWebApplicationContext,否则:AnnotationConfigApplicationContext
  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容器;先启动嵌入式的Servlet容器,再将ioc容器中剩下没有创建出的对象获取出来;
    IOC容器启动创建嵌入式的Servlet容器

五、如何使用其他Servlet容器

在这里插入图片描述
在SpringBoot中,默认使用Tomcat服务器,但是也可以切换成·Undertow(不支持JSP)和Jetty(适合开发长连接)。

如何切换其他容器?

【1】先排除SpringBoot的默认容器Tomcat

<!--导入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>

【2】引入目标容器的依赖

① 切换成undertow容器:

<!--切换其他容器
   1、先排除默认的tomcat容器
   2、引入要切换的容器的坐标
-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-undertow</artifactId>
</dependency>

② 切换成jetty容器

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jetty</artifactId>
    <version>版本号</version>
</dependency>
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值