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和实现的更多相关文章

  1. Spring-Boot自动装载servlet

    Spring-Boot自动装载servlet 本人spring-boot相关博客均自己手动编写,但技术均从简书 恒宇少年 处学习,该大佬一直是我的偶像,鉴于能充分理解,所以已做笔记的方式留下这些文档, ...

  2. SpringBoot中使用Servlet,Filter,Listener

    项目最近在替换之前陈旧的框架,改用SpringBoot进行重构,初接触,暂时还没有用到Servlet,Filter,Listener的地方,但在之前回顾Servlet的生命周期时,https://ww ...

  3. java框架之SpringBoot(8)-嵌入式Servlet容器

    前言 SpringBoot 默认使用的嵌入式 Servlet 容器为 Tomcat,通过依赖关系就可以看到: 问题: 如何定制和修改 Servlet 容器相关配置? SpringBoot 能否支持其它 ...

  4. springboot外置的Servlet容器

    嵌入式Servlet容器:应用打成可执行的jar ​ 优点:简单.便携: ​ 缺点:默认不支持JSP.优化定制比较复杂(使用定制器[ServerProperties.自定义EmbeddedServle ...

  5. springboot(八) 嵌入式Servlet容器自动配置原理和容器启动原理

    1.嵌入式Servlet容器自动配置原理 1.1 在spring-boot-autoconfigure-1.5.9.RELEASE.jar => springboot自动配置依赖 jar包下,E ...

  6. SpringBoot学习笔记(6)----SpringBoot中使用Servlet,Filter,Listener的三种方式

    在一般的运用开发中Controller已经大部分都能够实现了,但是也不排除需要自己实现Servlet,Filter,Listener的方式,SpringBoot提供了三种实现方式. 1. 使用Bean ...

  7. 小D课堂 - 零基础入门SpringBoot2.X到实战_第6节 SpringBoot拦截器实战和 Servlet3.0自定义Filter、Listener_24、深入SpringBoot过滤器和Servlet配置过滤器

    笔记 1.深入SpringBoot2.x过滤器Filter和使用Servlet3.0配置自定义Filter实战(核心知识)     简介:讲解SpringBoot里面Filter讲解和使用Servle ...

  8. springboot(3):整合Servlet,filter,listener

    1.springboot整合Servlet(2种方式) 添加maven依赖:spring-boot-starter-web 1>通过注解扫描完成Servlet组件的注册(方式1) 步骤:需要3步 ...

  9. springboot中使用servlet通过配置类

    在servlet目录下创建个servlet类,示例代码如下: package com.bjpowernode.springboot.servlet; import javax.servlet.Serv ...

  10. SpringBoot动态注册Servlet

    1.SpringBoot配置自定义监听器 实质上是在servlet3.0+的容器中,注册一个Servlet. 功能:监听对应的请求路径url-api @Slf4j @Configuration pub ...

随机推荐

  1. mosn基于延迟负载均衡算法 -- 走得更快,期待走得更稳

    前言 这篇文章主要是介绍mosn在v1.5.0中新引入的基于延迟的负载均衡算法. 对分布式系统中延迟出现的原因进行剖析 介绍mosn都通过哪些方法来降低延迟 构建来与生产环境性能分布相近的测试用例来对 ...

  2. MySQL百万数据深度分页优化思路分析

    业务场景 一般在项目开发中会有很多的统计数据需要进行上报分析,一般在分析过后会在后台展示出来给运营和产品进行分页查看,最常见的一种就是根据日期进行筛选.这种统计数据随着时间的推移数据量会慢慢的变大,达 ...

  3. Centos环境下部分中间件“rabbitmq、rocketmq、clickhouse”部署

    部分中间件部署 目录 部分中间件部署 docker部署rabbitmq docker部署rocketmq 单机部署clickhouse docker部署rabbitmq # 拉镜像 docker pu ...

  4. 代码打包的可视化数据分析图: webpack-bundle-analyzer 的使用

    先看webpack-bundle-analyzer的效果图(官方效果图): 通过使用webpack-bundle-analyzer可以看到项目各模块的大小,可以按需优化 1.先安装 npm insta ...

  5. 2023-05-05:给定一个无向、连通的树 树中有 n 个标记为 0...n-1 的节点以及 n-1 条边 。 给定整数 n 和数组 edges , edges[i] = [ai, bi]表示树中的

    2023-05-05:给定一个无向.连通的树 树中有 n 个标记为 0...n-1 的节点以及 n-1 条边 . 给定整数 n 和数组 edges , edges[i] = [ai, bi]表示树中的 ...

  6. 2022-06-25:给定一个正数n, 表示有0~n-1号任务, 给定一个长度为n的数组time,time[i]表示i号任务做完的时间, 给定一个二维数组matrix, matrix[j] = {a,

    2022-06-25:给定一个正数n, 表示有0~n-1号任务, 给定一个长度为n的数组time,time[i]表示i号任务做完的时间, 给定一个二维数组matrix, matrix[j] = {a, ...

  7. OneForAll下载安装以及环境配置

    python-3.9.7-amd64 OneForAll-master python安装以及插件安装 首先下载python解压到电脑c盘在c盘中创建一个工具文件夹,然后下载OneForAll-mast ...

  8. 有关 python 切片的趣事

    哈喽大家好,我是咸鱼 今天来讲一个我在实现 python 列表切片时遇到的趣事 在正式开始之前,我们先来了解一下切片(slice) 切片操作是访问序列(列表.字符串......)中元素的另一种方法,它 ...

  9. CentOS7 配置本地yum源软件仓库

    CentOS7 配置本地yum源软件仓库 前言 配置本地yum源软件仓库可以离线状态下安装本地已有的软件 先连接虚拟光驱,对应的光驱iso文件 查看磁盘分区状态 可以看到sr0 未挂载 [root@l ...

  10. 【leetcode】#647 回文子串 Rust Solution

    给定一个字符串,你的任务是计算这个字符串中有多少个回文子串.具有不同开始位置或结束位置的子串,即使是由相同的字符组成,也会被视作不同的子串. 示例 1:输入:"abc"输出:3解释 ...