- 一、如何定制和修改Servlet容器的相关配置
- 二、注册Servlet三大组件【Servlet、Filter、Listener】
- 三、替换为其他嵌入式web服务器
- 四、嵌入式Servlet容器启动原理
- 五、使用外置的Servlet容器
跳转到目录
1、修改和server有关的配置server.port=8081
server.context-path=/crud
server.tomcat.uri-encoding=UTF-8
//通用的Servlet容器设置
server.xxx
//Tomcat的设置
server.tomcat.xxx
2、编写一个EmbeddedServletContainerCustomizer,2.0以后改为WebServerFactoryCustomizer
:嵌入式的Servlet容器的定制器;来修改Servlet容器的配置
@Configuration
public class MyServerConfig {
// 嵌入式Servlet的配置修改(还可以通过全局配置文件的方式修改)
@Bean
public WebServerFactoryCustomizer webServerFactoryCustomizer(){
return new WebServerFactoryCustomizer() {
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8083);
}
};
}
}
代码方式的配置会覆盖配置文件的配置
二、注册Servlet三大组件【Servlet、Filter、Listener】跳转到目录
- 由于SpringBoot默认是以
jar
包的方式启动嵌入式的Servlet容器来启动SpringBoot的web应用,没有web.xml文件。
- 向容器中添加
ServletRegistrationBean
MyServlet
public class MyServlet extends HttpServlet {
// 处理get请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("Hello MyServlet");
}
}
向容器中添加ServletRegistrationBean
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Bean
public ServletRegistrationBean myServlet() {
ServletRegistrationBean register = new ServletRegistrationBean(new MyServlet(), "/myServlet");
// 可以在这里设置
register.setLoadOnStartup(1);
return register;
}
......
2、Filter
- 向容器中添加
FilterRegistrationBean
MyFilter
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("MyFilter process...");
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
}
}
向容器中添加FilterRegistrationBean
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Bean
public FilterRegistrationBean myFilter() {
FilterRegistrationBean register = new FilterRegistrationBean(new MyFilter());
register.setUrlPatterns(Arrays.asList("/myServlet","/"));
return register;
}
......
3、Listener
- 向容器中添加
FilterRegistrationBean
MyListener
public class MyListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("MyListener.contextInitialized -- web应用启动");
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("MyListener.contextDestroyed -- web应用关闭");
}
}
向容器中注入ServletListenerRegistrationBean
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Bean
public ServletListenerRegistrationBean myServletContextListener(){
return new ServletListenerRegistrationBean(new MyServletContextListener());
}
......
三、替换为其他嵌入式web服务器
跳转到目录
- SpringBoot默认使用的是Tomcat
如果要换成其他的就把Tomcat的依赖排除掉,然后引入其他嵌入式Servlet容器的以来,如
Jetty
,Undertow
。
org.springframework.boot
spring-boot-starter-web
spring-boot-starter-tomcat
org.springframework.boot
spring-boot-starter-jetty
org.springframework.boot
org.springframework.boot
spring-boot-starter-web
spring-boot-starter-tomcat
org.springframework.boot
spring-boot-starter-undertow
org.springframework.boot
原理:
查看web容器自动配置类
2.0以下是:EmbeddedServletContainerAutoConfiguration
ServletWebServerFactoryAutoConfiguration
:嵌入式的web服务器自动配置
@Configuration(
proxyBeanMethods = false
)
@AutoConfigureOrder(-2147483648)
@ConditionalOnClass({ServletRequest.class})
@ConditionalOnWebApplication(
type = Type.SERVLET
)
@EnableConfigurationProperties({ServerProperties.class})
//---看这里---
@Import({ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class, EmbeddedTomcat.class, EmbeddedJetty.class, EmbeddedUndertow.class})
public class ServletWebServerFactoryAutoConfiguration {
EmbeddedTomcat.class
@Configuration(
proxyBeanMethods = false
)
//判断当前是否引入了Tomcat依赖;
@ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
/**
*判断当前容器没有用户自己定义ServletWebServerFactory:嵌入式的web服务器工厂;
*作用:创建嵌入式的web服务器
*/
@ConditionalOnMissingBean(
value = {ServletWebServerFactory.class},
search = SearchStrategy.CURRENT
)
public static class EmbeddedTomcat {
ServletWebServerFactory
:嵌入式的web服务器工厂
@FunctionalInterface
public interface ServletWebServerFactory {
//获取嵌入式的Servlet容器
WebServer getWebServer(ServletContextInitializer... initializers);
}
工厂实现类
WebServer
:嵌入式的web服务器实现 以
TomcatServletWebServerFactory
为例,下面是TomcatServletWebServerFactory类
public WebServer getWebServer(ServletContextInitializer... initializers) {
if (this.disableMBeanRegistry) {
Registry.disableRegistry();
}
//创建一个Tomcat
Tomcat tomcat = new Tomcat();
//配置Tomcat的基本环境,(tomcat的配置都是从本类获取的,tomcat.setXXX)
File baseDir = this.baseDirectory != null ? this.baseDirectory : this.createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
Connector connector = new Connector(this.protocol);
connector.setThrowOnFailure(true);
tomcat.getService().addConnector(connector);
this.customizeConnector(connector);
tomcat.setConnector(connector);
tomcat.getHost().setAutoDeploy(false);
this.configureEngine(tomcat.getEngine());
Iterator var5 = this.additionalTomcatConnectors.iterator();
while(var5.hasNext()) {
Connector additionalConnector = (Connector)var5.next();
tomcat.getService().addConnector(additionalConnector);
}
this.prepareContext(tomcat.getHost(), initializers);
//将配置好的Tomcat传入进去,返回一个WebServer;并且启动Tomcat服务器
return this.getTomcatWebServer(tomcat);
}
我们对嵌入式容器的配置修改是怎么生效的?
配置修改原理ServletWebServerFactoryAutoConfiguration
在向容器中添加web容器时还添加了一个组件
BeanPostProcessorsRegistrar
:后置处理器注册器(也是给容器注入一些组件)
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
public BeanPostProcessorsRegistrar() {...}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {...}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (this.beanFactory != null) {
//注册了下面两个组件
this.registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class);
this.registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class);
}
}
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class beanClass) {...}
}
webServerFactoryCustomizerBeanPostProcessor
public class WebServerFactoryCustomizerBeanPostProcessor implements BeanPostProcessor, BeanFactoryAware {
......
//在Bean初始化之前
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
//判断添加的Bean是不是WebServerFactory类型的
if (bean instanceof WebServerFactory) {
this.postProcessBeforeInitialization((WebServerFactory)bean);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
//获取所有的定制器,调用每一个定制器的customize方法来给Servlet容器进行属性赋值;
((Callbacks)LambdaSafe.callbacks(WebServerFactoryCustomizer.class, this.getCustomizers(), webServerFactory, new Object[0]).withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)).invoke((customizer) -> {
customizer.customize(webServerFactory);
});
}
关于配置文件是如何设置的,参考EmbeddedWebServerFactoryCustomizerAutoConfiguration
类,最后还是使用上面的方便
总结:
-
SpringBoot根据导入的依赖情况,给容器中添加相应的
XXXServletWebServerFactory
-
容器中某个组件要创建对象就会惊动后置处理器
webServerFactoryCustomizerBeanPostProcessor
只要是嵌入式的是Servlet容器工厂,后置处理器就会工作; -
后置处理器,从容器中获取所有的
WebServerFactoryCustomizer
,调用定制器的定制方法给工厂添加配置
跳转到目录 1、SpringBoot应用启动运行run方法 2、153行,创建IOC容器对象,根据当前环境创建
3、156行,刷新IOC容器
4、刷新IOC容器中272行,onRefresh()
;web的ioc容器重写了onRefresh方法,查看ServletWebServerApplicationContext类的onRefresh方法,在方法中调用了this.createWebServer();方法创建web容器
protected void onRefresh() {
super.onRefresh();
try {
this.createWebServer();
} catch (Throwable var2) {
throw new ApplicationContextException("Unable to start web server", var2);
}
}
98行获取嵌入式的web容器工厂
5、接下来就是上面的上面的相关配置流程,在创建web容器工厂时会触发webServerFactoryCustomizerBeanPostProcessor
6、然后99行使用容器工厂获取嵌入式的Servlet容器
7、嵌入式的Servlet容器创建对象并启动Servlet容器;
8、嵌入式的Servlet容器启动后,再将ioc容器中剩下没有创建出的对象获取出来(Controller,Service等);
使用外置的Servlet容器
五、使用外置的Servlet容器跳转到目录 1、将项目的打包方式改为war
2、编写一个类继承SpringBootServletInitializer
,并重写configure方法,调用参数的sources方法springboot启动类传过去然后返回
public class ServletInitializer extends SpringBootServletInitializer {
@Override
protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
return application.sources(HelloSpringBootWebApplication.class);
}
}
3、然后把tomcat的依赖范围改为provided
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter-tomcat
2.2.1.RELEASE
provided
......
4、最后就可以把项目打包成war放到tomcat中了
5、在IDEA中可以这样配置 6、在创建项目时使用Spring Initializr创建选择打包方式为war,1,2,3步骤会自动配置
如果启动tomcat,报了一大堆错误,不妨把Tomcat改为更高的版本试试,如果你项目中的Filter是继承了HttpFilter,请使用tomcat9版本,9以下好像没有HttpFilter
原理:1、Servlet3.0标准ServletContainerInitializer扫描所有jar包中METAINF/services/javax.servlet.ServletContainerInitializer文件指定的类并加载
2、还可以使用@HandlesTypes,在应用启动的时候加载我们感兴趣的类;
3、在spring-web-xxx.jar包中的METAINF/services下有javax.servlet.ServletContainerInitializer这个文件
文件中的类是:
org.springframework.web.SpringServletContainerInitializer
对应的类:
@HandlesTypes({WebApplicationInitializer.class})
public class SpringServletContainerInitializer implements ServletContainerInitializer {
public SpringServletContainerInitializer() {
}
public void onStartup(@Nullable Set webAppInitializerClasses, ServletContext servletContext) throws ServletException {
......
4、SpringServletContainerInitializer将@HandlesTypes(WebApplicationInitializer.class)标注的所有这个类型的类都传入到onStartup方法的Set
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?