文章字数:784,阅读全文大约需要3分钟
SpringBoot
内置了Servlet
容器,所以可以直接运行。而传统的javaWeb
程序需要嵌入到Tomcat
之类的Servlet
容器中才能运行。方便之余却带了问题,我们不能像传统的javaWeb
程序那样操作web.xml
。所以Spring
提供了自定义容器内容的途径。
自动配置 自动配置类EmbeddedServletContainerAutoConfiguration 首先看springBoot
如何自动配置的。 在spring-boot-autoconfigure-xxx.jar
的web
模块中可以找到
EmbeddedServletContainerAutoConfiguration
1 2 3 4 5 6 @AutoConfigureOrder (Ordered.HIGHEST_PRECEDENCE)@Configuration @ConditionalOnWebApplication @Import (BeanPostProcessorsRegistrar.class ) public class EmbeddedServletContainerAutoConfiguration {...
这个类中主要作用是配置三个容器工厂的bean
都是EmbeddedServletContainerFactory
接口的
TomcatEmbeddedServletContainerFactory
JettyEmbeddedServletContainerFactory
UndertowEmbeddedServletContainerFactory
通过注解设置有servlet
依赖和类对应的servlet
容器依赖时,没有其它EmbeddedServletContainerFactory
接口时创建。(重点1)
容器工厂接口EmbeddedServletContainerFactory EmbeddedServletContainerFactory
接口内部只有获取嵌入式servlet
容器的方法
1 2 3 4 5 public interface EmbeddedServletContainerFactory { EmbeddedServletContainer getEmbeddedServletContainer ( ServletContextInitializer... initializers) ;}
容器接口EmbeddedServletContainer EmbeddedServletContainerFactory
获取的容器就是EmbeddedServletContainer
返回的类型。
也对应三个实现类
TomcatEmbeddedServletContainer
JettyEmbeddedServletContainer
UndertowEmbeddedServletContainer
以Tomcat
容器工厂TomcatEmbeddedServletContainerFactory
类为例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 @Override public EmbeddedServletContainer getEmbeddedServletContainer ( ServletContextInitializer... initializers) { Tomcat tomcat = new 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); return getTomcatEmbeddedServletContainer(tomcat); }
最后调用的方法,主要就是创建Tomcat
容器,启动容器。
1 2 3 4 protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer ( Tomcat tomcat) { return new TomcatEmbeddedServletContainer(tomcat, getPort() >= 0 ); }
自定义容器参数 EmbeddedServletContainerCustomizer定制器 自定义属性可以通过ServerProperties
和EmbeddedServletContainerCustomizer
定制器实现,ServerProperties
是EmbeddedServletContainerCustomizer
的子类,所以其实都是EmbeddedServletContainerCustomizer
在起作用。
BeanPostProcessorsRegistrar给容器导入组件的类 在最顶级的自动配置类EmbeddedServletContainerAutoConfiguration
上有个注解@Import(BeanPostProcessorsRegistrar.class)
导入了BeanPostProcessorsRegistrar
这个类成Bean
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar , BeanFactoryAware { private ConfigurableListableBeanFactory beanFactory; @Override public void setBeanFactory (BeanFactory beanFactory) throws BeansException { if (beanFactory instanceof ConfigurableListableBeanFactory) { this .beanFactory = (ConfigurableListableBeanFactory) beanFactory; } } @Override public void registerBeanDefinitions (AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this .beanFactory == null ) { return ; } registerSyntheticBeanIfMissing(registry, "embeddedServletContainerCustomizerBeanPostProcessor" , EmbeddedServletContainerCustomizerBeanPostProcessor.class ) ; 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); } } }
后置处理器:在bean
初始化前(创建完成,还未属性赋值),会执行初始化工作。
registerBeanDefinitions
这个方法导入了EmbeddedServletContainerCustomizerBeanPostProcessor
的Bean
这个Bean
会从ICO
容器中拿到所有EmbeddedServletContainerCustomizer
类,也就是上面说的定制器。可以引入这个定制器来操作容器参数内容
通过容器工厂自定义参数 EmbeddedServletContainerFactory容器工厂 也就是上面的重点1,spring
会根据依赖和有没有其他的工厂来判断是否要注入。我们可以自定义工厂,并加入Bean
。
例:
禁用post和get之外的方法 配置类中1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @Bean public EmbeddedServletContainerFactory servletContainer () { TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() { protected void postProcessContext (Context context) { SecurityConstraint securityConstraint = new SecurityConstraint(); securityConstraint.setUserConstraint("CONFIDENTIAL" ); SecurityCollection collection = new SecurityCollection(); collection.addPattern("/*" ); collection.addMethod("HEAD" ); collection.addMethod("PUT" ); collection.addMethod("DELETE" ); collection.addMethod("OPTIONS" ); collection.addMethod("TRACE" ); collection.addMethod("COPY" ); collection.addMethod("SEARCH" ); collection.addMethod("PROPFIND" ); securityConstraint.addCollection(collection); context.addConstraint(securityConstraint); } }; tomcat.addConnectorCustomizers(connector -> { connector.setAllowTrace(true ); }); return tomcat; }
同等于传统的web.xml
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <security-constraint> <web-resource-collection> <url-pattern>/*</url-pattern> <http-method>HEAD</http-method> <http-method>PUT</http-method> <http-method>DELETE</http-method> <http-method>OPTIONS</http-method> <http-method>TRACE</http-method> <http-method>COPY</http-method> <http-method>SEARCH</http-method> <http-method>PROPFIND</http-method> </web-resource-collection> <auth-constraint> </auth-constraint> </security-constraint>
转发端口(http转到https)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 @Bean public EmbeddedServletContainerFactory servletContainer () { TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() { @Override protected void postProcessContext (Context context) { SecurityConstraint securityConstraint = new SecurityConstraint(); securityConstraint.setUserConstraint("CONFIDENTIAL" ); SecurityCollection collection = new SecurityCollection(); collection.addPattern("/*" ); collection.addMethod("HEAD" ); collection.addMethod("PUT" ); collection.addMethod("DELETE" ); collection.addMethod("OPTIONS" ); collection.addMethod("TRACE" ); collection.addMethod("COPY" ); collection.addMethod("SEARCH" ); collection.addMethod("PROPFIND" ); collection.addMethod("BOGUS" ); securityConstraint.addCollection(collection); context.addConstraint(securityConstraint); } }; tomcat.addAdditionalTomcatConnectors(httpConnector()); return tomcat; } @Bean public Connector httpConnector () { Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol" ); connector.setScheme("http" ); connector.setPort(httpPort); connector.setSecure(false ); connector.setRedirectPort(httpsPort); return connector; }