嵌入式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重構,這就不探究了。