SpringBoot内嵌Tomcat(9)- @WebServlet自定义Servlet加载

北城余情 提交于 2020-08-11 04:50:44

可以通过(都在javax.servlet.annotation包下)@WebServlet@WebFilter@WebListener 来配置相应的组件。

需要在启动SpringBootApplication类上增加注解:org.springframework.boot.web.servlet.ServletComponentScan
其引入的org.springframework.boot.web.servlet.ServletComponentScanRegistrar.addPostProcessor方法中加载了org.springframework.boot.web.servlet.ServletComponentRegisteringPostProcessor

通过org.springframework.boot.web.servlet.ServletComponentHandler的具体实现org.springframework.boot.web.servlet.WebServletHandler来处理

通过上图可看到:使用了org.springframework.boot.web.servlet.ServletRegistrationBean 来包装 javax.servlet.annotation.WebServlet

ps:
在org.apache.catalina.startup.Tomcat.addServlet方法中使用org.apache.catalina.startup.Tomcat.ExistingStandardWrapper 包装已经存在的Servlet时会判定是否有注解javax.servlet.annotation.WebServlet
调用入口是:
org.springframework.boot.web.embedded.tomcat.TomcatReactiveWebServerFactory.prepareContext方法

撸一个

标准使用异步Servlet必要条件:

  1. @WebServlet的属性 asyncSupported = true
  2. 开启异步上下文: HttpServletRequest.startAsync() 
  3. 业务逻辑内使用异步线程的方式触发回调 AsyncContext.complete()
import java.io.IOException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.servlet.AsyncContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import lombok.extern.slf4j.Slf4j;

@WebServlet(urlPatterns = "/async", asyncSupported = true)
@Slf4j
public class AsyncServlet extends HttpServlet {

	ExecutorService executorService = Executors.newSingleThreadExecutor();

	@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
		log.info("step into AsyncServlet");
		// step1: 开启异步上下文
		final AsyncContext ctx = req.startAsync();
		ctx.getResponse().getWriter().print("async servlet1");
		ctx.getResponse().getWriter().flush(); 
		resp.getWriter().print("async servlet2");
		resp.getWriter().flush();
		// 并不会立即输出。也是等到ctx.complete()完成 一并输出
		// step2: 提交线程池异步执行
		executorService.execute(() -> {
			try {
				log.info("async SocketEvent.OPEN_READ 准备执行了");
				// 模拟耗时
				Thread.sleep(1000L);
				ctx.getResponse().getWriter().print("async servlet3");
				log.info("async SocketEvent.OPEN_READ 执行了");
			} catch (Exception e) {
				e.printStackTrace();
			}
			// step3: 完成回调输出。
			ctx.complete();
		});
	}

}

Response输出都会延时到 ctx.complete()  执行完成后。

同步模式下, 在异步线程池里Response进行输出是无效的。

resp.getWriter().print("async servlet2");
resp.getWriter().flush();
// step2: 提交线程池异步执行
executorService.execute(() -> {
	try {
		log.info("async SocketEvent.OPEN_READ 准备执行了");
		// 模拟耗时
		Thread.sleep(10000L);
			resp.getWriter().print("async servlet3");

		log.info("async SocketEvent.OPEN_READ 执行了");
	} catch (Exception e) {
		e.printStackTrace();
	}
});

浏览器只会输出:

加载处理过程

对于org.springframework.web.servlet.DispatcherServlet的包装使用的是org.springframework.boot.autoconfigure.web.servlet.DispatcherServletRegistrationBean

执行堆栈如下图:

启动Tomcat过程中执行到org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.selfInitialize方法体内:


首先getServletContextInitializerBeans():
 org.springframework.boot.web.servlet.ServletContextInitializerBeans.addServletContextInitializerBean方法归集了ServletRegistrationBean (DispatcherServlet的处理也是如此)


接着回到主方法执行ServletContextInitializer.onStartup:   将Servlet注册到ServletContext中


执行到org.springframework.boot.web.servlet.RegistrationBean.onStartup -> org.springframework.boot.web.servlet.DynamicRegistrationBean.register里:
(这里实际对象是ServletRegistrationBean)

第一步

创建org.apache.catalina.core.ApplicationServletRegistration对象

ServletRegistrationBean.addRegistration :

-> org.apache.catalina.core.ApplicationContext.addServlet: 在这里创建了StandardWrapper, 其内部asyncSupported默认是false.

第二步

设置属性 
ServletRegistrationBean.configure

-> org.springframework.boot.web.servlet.DynamicRegistrationBean.configure 
这里取的就是ServletRegistrationBean在实例化时的asyncSupported属性值 (文章开头讲述的BeanDefinition构建过程时填入)

-> org.apache.catalina.core.ApplicationServletRegistration.setAsyncSupported
-> 最终执行到了org.apache.catalina.core.StandardWrapper.setAsyncSupported

urlMapping的设置

在 ServletRegistrationBean.configure -> ApplicationServletRegistration.addMapping方法实现里:

->org.apache.catalina.core.StandardContext.addServletMappingDecoded
StandardContext里保存了一份<url, wrapper> 的映射

-> org.apache.catalina.core.StandardWrapper.addMapping
而后StandardWrapper里自己也保存这个Url

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!