嵌入式servlet配置修改
SpringBoot默认使用Tomcat作为嵌入式的Servlet容器
嵌入式tomcat:tomca-embed-core
问题?
1)、如何定制和修改Servlet容器相关配置;
1.修改和server有关的配置:(ServerProperties.class)
server.port=8081
server.servlet.context-path=/ogj
server.tomcat.uri-encoding=UTF-8
//通用的Server配置
server.xxx
//Tomcat的设置
server.tomcat.xxx
前几个版本还可以自定义接口设置,但是在springboot2.x以后取消了该接口,所有Servlet配置在配置文件中配置。
对于SpringBoot中可以用xxxCustomize进行定制配置。
2. 注册Servlet三大组件【Servlet、Filter、Listener】
由于SpringBoot默认是以jar包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件。
所以SpringBoot使用三大组件来配置web:Servlet、Filter、Listener**
注册三大组件的方式:
1、ServletRegistrationBean
定义servlet
public class MyServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-----------doGet----------------");
//此处不能访问父方法,否则会405
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("-----------doPost----------------");
resp.getWriter().write("Hello MyServlet");
}
}
注册servlet:
@Bean
public ServletRegistrationBean myServlet(){
ServletRegistrationBean registrationBean =
new ServletRegistrationBean(new MyServlet(), "/myServlet");
return registrationBean;
}
2、FilterRegistrationBean
定义Filter
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("-----Filter-----");
filterChain.doFilter(servletRequest,servletResponse);
}
}
注册Filter
//注册Filter 拦截URL
@Bean
public FilterRegistrationBean myFilter(){
FilterRegistrationBean registrationBean = new FilterRegistrationBean();
registrationBean.setFilter(new MyFilter());
registrationBean.setUrlPatterns(Arrays.asList("/hello","/myServlet"));
return registrationBean;
}
3、ServletListenerRegistrationBean
定义Listener:
public class Mylistener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("contextInitialized...web应用启动");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("contextDestroy...web项目销毁");
}
}
注册Listener:
//注册Listener 监听器
@Bean
public ServletListenerRegistrationBean<Mylistener> myListener(){
ServletListenerRegistrationBean<Mylistener> registrationBean
= new ServletListenerRegistrationBean<>(new Mylistener());
return registrationBean;
}
SpringBoot帮助我们自动SpringMVC的时候,自动的注册SpringMVC的前端控制器:DispatcherServlet;
配置类:DispatcherServletAutoConfiguration
ctrl+n查找该类源码可得自动注册方法:dispatcherServletRegistration
@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;
}
-
默认拦截: / 所有请求; 包括静态资源,但是不拦截jsp请求, /* 会拦截jsp
-
可以通过server.servletpath来修改SpringMVC前端控制器拦截的请求路径
2)、SpringBoot能不能支持其他的Servlet容器;
3)、替换为其他嵌入式Servlet容器
默认支持:Tomcat、Jetty、Undertow,新版本SpringBoot中还添加了Netty新机制容器;默认使用tomcat
<!--引入web模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--引入web模块的时候就是默认使用的tomcat作为Servlet容器-->
切换方式:
1、在pow中排除tomcat的依赖;
2、添加jetty(Undertow)的依赖; Undertow不支持jsp组件;
<!--引入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>
<!--配置其他Servlet容器-->
<dependency>
<artifactId>spring-boot-starter-jetty</artifactId>
<groupId>org.springframework.boot</groupId>
</dependency>
4)、嵌入式Servlet容器自动配置原理:
新版本SpringBoot2.x在该类中进行自动配置:EmbeddedWebServerFactoryCustomizerAutoConfiguration
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnWebApplication
@EnableConfigurationProperties({ServerProperties.class})
public class EmbeddedWebServerFactoryCustomizerAutoConfiguration {
//配置NettyWebServer
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({HttpServer.class})
public static class NettyWebServerFactoryCustomizerConfiguration {
public NettyWebServerFactoryCustomizerConfiguration() {
}
@Bean
public NettyWebServerFactoryCustomizer nettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new NettyWebServerFactoryCustomizer(environment, serverProperties);
}
}
//配置Undertow
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Undertow.class, SslClientAuthMode.class})
public static class UndertowWebServerFactoryCustomizerConfiguration {
public UndertowWebServerFactoryCustomizerConfiguration() {
}
@Bean
public UndertowWebServerFactoryCustomizer undertowWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new UndertowWebServerFactoryCustomizer(environment, serverProperties);
}
}
//配置jetty
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Server.class, Loader.class, WebAppContext.class})
public static class JettyWebServerFactoryCustomizerConfiguration {
public JettyWebServerFactoryCustomizerConfiguration() {
}
@Bean
public JettyWebServerFactoryCustomizer jettyWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new JettyWebServerFactoryCustomizer(environment, serverProperties);
}
}
//配置Tomcat
@Configuration(
proxyBeanMethods = false
)
@ConditionalOnClass({Tomcat.class, UpgradeProtocol.class})
public static class TomcatWebServerFactoryCustomizerConfiguration {
public TomcatWebServerFactoryCustomizerConfiguration() {
}
@Bean
public TomcatWebServerFactoryCustomizer tomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
return new TomcatWebServerFactoryCustomizer(environment, serverProperties);
}
}
}
新版本的SpringBoot2.x使用的自定义配置已经不是传入容器了,而是传入xxWebServerFactoryCustomizer,作为自定义Servlet容器配置。
我们还可以看到以下几个xxWebServletFactoryCustomizer配置类
Tomcat配置过程(该代码位于源码:TomcatWebServerFactoryCustomizer):
public TomcatWebServerFactoryCustomizer(Environment environment, ServerProperties serverProperties) {
this.environment = environment;
this.serverProperties = serverProperties;
}
//customer的自动配置
public void customize(ConfigurableTomcatWebServerFactory factory) {
ServerProperties properties = this.serverProperties;
Tomcat tomcatProperties = properties.getTomcat();
PropertyMapper propertyMapper = PropertyMapper.get();
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getBasedir).whenNonNull().to(factory::setBaseDirectory);
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getBackgroundProcessorDelay).whenNonNull().as(Duration::getSeconds).as(Long::intValue).to(factory::setBackgroundProcessorDelay);
this.customizeRemoteIpValve(factory);
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getMaxThreads).when(this::isPositive).to((maxThreads) -> {
this.customizeMaxThreads(factory, tomcatProperties.getMaxThreads());
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getMinSpareThreads).when(this::isPositive).to((minSpareThreads) -> {
this.customizeMinThreads(factory, minSpareThreads);
});
propertyMapper.from(this.serverProperties.getMaxHttpHeaderSize()).whenNonNull().asInt(DataSize::toBytes).when(this::isPositive).to((maxHttpHeaderSize) -> {
this.customizeMaxHttpHeaderSize(factory, maxHttpHeaderSize);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getMaxSwallowSize).whenNonNull().asInt(DataSize::toBytes).to((maxSwallowSize) -> {
this.customizeMaxSwallowSize(factory, maxSwallowSize);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getMaxHttpFormPostSize).asInt(DataSize::toBytes).when((maxHttpFormPostSize) -> {
return maxHttpFormPostSize != 0;
}).to((maxHttpFormPostSize) -> {
this.customizeMaxHttpFormPostSize(factory, maxHttpFormPostSize);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getAccesslog).when(Accesslog::isEnabled).to((enabled) -> {
this.customizeAccessLog(factory);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getUriEncoding).whenNonNull().to(factory::setUriEncoding);
properties.getClass();
propertyMapper.from(properties::getConnectionTimeout).whenNonNull().to((connectionTimeout) -> {
this.customizeConnectionTimeout(factory, connectionTimeout);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getConnectionTimeout).whenNonNull().to((connectionTimeout) -> {
this.customizeConnectionTimeout(factory, connectionTimeout);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getMaxConnections).when(this::isPositive).to((maxConnections) -> {
this.customizeMaxConnections(factory, maxConnections);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getAcceptCount).when(this::isPositive).to((acceptCount) -> {
this.customizeAcceptCount(factory, acceptCount);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getProcessorCache).to((processorCache) -> {
this.customizeProcessorCache(factory, processorCache);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getRelaxedPathChars).as(this::joinCharacters).whenHasText().to((relaxedChars) -> {
this.customizeRelaxedPathChars(factory, relaxedChars);
});
tomcatProperties.getClass();
propertyMapper.from(tomcatProperties::getRelaxedQueryChars).as(this::joinCharacters).whenHasText().to((relaxedChars) -> {
this.customizeRelaxedQueryChars(factory, relaxedChars);
});
this.customizeStaticResources(factory);
this.customizeErrorReportValve(properties.getError(), factory);
}
由于Servlet加载和配置原理在SpringBoot2.X重构,这就不探究了。