SpringBoot里的Servlet和实现
Servlet
接口,一个规范,
SpringBoot
Spring Boot 是 Spring 的子项目,正如其名字,提供 Spring 的引导( Boot )的功能。
通过 Spring Boot ,开发者可以快速配置 Spring 项目,引入各种 Spring MVC、Spring Transaction、Spring AOP、等等框架,而无需不断重复编写繁重的 Spring 配置,降低了 Spring 的使用成本。
Servlet 和 SpringBoot
搭建一个 SpringBoot 项目:
引入依赖:
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-web</artifactid>
</dependency>
添加 Controller
@Controller
public class IndexController {
@RequestMapping("/hello")
public String hello(){
return "hello";
}
}
添加启动类
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
运行DemoApplication.main
即可启动一个简单的 Springboot web 项目。
项目是通过一个内嵌的Tomcat容器
来提供 web 服务。Tomcat 是 Servlet 容器,SpringBoot 想要使用 Servlet 容器就需要有 servlet 那么 SpringBoot 中有哪些 Servlet?是怎么通过这些 Servlet 来提供 web 服务的呢?
SpringBoot 中的 Servlet
SpringBoot 中涉及到的 Servlet 及它们的关系
HttpServletBean
主要方法:
设置一些配置信息,并提供子类初始化入口。无具体业务逻辑
@Override
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// Let subclasses do whatever initialization they like.
//增加子类实例化入口
initServletBean();
}
FrameworkServlet
FrameworkServlet 会实现
#doGet(HttpServletRequest request, HttpServletResponse response)
#doPost(HttpServletRequest request, HttpServletResponse response)
#doPut(HttpServletRequest request, HttpServletResponse response)
#doDelete(HttpServletRequest request, HttpServletResponse response)
#doOptions(HttpServletRequest request, HttpServletResponse response)
#doTrace(HttpServletRequest request, HttpServletResponse response)
#service(HttpServletRequest request, HttpServletResponse response)
等方法。而这些实现,最终会调用 #processRequest(HttpServletRequest request, HttpServletResponse response)
方法,处理请求。
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
HttpMethod httpMethod = HttpMethod.resolve(request.getMethod());
if (httpMethod == HttpMethod.PATCH || httpMethod == null) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
@Override
protected final void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
@Override
protected final void doDelete(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
processRequest(request, response);
}
...
处理请求:
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// <1> 记录当前时间,用于计算 web 请求的处理时间
long startTime = System.currentTimeMillis();
// <2> 记录异常
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
try {
// 执行真正的逻辑
doService(request, response);
} catch (ServletException | IOException ex) {
failureCause = ex; //
throw ex;
} catch (Throwable ex) {
failureCause = ex; //
throw new NestedServletException("Request processing failed", ex);
} finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
// 打印请求日志,并且日志级别为 DEBUG
logResult(request, response, failureCause, asyncManager);
// 发布 ServletRequestHandledEvent 事件
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
//抽象类给子类实现
protected abstract void doService(HttpServletRequest request, HttpServletResponse response)
throws Exception;
DispatcherServlet
doService
#doService(HttpServletRequest request, HttpServletResponse response)
方法,DispatcherServlet 的处理请求的入口方法,代码如下:
// DispatcherServlet.java
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
logRequest(request);
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<string, object=""> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<>();
Enumeration<!--?--> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith(DEFAULT_STRATEGIES_PREFIX)) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
// 设置 Spring 框架中的常用对象到 request 属性中
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
...
try {
// 执行请求的分发
doDispatch(request, response);
} finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
doDispatch
#doDispatch(HttpServletRequest request, HttpServletResponse response)
方法,执行请求的分发。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// <2> 映射处理器
//获得请求对应的 HandlerExecutionChain 对象
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) { // 如果获取不到,则根据配置抛出异常或返回 404 错误
noHandlerFound(processedRequest, response);
return;
}
// <3> 处理器适配器
// 获得当前 handler 对应的 HandlerAdapter 对象
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// last-modified
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 前置处理 拦截器
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// <4> 调用处理器方法
// 真正的调用 handler 方法,并返回视图
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 视图
applyDefaultViewName(processedRequest, mv);
// 后置处理 拦截器
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex; // 记录异常
} catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err); // <10> 记录异常
}
// 处理正常和异常的请求调用结果。
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
} catch (Exception ex) {
...
}
...
}
processDispatchResult
#processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception)
方法,处理正常和异常的请求调用结果。代码如下:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
@Nullable HandlerExecutionChain mappedHandler, @Nullable ModelAndView mv,
@Nullable Exception exception) throws Exception {
// 标记,是否是生成的 ModelAndView 对象
boolean errorView = false;
//如果是否异常的结果
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
} else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
if (mv != null && !mv.wasCleared()) {
// 渲染页面
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
} else {
if (logger.isTraceEnabled()) {
logger.trace("No view rendering, null ModelAndView returned.");
}
}
...
}
render
#render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response)
protected void render(ModelAndView mv, HttpServletRequest request, HttpServletResponse response) throws Exception {
// Determine locale for request and apply it to the response.
Locale locale =
(this.localeResolver != null ? this.localeResolver.resolveLocale(request) : request.getLocale());
response.setLocale(locale);
View view;
String viewName = mv.getViewName();
if (viewName != null) {
//<5> 解析视图
// 视图解析器ViewResolver
view = resolveViewName(viewName, mv.getModelInternal(), locale, request);
if (view == null) {
throw new ServletException("Could not resolve view with name '" + mv.getViewName() +
"' in servlet with name '" + getServletName() + "'");
}
}
else {
// No need to lookup: the ModelAndView object contains the actual View object.
view = mv.getView();
if (view == null) {
throw new ServletException("ModelAndView [" + mv + "] neither contains a view name nor a " +
"View object in servlet with name '" + getServletName() + "'");
}
}
// Delegate to the View object for rendering.
if (logger.isTraceEnabled()) {
logger.trace("Rendering view [" + view + "] ");
}
try {
if (mv.getStatus() != null) {
response.setStatus(mv.getStatus().value());
}
//<6> 渲染视图
//视图渲染
view.render(mv.getModelInternal(), request, response);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Error rendering view [" + view + "]", ex);
}
throw ex;
}
}
至此,对照图中的一个请求的处理流程在 DispatcherServlet 中的流转结束。
End
SpringBoot里的Servlet和实现的更多相关文章
- Spring-Boot自动装载servlet
Spring-Boot自动装载servlet 本人spring-boot相关博客均自己手动编写,但技术均从简书 恒宇少年 处学习,该大佬一直是我的偶像,鉴于能充分理解,所以已做笔记的方式留下这些文档, ...
- SpringBoot中使用Servlet,Filter,Listener
项目最近在替换之前陈旧的框架,改用SpringBoot进行重构,初接触,暂时还没有用到Servlet,Filter,Listener的地方,但在之前回顾Servlet的生命周期时,https://ww ...
- java框架之SpringBoot(8)-嵌入式Servlet容器
前言 SpringBoot 默认使用的嵌入式 Servlet 容器为 Tomcat,通过依赖关系就可以看到: 问题: 如何定制和修改 Servlet 容器相关配置? SpringBoot 能否支持其它 ...
- springboot外置的Servlet容器
嵌入式Servlet容器:应用打成可执行的jar 优点:简单.便携: 缺点:默认不支持JSP.优化定制比较复杂(使用定制器[ServerProperties.自定义EmbeddedServle ...
- springboot(八) 嵌入式Servlet容器自动配置原理和容器启动原理
1.嵌入式Servlet容器自动配置原理 1.1 在spring-boot-autoconfigure-1.5.9.RELEASE.jar => springboot自动配置依赖 jar包下,E ...
- SpringBoot学习笔记(6)----SpringBoot中使用Servlet,Filter,Listener的三种方式
在一般的运用开发中Controller已经大部分都能够实现了,但是也不排除需要自己实现Servlet,Filter,Listener的方式,SpringBoot提供了三种实现方式. 1. 使用Bean ...
- 小D课堂 - 零基础入门SpringBoot2.X到实战_第6节 SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener_24、深入SpringBoot过滤器和Servlet配置过滤器
笔记 1.深入SpringBoot2.x过滤器Filter和使用Servlet3.0配置自定义Filter实战(核心知识) 简介:讲解SpringBoot里面Filter讲解和使用Servle ...
- springboot(3):整合Servlet,filter,listener
1.springboot整合Servlet(2种方式) 添加maven依赖:spring-boot-starter-web 1>通过注解扫描完成Servlet组件的注册(方式1) 步骤:需要3步 ...
- springboot中使用servlet通过配置类
在servlet目录下创建个servlet类,示例代码如下: package com.bjpowernode.springboot.servlet; import javax.servlet.Serv ...
- SpringBoot动态注册Servlet
1.SpringBoot配置自定义监听器 实质上是在servlet3.0+的容器中,注册一个Servlet. 功能:监听对应的请求路径url-api @Slf4j @Configuration pub ...
随机推荐
- Nuget 多项目复合打包
问题描述 我这里有个进程间通信组件,用于提供多应用间通信的解决方案. 进程间通信,分为客户端和服务端,俩端肯定会有些共性代码,所以加了个H3C.Channel.Core项目 因为之前还不太了解nuge ...
- 2021牛客OI赛前集训营-提高组(第三场) 第二题 交替 题解与结论证明
题目描述 一个长度为 \(n\) 的数组\(A\),每秒都会变成一个长度为 \(n − 1\) 新数组 \(A'\),其变化规 则如下: 若当前数组 \(A\) 的长度 \(n\) 为偶数,则对于新数 ...
- Apache hudi 核心功能点分析
Hudi 文中部分代码对应 0.14.0 版本 发展背景 初始的需求是Uber公司会有很多记录级别的更新场景,Hudi 在Uber 内部主要的一个场景,就是乘客打车下单和司机接单的匹配,乘客和司机分别 ...
- Maven工程开发
Maven工程开发 编写pom.xml配置文件 <properties></properties>定义项目的基本信息 groupId 反向域名,定义项目组名 artifactI ...
- 数据结构与算法大作业:走迷宫程序(C语言,DFS)(代码以及思路)
好家伙,写大作业,本篇为代码的思路讲解 1.大作业要求 走迷宫程序 问题描述: 以一个 m * n 的长方阵表示迷宫, 0和1分别表示迷宫的通路和障碍. 设计一个程序, 对任意设定的迷宫, 求出一 ...
- pngquant 在 Windows 上压缩带中文路径的 png 图片
pngquant 是一个优秀的 png 压缩工具,但是在 Windows 上不支持目录中带有 unicode 字符(例如中文)的文件.所以要用一个折中的办法(即标准输入)让 pngquant 压缩目录 ...
- Java 网络编程 —— 非阻塞式编程
线程阻塞概述 在生活中,最常见的阻塞现象是公路上汽车的堵塞.汽车在公路上快速行驶,如果前方交通受阻,就只好停下来等待,等到公路顺畅,才能恢复行驶. 线程在运行中也会因为某些原因而阻塞.所有处于阻塞状态 ...
- 【GiraKoo】could not find UI helper 'git-credential-manager-ui'
环境 Windows 11 git version 2.39.0.windows.1 TortoiseGit 现象 使用TortoiseGit执行git pull命令时,提示could not fin ...
- es笔记四之中文分词插件安装与使用
本文首发于公众号:Hunter后端 原文链接:es笔记四之中文分词插件安装与使用 前面我们介绍的操作及演示都是基于英语单词的分词,但我们大部分使用的肯定都是中文,所以如果需要使用分词的操作肯定也是需要 ...
- postman接口关联-token值
背景: 在测试工作中,测试鉴权的接口需要用到登录接口的token,需要我们先调用登录接口,获得token,然后把即时获得的token填入请求中发送请求,我们可以用设置全局变量的办法解决这个问题 实 ...