一、嵌入式容器的配置
-
SpringBoot中,默认的web容器为Tomcat。因为我SpringBoot版本是2.0.2,所以tomcat的版本为8.5.31,这些都可以从依赖树中看到。如下图
-
既然SpringBoot默认嵌入了tomcat容器,那么我们该如何去配置它的一些属性呢?比如说端口号之类的。SpringBoot给我们以下2种方式去配置。
-
通过yml配置文件
-
在yml中配置,我们只要配置
server.xxx
即可 。如:1
2
3server:
port: 80 # 配置端口
address: 127.0.0.1 # 配置服务器绑定的地址
-
-
通过自定义实现了
WebServerFactoryCustomizer
的类(**在SpringBoot2.0.x以下,需要实现EmbeddedServletContainerCustomizer
接口 **)-
2.0.x以下版本
1
2
3
4
5
6
7
8
9
10
11
12//一定要将这个定制器加入到容器中
public EmbeddedServletContainerCustomizer embeddedServletContainerCustomizer(){
return new EmbeddedServletContainerCustomizer() {
//定制嵌入式的Servlet容器相关的规则
public void customize(ConfigurableEmbeddedServletContainer container) {
//设置端口
container.setPort(8083);
}
};
} -
2.0.x版本
1
2
3
4
5
6
7
8
9
10
11
12
public class MyServerConfig {
public WebServerFactoryCustomizer tomcatCustomizer (){
return new WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>() {
public void customize(ConfigurableServletWebServerFactory customizer) {
customizer.setPort(8088);
}
};
}
}
-
二、配置原理:
-
我们首先来看看yml中server绑定的类
ServerProperties
在里面定义了各种和web容器配置相关的属性。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"server", ignoreUnknownFields = true) (prefix =
public class ServerProperties {
/**
* Server HTTP port.
*/
private Integer port;
/**
* Network address to which the server should bind.
*/
private InetAddress address;
/**
* Servlet properties.
*/
public static class Servlet {
....
}
/**
* Tomcat properties.
*/
public static class Tomcat {
...
}
...
} -
我们现在看看
EmbeddedWebServerFactoryCustomizerAutoConfiguration
该类简单翻译出来就是“嵌入式Web服务器工厂定制器”声明如下: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
.class) //为ServerProperties类绑定配置文件 (ServerProperties
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
//只有在classPath下引入了Tomcat的jar包才会生效
.class, UpgradeProtocol.class }) ({ Tomcat
public static class TomcatWebServerFactoryCustomizerConfiguration {
//创建一个TomcatWebServerFactoryCustomizer对象,并把配置文件传进去。
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
//只有在classp下引入了jetty的jar包才生效,Server.class是jetty的特征。
.class, Loader.class, WebAppContext.class }) ({ Server
public static class JettyWebServerFactoryCustomizerConfiguration {
//创建jetty的web容器工厂定制器。把配置文件传进去
public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
return new JettyWebServerFactoryCustomizer(environment, serverProperties);
}
}
//只有在classp下引入了Undertow的jar包才生效,Undertow.class是Undertow的特征。
.class, SslClientAuthMode.class }) ({ Undertow
public static class UndertowWebServerFactoryCustomizerConfiguration {
//创建Undertow的web容器工厂定制器。把配置文件传进去
public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(
Environment environment, ServerProperties serverProperties) {
return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
}
}
}从上面的源码看,这个自动配置类在启动的时候,为ServerProperties类绑定配置文件,然后把ServerProperties注入进来。再通过导入的web容器的jar包判断是导入的哪个web容器,生成对应的容器工厂定制器。并把配置文件传进去。下面我们就针对tomcat来进行分析。
TomcatWebServerFactoryCustomizer
源码如下(在SpringBoot2.0.x以下,是EmbeddedServletContainerCustomizer
类 ) :1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class TomcatWebServerFactoryCustomizer implements
//该类也实现了WebServerFactoryCustomizer接口
WebServerFactoryCustomizer<ConfigurableTomcatWebServerFactory>, Ordered {
//重写customize方法
public void customize(ConfigurableTomcatWebServerFactory factory) {
ServerProperties properties = this.serverProperties;
ServerProperties.Tomcat tomcatProperties = properties.getTomcat();
....//设置各种配置
//设置静态资源路径
customizeStaticResources(factory);
//把设置好的Tomcat容器放进去,
customizeErrorReportValve(properties.getError(), factory);
}
}以上说明,其实使用ServerProperties类配置,也是通过实现
WebServerFactoryCustomizer
接口重写customize
来设置容器配置的。那么是在什么时候调用customize
这个方法进行设置呢?这就在类WebServerFactoryCustomizerBeanPostProcessor
中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//该类实现了BeanPostProcessor接口,Spring将在初始化bean前后对BeanPostProcessor实现类进行回调
public class WebServerFactoryCustomizerBeanPostProcessor
implements BeanPostProcessor, BeanFactoryAware {
//在Bean初始化之前调用
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
//如果bean是WebServerFactory类型的。就执行postProcessBeforeInitialization()方法
if (bean instanceof WebServerFactory) {
postProcessBeforeInitialization((WebServerFactory) bean);
}
return bean;
}
"unchecked") (
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
//Java8的Lambda表达式,大概的意识是,调用getCustomizers()方法获取所有的WebServerFactoryCustomizer实现类,然后执行里面的customize()方法。这样就在创建WebServerFactory实例前,先调用customize()方法,进行设置。
LambdaSafe
.callbacks(WebServerFactoryCustomizer.class, getCustomizers(),
webServerFactory)
.withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
.invoke((customizer) -> customizer.customize(webServerFactory));
}
//获取所有WebServerFactoryCustomizer实现类
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;
}
//获取所有WebServerFactoryCustomizer实现类
"unchecked", "rawtypes" }) ({
private Collection<WebServerFactoryCustomizer<?>> getWebServerFactoryCustomizerBeans() {
return (Collection) this.beanFactory
.getBeansOfType(WebServerFactoryCustomizer.class, false, false).values();
}
}总结下步骤:
- SpringBoot根据导入的依赖情况,给容器中添加相应的
WebServerFactoryCustomizer
,例如TomcatWebServerFactoryCustomizer
- 容器中某个组件要创建对象就会触发后置处理器;
WebServerFactoryCustomizerBeanPostProcessor
, 只要是WebServerFactory
类型的,都会触发 . WebServerFactoryCustomizerBeanPostProcessor
通过调用getCustomizers
()方法,获取所有实现了WebServerFactoryCustomizer
接口 的实例,执行其中的customize()方法。
- SpringBoot根据导入的依赖情况,给容器中添加相应的
三、如何注册三大组件(Servlet、Filter、Listener)
在Servlet、jsp时代,这三大组件是重中之重,在现在作用也不可忽视。之前的web项目在WEB-INF下面会有一个web.xml文件,用来配置这三大组件的名称、拦截路径等。但是在SpringBoot中已经没有了web.xml文件,那么我们该如何去注册呢?
-
Servlet
我们只需要创建个
ServletRegistrationBean
,并加入到容器中就行了。如下1
2
3
4
5
6
7
8
9
10
11
12
13
public class ServletConfig {
public ServletRegistrationBean servlet(){
//创建ServletRegistrationBean
ServletRegistrationBean<HttpServlet> servletRegistrationBean = new ServletRegistrationBean();
//设置Servlet。SayHelloServlet是我们自定义的一个Servlet。该类继承HTTPServlet
servletRegistrationBean.setServlet(new SayHelloServlet());
//设置url映射。当我们访问localhost:8080/sayHello 这个路径时,会执行SayHelloServlet中相应请求的方法。
servletRegistrationBean.setUrlMappings(Arrays.asList("/sayHello"));
return servletRegistrationBean;
}
} -
Filter
Filter也同理。只需要创建个
FilterRegistrationBean
并加入到容器中即可 。1
2
3
4
5
6
7
8
9
10
11
12
13
public class FilterConfig {
public FilterRegistrationBean servlet(){
//创建FilterRegistrationBean
FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean();
//设置Filter。MyFilter是我们自定义的一个Filter。该类继承Filter
filterRegistrationBean.setFilter(new MyFilter());
//设置url映射。当我们访问localhost:8080/sayHello 这个路径时,该请求会被MyFilter所拦截。
filterRegistrationBean.setUrlPatterns(Arrays.asList("/sayHello"));
return filterRegistrationBean;
}
} -
Listener
同上,只需要创建个
ServletListenerRegistrationBean
实例,并加入到Spring容器即可。1
2
3
4
5
public ServletListenerRegistrationBean myListener(){
ServletListenerRegistrationBean<MyListener> registrationBean = new ServletListenerRegistrationBean<>(new MyListener());
return registrationBean;
} -
SpringBoot在启动SpringMVC时,会自动帮我们把SpringMVC的前端控制器DispatcherServlet在
DispatcherServletAutoConfiguration
中注册好。源码如下: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(Ordered.HIGHEST_PRECEDENCE)
//只有是Web应用才会生效 (type = Type.SERVLET)
.class) //拥有DispatcherServlet这个类才会生效 (DispatcherServlet
@AutoConfigureAfter(ServletWebServerFactoryAutoConfiguration.class)
@EnableConfigurationProperties(ServerProperties.class)
public class DispatcherServletAutoConfiguration {
.class) (DispatcherServletRegistrationCondition
@ConditionalOnClass(ServletRegistration.class)
@EnableConfigurationProperties(WebMvcProperties.class)
@Import(DispatcherServletConfiguration.class)
protected static class DispatcherServletRegistrationConfiguration {
(name = DEFAULT_DISPATCHER_SERVLET_REGISTRATION_BEAN_NAME)
.class, name = DEFAULT_DISPATCHER_SERVLET_BEAN_NAME) (value = DispatcherServlet
//创建一个ServletRegistrationBean实例
public ServletRegistrationBean<DispatcherServlet> dispatcherServletRegistration(
DispatcherServlet dispatcherServlet) {
//创建ServletRegistrationBean,把dispatcherServlet这个类放进去,并绑定url映射
ServletRegistrationBean<DispatcherServlet> registration = new ServletRegistrationBean<>(
dispatcherServlet,
//url映射。默认为"/"
this.serverProperties.getServlet().getServletMapping());
registration.setName(DEFAULT_DISPATCHER_SERVLET_BEAN_NAME);
//设置优先级
registration.setLoadOnStartup(
this.webMvcProperties.getServlet().getLoadOnStartup());
if (this.multipartConfig != null) {
registration.setMultipartConfig(this.multipartConfig);
}
return registration;
}
}
}
-
四、替换其他web嵌入容器
从上面EmbeddedWebServerFactoryCustomizerAutoConfiguration
的分析可以看到,SpringBoot通过@ConditionalOnClass
的方式来判断该应用是使用何种嵌入式容器,从而生成不同的WebServerFactoryCustomizerConfiguration
进行配置。所以我们只需要修改pom.xml
文件,就可以达到切换web嵌入容器的目的了。
-
Tomcat
1
2
3
4
5<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
引入web模块默认就是使用嵌入式的Tomcat作为Servlet容器;
</dependency> -
Jetty
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<!-- 引入web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<!-- 排除默认的Tomcat容器 -->
<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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18<!-- 引入web模块 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<!-- 排除默认的Tomcat容器 -->
<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>
五、小结
-
如何修改SpringBoot中嵌入式容器的配置?
答:有两种方法,其实本质上只有一种。两种方法为:
- 修改yml配置文件 (推荐);
- 自定义实现
WebServerFactoryCustomizer
的类;
-
如何注册三大组件(Servlet、Filter、Listener)?
答:
- 在配置类中创建
ServletRegistrationBean
类,并加入到容器中,可向容器注册Servlet - 在配置类中创建
FilterRegistrationBean
类,并加入到容器中,可向容器注册Filter - 在配置类中创建
ServletListenerRegistrationBean
类,并加入到容器中,可向容器注册Listener
- 在配置类中创建
-
如何把默认的web容器替换成其他的内嵌的web容器?
答:修改pom依赖即可。
此次分析到此结束,谢谢大家的观看,如若有任何错误,希望大家可以在评论处给出。我会虚心学习。谢谢!