从 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.mzph.cn/news/831514.shtml

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

相关文章

华为手机 鸿蒙系统-android studio识别调试设备,开启adb调试权限

1.进入设置-关于手机-版本号&#xff0c;连续点击7次 认证&#xff1a;有锁屏密码需要输入密码&#xff0c; 开启开发者配置功能ok 进入开发者配置界面 打开调试功能 重新在androd studio查看可运行running devices显示了&#xff0c; 不行的话&#xff0c;重启一下android …

【开源物联网平台】window环境下搭建调试监控设备环境

&#x1f308; 个人主页&#xff1a;帐篷Li &#x1f525; 系列专栏&#xff1a;FastBee物联网开源项目 &#x1f4aa;&#x1f3fb; 专注于简单&#xff0c;易用&#xff0c;可拓展&#xff0c;低成本商业化的AIOT物联网解决方案 目录 一、使用docker脚本部署zlmediakit 1.1 …

Nextjs+Antd5.0打造面向AI的文档可视化引擎(最新更新)

hello&#xff0c;大家好&#xff0c;我是徐小夕。之前和大家分享了很多可视化&#xff0c;零代码和前端工程化的最佳实践&#xff0c;今天继续分享一下我开发的文档引擎 Nocode/WEP 的最新更新。 issue收集&#xff1a; https://github.com/MrXujiang/Nocode-Wep/issues 演示地…

ReentrantReadWriteLock(可重入读写锁)源码解读与使用

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Java源码解读-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. 读写锁是什么 3. ReentrantReadWriteLock是什么 4. 源码解…

ColdDTA:利用数据增强和基于注意力的特征融合进行药物靶标结合亲和力预测

ColdDTA发表在Computers in Biology and Medicine 的一篇一区文章 突出 • 数据增强和基于注意力的特征融合用于药物靶点结合亲和力预测。 • 与其他方法相比&#xff0c;它在 Davis、KIBA 和 BindingDB 数据集上显示出竞争性能。 • 可视化模型权重可以获得可解释的见解。 …

Python梯度提升决策树库之lightgbm使用详解

概要 LightGBM是一个快速、分布式、高性能的梯度提升决策树(Gradient Boosting Decision Tree)库,它在机器学习和数据挖掘领域被广泛应用。本文将介绍LightGBM库的安装方法、主要特性、基本功能、高级功能、以及在实际应用中的场景和总结。 安装 首先,需要安装LightGBM库…

第Y9周:重要模块解读

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 目录 以con.py为例&#xff1a; 一、autopad 二、Conv 三、Focus 四、C2f 文件…

Golang | Leetcode Golang题解之第66题加一

题目&#xff1a; 题解&#xff1a; func plusOne(digits []int) []int {n : len(digits)for i : n - 1; i > 0; i-- {if digits[i] ! 9 {digits[i]for j : i 1; j < n; j {digits[j] 0}return digits}}// digits 中所有的元素均为 9digits make([]int, n1)digits[0]…

如何基于nginx组建多个子目录网站

华子目录 实验要求实验步骤 实验要求 组建多个子目录网站www.openlab.com&#xff0c;该网站有2个子目录www.openlab.com/sxhkt和www.openlab.com/zywww.openlab.com/sxhkt使用http读取www.openlab.com/zy使用https读取 实验步骤 准备工作 [rootserver ~]# setenforce 0[ro…

串口单线半双工转换电路

用来把单线半双工模式的串口转换成双线&#xff0c;然后才能连接到普通的双线USB 串口模块&#xff0c;比如CH340 之类的。电路设计来自大佬的博客&#xff1a;AVR half-duplex software UART supporting single pin operation。他在Arduino 上用软件模拟串口功能&#xff0c;利…

测试PG中事务隔离级别

我们知道事务隔离级别有&#xff1a;读未提交&#xff0c;读已提交&#xff0c;可重复读&#xff0c;可序列化。 读未提交 存在 脏读&#xff0c;不可重复读&#xff0c;幻读&#xff1b; 读已提交 存在 不可重复读&#xff0c;幻读 可重复读 存在 幻读 PG 下默认为读已提交…

微信小程序使用蓝牙连接硬件

目录 一、蓝牙官方api文档 二、蓝牙重要参数介绍 三、案例教程 1. 获取蓝牙权限&#xff08;openBluetoothAdapter&#xff09; 2. 开始搜索蓝牙设备(startBluetoothDevicesDiscovery) 3. 监听搜索到新设备的事件(onBluetoothDeviceFound) 4.连接蓝牙设备&#xff08;crea…

SpringData JPA - ORM 框架下,打造高效数据访问层

目录 一、SpringData JPA 概述 1.1、什么是 JPA 1.2、什么是 ORM 1.3、什么是 Hibernate 1.4、JPA 和 Hibernate 的关系 1.5、JPA 的优势 二、SpringData JPA 实战开发 2.1、依赖 2.2、配置文件 2.3、启动类 2.4、创建实体 2.5、基于 JpaRepository 的 CRUD 三、…

网络安全审计

一、什么叫网络安全审计 网络安全审计是按照一定的安全策略&#xff0c;利用记录、系统活动和用户活动等信息&#xff0c;检查、审查和检验操作时间的环境及活动&#xff0c;从而发现系统漏洞、入侵行为或改善系统性能的过程&#xff0c;它是提高系统安全性的重要手段。 系统…

数据结构:时间复杂度/空间复杂度

目录 一、时间复杂度 定义 常见的时间复杂度 如何计算时间复杂度 计算方法 三、实例分析 二、空间复杂度 定义 重要性 常见的空间复杂度 二、空间复杂度 定义 重要性 常见的空间复杂度 计算方法 三、实例分析 大O的渐进表示法 最好情况&#xff08;Best Case…

spring框架学习记录(1)

前半个月一直在应付期中考试&#xff0c;快被折磨似了orz 文章目录 SpringIoC(Inversion of Control) 控制反转与DI(Dependency Injection)依赖注入bean相关bean配置bean实例化bean的生命周期 依赖注入相关依赖注入方式依赖自动装配 容器创建容器获取bean Spring IoC(Inversi…

leetcode295. 数据流的中位数

class MedianFinder {//A为小根堆&#xff0c;B为大根堆List<Integer> A,B;public MedianFinder() {A new ArrayList<Integer>();B new ArrayList<Integer>();}public void addNum(int num) {int m A.size(),n B.size();if(m n){insert(B,num);int top …

BeanFactory 源码浅析

BeanFactory 功能介绍 BeanFactory 是核心容器&#xff0c;负责管理 Bean 对象 BeanFactory 接口的功能只有一个 getBean() 方法BeanFactory 的实现类&#xff08;DefaultListableBeanFactory&#xff09;包含&#xff1a;控制反转、基本的依赖注入、Bean 生命周期的各种功能…

从浏览器输入url到页面加载(八)你的web网站有几台服务器?

你有没有想过一个问题&#xff0c;做为一名前端开发&#xff0c;你的网站上线后&#xff0c;准备了几台服务器&#xff1f;前端静态资源用了几台&#xff0c;你调接口的那个后端部署了几台&#xff1f; 目录 1 没接触过这个问题很正常 2 当访问量上升的时候 2.1 提升带宽 …

绝了!这是我见过最详细的HashMap源码解析

1 概述 HashMap是基于哈希表实现的,每一个元素是一个key-value对,其内部通过单链表解决冲突问题,容量不足(超过了阀值)时,同样会自动增长. HashMap是非线程安全的,只适用于单线程环境,多线程环境可以采用并发包下的concurrentHashMap HashMap 实现了Serializable接口&#x…