文章字数: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; }
|