从 Servlet 到 DispatcherServlet(SpringMvc 容器的创建)

DispatcherServlet 的继承体系

SpringMvc 是一个具有 Spring 容器(ApplicationContext)的 Servlet。其中,HttpServlet 属于 JDK 的内容,从 HttpServletBean 开始,便属于 Spring 体系中的内容。

  • HttpServletBean:XXXBean 是 Spring 框架和其它框架整合时使用,这里将 JDK 中的 Servlet 作为一个框架。
  • Aware 接口:由 Spring 的 BeanPostProcessor 进行调用,通过 setXXX()为 Spring 容器中的 Bean 组件提供功能。例如,当组件希望能够获得整个 Spring 容器时,可以实现 ApplicationContextAware 接口。
    • ApplicationContextAware:告诉 Spring,需要 ApplicationContext 容器
    • EnvironmentAware:告诉 Spring,需要 Environment(包括配置文件和环境变量)
  • Capable 接口:告诉 Spring,可以提供什么东西。如果 Spring 需要这个东西,会通过 getXXX()来获取。
    • EnvironmentCapable:告诉 Spring,可以提供 Environment 配置信息

DispatcherServlet 的继承体系

HttpServletBean 源码解析

重写 GenericServlet 的 init()方法,并提供 initServletBean()方法作为新的扩展点,以取代 GenericServlet 提供的 init()扩展点。
注意:init()方法使用了 final 关键字进行修饰,因此子类无法重载 init()方法。这样便从 Servlet 的规范向 Spring 的规范靠近。

init 流程

public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {// 重写GenericServlet中的无参init()方法@Overridepublic final void init() throws ServletException {// 从ServletConfig到PropertyValuesPropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);if (!pvs.isEmpty()) {try {// this实际会代表DispatcherServlet// BeanWrapper用来操作DispatcherServlet中的属性BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));initBeanWrapper(bw);// 将PropertyValues设置到DispatchServlet中bw.setPropertyValues(pvs, true);}catch (BeansException ex) {if (logger.isErrorEnabled()) {logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);}throw ex;}}// 提供给子类的扩展点,子类可以接着已有的逻辑进行扩展,// 而不用去重写init()方法,然后把这段内容拷贝过去initServletBean();}protected void initServletBean() throws ServletException {}
}

内部类 ServletConfigPropertyValues

键值对(key-value)在不同的框架中由不同的对象进行封装,在 Servlet 中保存在 ServletConfig 的 initParameter 中,在 Spring 中封装成对象 PropertyValue。
ServletConfigPropertyValues 这个类的作用便是将保存在 ServletConfig 中的配置项信息添加到 PropertyValues 中,并检查这些 initParameter 是否满足 requiredProperties 集合的全部要求,如果不满足则抛出异常。

private static class ServletConfigPropertyValues extends MutablePropertyValues {public ServletConfigPropertyValues(ServletConfig config, Set<String> requiredProperties) throws ServletException {Set<String> missingProps = CollectionUtils.isEmpty(requiredProperties) ?null : new HashSet<>(requiredProperties);Enumeration<String> paramNames = config.getInitParameterNames();while (paramNames.hasMoreElements()) {String property = paramNames.nextElement();Object value = config.getInitParameter(property);addPropertyValue(new PropertyValue(property, value));if (missingProps != null) {missingProps.remove(property);}}// Fail if we are still missing properties.if (!CollectionUtils.isEmpty(missingProps)) {// throw ... }
}

BeanWrapper 是什么?怎么用?

BeanWrapper 是 Spring 提供的用来操作 JavaBean 属性的工具,使用 BeanWrapper 可以直接修改一个对象的属性,例如

public class BeanWrapperDemo {public static void main(String[] args) {User user = new User(1, "root", 18);System.out.println("user = " + user);BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(user);// 获取 JavaBean 中的属性值,借助 Spring 的类型系统进行类型强转?Integer userId = (Integer) bw.getPropertyValue("userId");System.out.println("userId = " + userId);String nickname = (String) bw.getPropertyValue("nickname");System.out.println("nickname = " + nickname);HashMap<String, Object> map = new HashMap<>();map.put("userId", 10);map.put("nickname", "admin");// 故意添加一个不匹配的字段map.put("name", "error");PropertyValues propertyValues = new MutablePropertyValues(map);// User对象并没有name属性,如果不添加第二个参数,那么会报错bw.setPropertyValues(propertyValues, true);System.out.println("user = " + user);}
}@Data
@NoArgsConstructor
@AllArgsConstructor
class User {private Integer userId;private String nickname;private Integer age;
}

FrameworkServlet 源码解析

从父类 HttpServletBean 的 init()可以看出,子类的 init()无法重写,因此方法扩展点就是 HttpServletBean 中的 initServletBean()

public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {public static final String DEFAULT_NAMESPACE_SUFFIX = "-servlet";public static final String SERVLET_CONTEXT_PREFIX = FrameworkServlet.class.getName() + ".CONTEXT.";private static final String INIT_PARAM_DELIMITERS = ",; \t\n";@Overrideprotected final void initServletBean() throws ServletException {// 核心,初始化SpringMvc容器this.webApplicationContext = initWebApplicationContext();// 留给子类的扩展点initFrameworkServlet();}protected WebApplicationContext initWebApplicationContext() {// 获取Spring根容器// 在前面某个时间点,会将WebApplicationContext对象设置到ServletContext的属性中(attributes)// 以 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 为key,所以这里从 ServletContext 中取值即可WebApplicationContext rootContext =WebApplicationContextUtils.getWebApplicationContext(getServletContext());WebApplicationContext wac = null;if (this.webApplicationContext != null) {wac = this.webApplicationContext;if (wac instanceof ConfigurableWebApplicationContext) {ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;if (!cwac.isActive()) {if (cwac.getParent() == null) {cwac.setParent(rootContext);}configureAndRefreshWebApplicationContext(cwac);}}}if (wac == null) {wac = findWebApplicationContext();}if (wac == null) {// 一般情况下会进入到这里来创建wac = createWebApplicationContext(rootContext);}if (!this.refreshEventReceived) {// 当ContextRefreshedEvent事件没有触发时触发onRefresh方法,// 上面对wac赋值的三种情况中,只有第二种情况会进入到该方法中synchronized (this.onRefreshMonitor) {onRefresh(wac);}}if (this.publishContext) {// 将ApplicationContext保存到ServletContext中String attrName = getServletContextAttributeName();getServletContext().setAttribute(attrName, wac);}return wac;}protected WebApplicationContext createWebApplicationContext(@Nullable WebApplicationContext parent) {//要调用重载的其它方法,这里只能强转(重载是编译时多态)return createWebApplicationContext((ApplicationContext) parent);}protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {Class<?> contextClass = getContextClass();if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {// throw ...}ConfigurableWebApplicationContext wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());wac.setParent(parent);String configLocation = getContextConfigLocation();if (configLocation != null) {wac.setConfigLocation(configLocation);}configureAndRefreshWebApplicationContext(wac);return wac;}
}

WebApplicationContext 接口

public interface WebApplicationContext extends ApplicationContext {String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";String SCOPE_REQUEST = "request";String SCOPE_SESSION = "session";String SCOPE_APPLICATION = "application";String SERVLET_CONTEXT_BEAN_NAME = "servletContext";String CONTEXT_PARAMETERS_BEAN_NAME = "contextParameters";String CONTEXT_ATTRIBUTES_BEAN_NAME = "contextAttributes";@NullableServletContext getServletContext();
}

ConfigurableWebApplicationContext 接口

  • ServletContext
  • ServletConfig
  • Namespace
  • ConfigLocations
public interface ConfigurableWebApplicationContext extends WebApplicationContext, ConfigurableApplicationContext {String APPLICATION_CONTEXT_ID_PREFIX = WebApplicationContext.class.getName() + ":";String SERVLET_CONFIG_BEAN_NAME = "servletConfig";// WebApplicationContext接口中有getServletContext()方法void setServletContext(@Nullable ServletContext servletContext);void setServletConfig(@Nullable ServletConfig servletConfig);ServletConfig getServletConfig();void setNamespace(@Nullable String namespace);String getNamespace();void setConfigLocation(String configLocation);void setConfigLocations(String... configLocations);String[] getConfigLocations();
}

DispatcherServlet 源码解析

按照和前面相同的分析,DispatcherServlet 应该使用 initFrameworkServlet()来进行初始化方法的扩展,但是实际却并没有使用,使用了 onRefresh()进行扩展。(为什么这么设计?)

public class DispatcherServlet extends FrameworkServlet {@Overrideprotected void onRefresh(ApplicationContext context) {initStrategies(context);}protected void initStrategies(ApplicationContext context) {initMultipartResolver(context);initLocaleResolver(context);initThemeResolver(context);initHandlerMappings(context);initHandlerAdapters(context);initHandlerExceptionResolvers(context);initRequestToViewNameTranslator(context);initViewResolvers(context);initFlashMapManager(context);}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/323458.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

unity制作app(5)--发送数据给数据库

这个之前做过&#xff0c;先不做照片的。下一节再做带照片的。 第一步 收集数据 1.先做一个AppModel结构体&#xff0c;这个结构体需要单做的。 using System; using System.Collections.Generic; using System.Linq; using System.Text; //using Assets.Model; public clas…

Unity 性能优化之GPU Instancing(五)

提示&#xff1a;仅供参考&#xff0c;有误之处&#xff0c;麻烦大佬指出&#xff0c;不胜感激&#xff01; 文章目录 前言一、GPU Instancing使用方法二、使用GPU Instancing的条件三、GPU Instancing弊端四、注意五、检查是否成功总结 前言 GPU Instancing也是一种Draw call…

探索C++的string:从基础到深入

文章目录 string类string类的接口string的常见构造string类对象的容量操作string类的遍历及访问操作string类对象的修改操作string类的非成员函数 总结 string类 C中的string类是一个非常重要的字符串处理工具&#xff0c;它提供了一种方便且灵活的方式来处理字符串。它位于标…

影响视频视觉质量的因素——各类视觉伪影

模糊效应&#xff08;Blurring Artifact&#xff09; 图像模糊&#xff08;blurring&#xff09;&#xff1a;平滑图像的细节和边缘产生的现象&#xff0c;模糊对于图像来说&#xff0c;是一个低通滤波器&#xff08;low-pass filter&#xff09;。一般而言&#xff0c;用户更…

炒美股怎么开户?

近年来&#xff0c;随着国内投资者对境外投资需求的不断增长&#xff0c;炒美股逐渐成为许多投资者的选择。然而&#xff0c;随着监管政策的不断完善&#xff0c;传统的互联网券商开户方式已经不再适用。那么&#xff0c;对于想要入场美股市场的投资者来说&#xff0c;该如何开…

2. Linux 基本指令(上)|ls|pwd|cd|tree|touch|mkdir|rmdir|rm

前言 计算机软硬件体系结构 层状结构应用软件Word&#xff0c;Matlab操作系统Windows&#xff0c;Linux设备驱动声卡驱动硬件CPU&#xff0c;内存&#xff0c;磁盘&#xff0c;显示器&#xff0c;键盘 操作系统概念 操作系统 是一款进行软硬件资源管理的软件 例子 比如在学…

vue2实现右键菜单功能——vue-diy-rightmenu——基础积累

五一之前遇到一个需求&#xff0c;就是关于要实现自定义右键菜单的功能&#xff0c;普通的右键展示的菜单有【返回/前进/重新加载/另存为】等&#xff0c;希望实现的效果就是右键出现自定义的菜单&#xff0c;比如【编辑/删除/新增】等。 遇到这种的需求&#xff0c;可以直接去…

光伏设备制造5G智能工厂数字孪生可视化平台,推进行业数字化转型

光伏设备制造5G智能工厂数字孪生可视化平台&#xff0c;推进行业数字化转型。光伏设备制造5G智能工厂数字孪生可视化平台是光伏行业数字化转型的重要一环。通过数字孪生平台&#xff0c;光伏设备制造企业可以实现对生产过程的全面监控和智能管理&#xff0c;提高生产效率&#…

C++ | Leetcode C++题解之第77题组合

题目&#xff1a; 题解&#xff1a; class Solution { public:vector<int> temp;vector<vector<int>> ans;vector<vector<int>> combine(int n, int k) {// 初始化// 将 temp 中 [0, k - 1] 每个位置 i 设置为 i 1&#xff0c;即 [0, k - 1] 存…

软件测试行业的变革与自我成长

随着科技的不断进步和市场的快速变化&#xff0c;软件测试行业也迎来了前所未有的变革。近期&#xff0c;一些大型互联网公司如阿里Lazada、字节跳动等纷纷宣布裁员计划&#xff0c;让不少软件测试从业者感受到了前所未有的压力与焦虑。面对这样的行业现状&#xff0c;我们不禁…

1707jsp电影视频网站系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 JSP 校园商城派送系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统采用web模式&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5开发&#xff0c;数…

Web安全研究(七)

NDSS 2023 开源地址&#xff1a;https://github.com/bfpmeasurementgithub/browser-fingeprint-measurement 霍普金斯大学 文章结构 introbackground threat model measurement methodology step1: traffic analysisstep2: fingerprint analysis dataset attack statisticsbro…

当CV遇上transformer(二)MAE模型及源码分析

当CV遇上transformer(二)MAE模型 2020年10月&#xff0c;Dosovitskiy首次将纯Transformer的网络结构应用于图像分类任务中(ViT)&#xff0c;并取得了当时最优的分类效果&#xff0c;其研究成果是Transformer完全替代标准卷积的首次尝试。大神何恺明在2021年11月基于(ViT)架构&…

单单单单单の刁队列

在数据结构的学习中&#xff0c;队列是一种常用的线性数据结构&#xff0c;它遵循先进先出&#xff08;FIFO&#xff09;的原则。而单调队列是队列的一种变体&#xff0c;它在特定条件下保证了队列中的元素具有某种单调性质&#xff0c;例如单调递增或单调递减。单调队列在处理…

AWS Lambda 第一个例子Hello (JAVA)

什么是Serverless&#xff08;无服务器计算&#xff09; 行业通常所说的Serverless&#xff0c;主要是指“无服务器计算&#xff08;Serverless Computing&#xff09;”。无服务器计算&#xff0c;并不是真的不需要服务器&#xff0c;而是说&#xff0c;对于用户&#xff0c;…

C语言指针相关知识(第一篇章)(非常详细版)

文章目录 前言一、指针概念的引入与指针的基本介绍&#xff08;一&#xff09;、内存与地址&#xff08;二&#xff09;、指针变量和地址&#xff08;三&#xff09;、指针变量类型的意义&#xff08;四&#xff09;、const修饰指针 二、指针的运算&#xff08;一&#xff09;、…

锤子蜡烛如何交易?Anzo Capital这样交易10倍收益结束

很多投资者发现以下的情况&#xff0c;就认为反转到来了&#xff0c;颓势即将结束牛市即将来临。什么情况呢&#xff1f;就是在交易中发现这种情况&#xff1a;会在局部低点形成&#xff0c;上影线很小或几乎没有上阴影&#xff0c;收盘价高出 1/4 &#xff0c;烛台总有长长的下…

【数据结构(邓俊辉)学习笔记】栈与队列01——栈应用(栈混洗、前缀后缀表达式、括号匹配)

文章目录 0. 概述1. 操作与接口2. 操作实例3. 实现4. 栈与递归5. 应用5.1 逆序输出5.1.1 进制转换5.1.1.1 思路5.1.1.2 算法实现 5.2 递归嵌套5.2.1 栈混洗5.2.1.1 混洗5.2.1.2 计数5.2.1.3 甄别 5.2.2 括号匹配5.2.2.1 构思5.2.2.2 实现5.2.2.3 实例 5.3 延迟缓冲5.3.1 中缀表…

Gitee 码云与Git 交互

优质博文&#xff1a;IT-BLOG-CN 一、进入码云官方网站&#xff0c;注册用户 码云(Gitee.com)是一个类似于GitHub的在线代码托管平台。 码云提供了包括版本控制、代码托管、协作开发和代码分享等功能&#xff0c;基于Git开发&#xff0c;支持代码在线查看、历史版本查看、Fo…

基于vs和C#的WPF应用之动画3

注&#xff1a;1、在内部和外部使用缓动函数 <Grid.Resources> <PowerEase x:Key"powerease" Power"3" EasingMode"EaseInOut"/> </Grid.Resources> <DoubleAnimation EasingFunction"{StaticResource powerease}&quo…