可以通过(都在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必要条件:
- @WebServlet的属性 asyncSupported = true
- 开启异步上下文: HttpServletRequest.startAsync()
- 业务逻辑内使用异步线程的方式触发回调: 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
来源:oschina
链接:https://my.oschina.net/u/3434392/blog/4286123