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. MySQL-带你上官网看索引

    在我之前的一篇文章中,有引用一个讨论用Hash还是Tree的问题,DB中关于查找类数据结构,除了树,还有Hash(HashMap,HashSet). 存储数据结构之争 B+树主要是照顾磁盘IO这种特殊 ...

  2. 视频会议中的AEC、AGC、ANS是什么?

    视频会议中的AEC.AGC.ANS是什么? 1.AGC是自动增益补偿功能(Automatic Gain Control),AGC可以自动调麦克风的收音量,使与会者收到一定的音量水平,不会因发言者与麦克 ...

  3. 快速傅里叶变换FFT学习笔记

    点值表示法 我们正常表示一个多项式的方式,形如 \(A(x)=a_0+a_1x+a_2x^2+...+a_nx^n\),这是正常人容易看懂的,但是,我们还有一种表示法. 我们知道,\(n+1\)个点可 ...

  4. 揭秘Karmada百倍集群规模多云基础设施体系

    摘要:本文结合Karmada社区对大规模场景的思考,揭示Karmada稳定支持100个大规模集群.管理超过50万个节点和200万个Pod背后的原理 本文分享自华为云社区<Karmada百倍集群规 ...

  5. 2023-03-29:如何高效计算三条线路选择方案?小A的旅行线路规划问题

    2023-03-29:第一行有一个正整数n(3<=n<=100000),代表小A拟定的路线数量 第二行有n个正整数,第i个代表第i条路线的起始日期 第三行有n个正整数,第i个代表第i条路线 ...

  6. 2022-07-04:以下go语言代码输出什么?A:true;B:false;C:编译错误。 package main import “fmt“ func main() { fmt.Pri

    2022-07-04:以下go语言代码输出什么?A:true:B:false:C:编译错误. package main import "fmt" func main() { fmt ...

  7. 2021-05-01:给定一个有序数组arr,代表坐落在X轴上的点。给定一个正数K,代表绳子的长度。返回绳子最多压中几个点?即使绳子边缘处盖住点也算盖住。

    2021-05-01:给定一个有序数组arr,代表坐落在X轴上的点.给定一个正数K,代表绳子的长度.返回绳子最多压中几个点?即使绳子边缘处盖住点也算盖住. 福大大 答案2021-05-01: 滑动窗口 ...

  8. 2021-11-19:[0,4,7] : 0表示这里石头没有颜色,如果变红代价是4,如果变蓝代价是7,[1,X,X] : 1表示这里石头已经是红,而且不能改颜色,所以后两个数X无意义,[2,X,X]

    2021-11-19:[0,4,7] : 0表示这里石头没有颜色,如果变红代价是4,如果变蓝代价是7,[1,X,X] : 1表示这里石头已经是红,而且不能改颜色,所以后两个数X无意义,[2,X,X] ...

  9. c3p0的配置及简单应用

    首先简单了解一下JDBC和c3p0 Java数据库连接,(Java Database Connectivity,简称JDBC)是Java语言中用来规范客户端程序如何来访问数据库的应用程序接口,提供了诸 ...

  10. 【前端方案】-表格排序列LRU缓存方案

    目标: 排序后的表格列,页面刷新或者用户重新登录后,能够保持之前的操作排序 完成效果: 解决方案: 利用localstorage对排序后的表格列属性进行存储,页面刷新或者用户重新进入该页面时都先从lo ...