Spring Controller内存马
获取当前上下文运行环境
getCurrentWebApplicationContext
WebApplicationContext context = ContextLoader.getCurrentWebApplicationContext();
在SpringMVC环境下获取到的是一个XmlWebApplicationContext类型的Root WebApplicationContext:

在Spring MVC环境中,由于使用xml文件默认配置了ContextLoaderListener,所以可以获取到Root Context:<listener><br /> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class><br /></listener>
在SpringBoot环境下,默认没有xml文件配置listener,这里获取到的结果会是null。
WebApplicationContextUtils
在Spring环境中可以通过如下代码获取到ApplicationContext(Facade):
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
// attributes.getRequest()获取到的是RequestFacade
ServletContext servletContext = attributes.getRequest().getServletContext();
// 最后获取到的servletContext是ApplicationContextFacade
这个servletContext的内部有一个Root Context的attribute:

这个Root Context可以通过WebApplicationContextUtils#getWebApplicationContext方法来获取:

WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
// 这里获取到的Root Context是AnnotationConfigServletWebServerApplicationContext
值得说明的是,由于在SpringBoot环境中没有使用xml文件进行配置,所以区别与第一种方式获取的XmlWebApplicationContext,这里的Root Context获取到的是AnnotationConfigServletWebServerApplicationContext。(注解形式配置)
RequestContextUtils
通过ServletRequest类的实例来获取WebApplicationContext:
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
WebApplicationContext context = RequestContextUtils.findWebApplicationContext(request);

这个context是作为attribute放在request对象中的:

所以引申出了第四种方法,直接通过attribute的名字获取。
getAttribute
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
WebApplicationContext context = (WebApplicationContext) attributes.getAttribute("org.springframework.web.servlet.DispatcherServlet.CONTEXT", 0);
attributes是ServletRequestAttributes的实例,其内部就有一个request对象的引用,通过调用getAttribute方法就可以从其内部的request对象里获取属性(0表示从request对象上获取属性,而不是从session对象上获取):

注意事项
前两种方式获取的到的是Root Context,后两种方式获取到的是Child Context,而Root Context无法获取Child Context中的Bean。
在有些MVC的配置中,开启注解扫描(component-scan)配置在dispatcherServlet-servlet.xml中,导致RequestMappingHandlerMapping的Bean只存在于Child Context中,而使用前两种方式获取到的Root Context是无法拿到对应Bean的,所以推荐使用后两种方式获取上下文环境。
手动注册Controller
Controller注册及查找原理
参考:SpringMVC源码之Controller查找原理 - 卧颜沉默 - 博客园 (cnblogs.com)
Spring2.5 ~ 3.1之间一般使用DefaultAnnotationHandlerMapping映射器。
Spring3.1之后使用RequestMappingHandlerMapping映射器来支持对@Controller和@RequestMapping注解。
认识两个类(具体细节可以去看一眼源码):
- RequestMappingInfo类:对@RequestMapping注解的封装,里面包含了Http请求头的信息,如url,method等。
- HandlerMethod类:对Controller处理请求的方法的封装,里面包含了方法所属的bean、方法对应的method,参数等。
Controller注册
在Spring MVC初始化的时候,会进入RequestMappingHandlerMapping#afterPropertiesSet方法:

来到父类的afterPropertiesSet方法,AbstractHandlerMethodMapping#afterPropertiesSet,这个方法会从context中查找出所有的bean然后进入processCandidateBean方法中处理:

org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#processCandidateBean:

可以注意到isHandler方法,它会判断bean是否含有@Controller和@RequestMapping的注解:

之后来到AbstractHandlerMethodMapping#detectHandlerMethods方法,可以结合动调来看:

跟进RequestMappingHandlerMapping#getMappingForMethod方法中先根据method创建RequestMappingInfo实例:

前面提到了这个类是对@RequestMapping注解的封装,跟进createRequestMappingInfo方法中:

再往后来到createRequestMappingInfo方法,它会根据RequestMapping封装的信息来build一个RequestMappingInfo实例然后返回:

处理完根据method寻找handler的逻辑之后,再回到detectHandlerMethods方法,来到了registerHandlerMethod方法来注册handler:

这个方法调用了AbstractHandlerMethodMapping.MappingRegistry#register方法:

至此Controller的完成了注册,下面来看一个http请求过来的时候Controller的查找逻辑。
Controller查找
在处理http请求的时候首先会被DispatcherServelt所处理,在DispatcherServlet#doDispatch方法中会根据当前的request对象的请求路径(lookup path)来匹配相应的handler:

之后经过一系列调用(一直单步跟入就好了)会来到AbstractHandlerMethodMapping#lookupHandlerMethod方法:

Controller的动态注册
registerMapping
最直接的一种方式,使用RequestMappingHandlerMapping#registerMapping来注册,最后还是调用了MappingRegistry#register方法:

@RestController
public class DemoController1 {@RequestMapping("/demo")public String demo() throws Exception {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();ServletContext servletContext = attributes.getRequest().getServletContext();WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);// 从IoC容器中获取beanRequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);// 防止重复注册: RequestMappingHandlerMapping#getHandlerMethods 可以获取所有已注册handler的映射关系for (RequestMappingInfo info : handlerMapping.getHandlerMethods().keySet()) {if (info.toString().contains("/hello")) {return "Already injected";}}// 获取methodClass<?> clazz = Class.forName("com.example.springboot.controller.MyController");Method method = clazz.getDeclaredMethods()[0];// 获取mapping (RequestMappingInfo)RequestMappingInfo mapping = RequestMappingInfo.paths("/hello").customCondition(new RequestMethodsRequestCondition()).build();// handler就是Controller的一个实例Object handler = new MyController();handlerMapping.registerMapping(mapping, handler, method);return "success";}
}
最后的效果与待注册的Controller中方法的注解有关,如果方法添加了@ResponseBody注解,则会直接返回内容:
public class MyController {@ResponseBodypublic String hello() {return "hello";}}

如果没有@ResponseBody注解,则会向view层解析(thymeleaf等模板引擎),这里是hello.html:
public class MyController {public String hello() {return "hello";}}

detectHandlerMethods
AbstractHandlerMethodMapping#detectHandlerMethods这个方法在上文中的Controller注册部分提到过:
这里最后会调用registerHandlerMethod方法,之后还是来到了MappingRegistry#register方法:
...@RequestMapping("/demo2")public String demo2() throws Exception {ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();ServletContext servletContext = attributes.getRequest().getServletContext();AnnotationConfigServletWebServerApplicationContext context = (AnnotationConfigServletWebServerApplicationContext) WebApplicationContextUtils.getWebApplicationContext(servletContext);// 注册beancontext.getBeanFactory().registerSingleton("MyController", new MyController());RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);Method method = AbstractHandlerMethodMapping.class.getDeclaredMethod("detectHandlerMethods", Object.class);method.setAccessible(true);method.invoke(handlerMapping,"MyController");return "demo2";}
...
这种方式必须在MyController中添加@RequestMapping注解,否则在detectHandlerMethods中无法获取到RequestMappingInfo对象。
与上面一样,如果有@ResponseBody注解,则会直接返回:
public class MyController {@RequestMapping("/hello")@ResponseBodypublic String hello() {return "hello";}}

没有@ResponseBody注解,则会向view层解析:
public class MyController {@RequestMapping("/hello")public String hello() {return "hello";}}

相关文章:
Spring Controller内存马
获取当前上下文运行环境 getCurrentWebApplicationContext WebApplicationContext context ContextLoader.getCurrentWebApplicationContext(); 在SpringMVC环境下获取到的是一个XmlWebApplicationContext类型的Root WebApplicationContext: 在Spring MVC环境中…...
Mysql004:用户管理
前言:本章节讲解的是mysql中的用户管理,包括(管理数据用户)、(控制数据库的访问权限)。 目录 1. 查询用户 2. 创建用户 3. 修改用户密码 4. 删除用户 5. 权限控制 1. 查询用户 在mysql数据库中࿰…...
计算机视觉与深度学习 | 视觉里程计(Visual Odometry,VO)研究现状
===================================================== github:https://github.com/MichaelBeechan CSDN:https://blog.csdn.net/u011344545 ===================================================== 视觉里程计(Visual Odometry,VO) 研究背景及意义视觉里程计国内外研…...
Mojo:新型AI语言中的7个令人惊叹的Python升级,用简单的英语解释人工智能
Mojo:新型AI语言中的7个令人惊叹的Python升级 编程之美 用简单的英语解释人工智能 编程之美 由Coding Beauty设计的图像,使用Mojo标志和Python标志。 它比C更快,与Python一样简单,但速度提高了35000倍。 进入Mojo:一种…...
TCP连接的三次握手与四次挥手【重点】
TCP的运输连接管理概述 TCP是面向连接的协议,它基于运输连接来传送TCP报文段 TCP运输连接的建立和释放是每一次面向连接的通信中必不可少的过程 TCP运输连接有以下三个阶段 TCP的运输连接管理就是使运输连接的建立和释放都能正常的进行 TCP建立连接的三次握手&a…...
重生奇迹MU新手玩家如何快速熟悉游戏
新手玩家必须掌握什么呢,建议新手玩家去官方网站上面看看游戏的详细介绍,必须要好的熟悉游戏的各种玩法,让玩家可以有一个初步的认识。 所以这方面是非常重要的,建议每位重生奇迹MU玩家都应该注重这些东西。下面我们就来简单介绍…...
MySQL 用户权限和远程访问设置
目录 一、用户操作查看当前拥有用户创建用户修改用户密码删除用户给root用户开放外网访问 二、用户权限操作授予权限的原则查看授予用户的权限给用户添加权限回收权限 一、用户操作 先要使用root用户登录MySQL后在执行后面操作 查看当前拥有用户 SELECT host,user,Grant_pri…...
Golang基础之关键字
Type 参考 ## https://blog.csdn.net/SHELLCODE_8BIT/article/details/122837699 type有如下几种用法: 定义结构体定义接口类型定义类型别名类型查询 类型定义 type Celsius float64 // 摄氏温度 type Fahrenheit float64 // 华氏温度const (AbsoluteZeroC Cels…...
DataFrame插入多列PerformanceWarning: DataFrame is highly fragmented.
DataFrame插入多列PerformanceWarning: DataFrame is highly fragmented. dataframe列比较多,增加列的代码如下: dfpd.DataFrame() for i in range(1000):vlist[]for j in range(1000):vlist.append(j) df[COL_ str(i)] vlistdf警告错误&#x…...
Springboot登录验证的统一拦截处理
在进行Springboot项目开发的时候如何把每次请求都要验证的用户进行提取拦截统一处理 背景 如果不进行统一的拦截处理,其实这是一个非常痛苦的一件事情,因为每次用户请求你都要去进行用户的信息(用户信息存储在session中)的验证&…...
自定义类型详解(上)
结构体 1 结构体的声明 1.1 结构的基础知识 结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。 1.2 结构的声明 struct tag//struct是结构体的标志,tag是标签;名字。 {member-list;//成员变量 }variable-list;//变量列…...
【数据库——MySQL】(9)函数、查询练习及讲解
目录 1. 题目1.1 函数练习1.2 数据库查询 2. 解答2.1 函数练习2.2 数据库查询 1. 题目 1.1 函数练习 求圆周率的值,保留 6 位小数。生成两个 100 到 200 间的随机数。将”武汉大学”,”数学学院”,”计算数学”连接成一个字符串。求字符串中第三个字符为 A 的所有…...
【数据结构与算法——C语言】“串操作与算法”之“找出最长串及其长度”
目录 1. 实验内容及上机实验所用平台1.1 实验内容1.2 实验平台软件 2. 流程图3. 源代码4. 用例测试5. 实验总结 1. 实验内容及上机实验所用平台 1.1 实验内容 【问题描述】 给定两个字符串 s1 和 s2,求最长的 s1 前缀 ss 使得 ss 为 s2 的最长后缀,输出…...
泡泡玛特:一家中国潮玩品牌的出海之旅
泡泡玛特的出海之旅,可以为中国出海企业提供怎样的启示和借鉴? 中国潮玩品牌的出海之旅 如果在年轻人群体中聊起泡泡玛特,那么估计无人不知无人不晓。这家成立于2010年的潮玩企业,凭借琳琅满目让消费者爱不释手的创新产品…...
淘宝商品sku信息抓取接口api
在电商行业中,SKU是一个经常被使用的术语,但是对于很多人来说,这个词可能还比较陌生。在这篇文章中,我们将详细解释什么是SKU,以及在电商业务中它的作用和意义。 什么是SKU? SKU是“Stock Keeping Unit”…...
MySQL 多表关系(多表查询 一)
多表关系描述 MySQL是一种关系型数据库管理系统,它支持多表关系,这在数据库设计和查询中非常重要。 项目开发中,在进行数据库表结构设计时,会根据业务需求及业务模块之间的关系,分析并设计表结构,由于业务…...
【面试高高手】——JavaIO篇(23题)
文章目录 1.什么是Java IO?2.如何从数据传输方式理解IO流?3.Java IO设计上使用了什么设计模式?4.什么是Java NIO?5.什么时BIO?6.什么是AIO?7.你怎么理解同步IO和异步IO?8.你怎么理解阻塞IO和非阻塞IO?9.IO中的输入流和输出流有…...
图像采集 deep OCR
按照芯片类型可以分为CCD相机、CMOS相机 按照传感器的结构特性可以分为线阵相机、面阵相机 按照扫描方式可以分为隔行扫描相机、逐行扫描相机 按照分辨率大小可以分为普通分辨率相机、高分辨率相机按照输出信号方式可以分为模拟相机、数字相机 按照输出色彩可以分为单色(黑白)相…...
Linux 终端命令总结
一、常用的七条命令 命令 对应英文作用lslist查看当前文件夹下的内容pwdprint work directory查看当前所在文件夹cd [目录名]change directory切换文件夹 touch [文件名]touch如果文件不存在新建文件mkdir [目录名]make directory创建目录rm[文件名]remo…...
中国核动力研究设计院使用 DolphinDB 替换 MySQL 实时监控仪表
随着仪表测点的大幅增多和采样频率的增加,中国核动力研究设计院仪控团队原本基于 MySQL 搭建的旧系统已经无法满足大量数据并发写入、实时查询和聚合计算的需求。他们在研究 DB-Engines 时序数据库榜单时了解到国内排名第一的 DolphinDB。经过测试,发现其…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
深入浅出:JavaScript 中的 `window.crypto.getRandomValues()` 方法
深入浅出:JavaScript 中的 window.crypto.getRandomValues() 方法 在现代 Web 开发中,随机数的生成看似简单,却隐藏着许多玄机。无论是生成密码、加密密钥,还是创建安全令牌,随机数的质量直接关系到系统的安全性。Jav…...
关于nvm与node.js
1 安装nvm 安装过程中手动修改 nvm的安装路径, 以及修改 通过nvm安装node后正在使用的node的存放目录【这句话可能难以理解,但接着往下看你就了然了】 2 修改nvm中settings.txt文件配置 nvm安装成功后,通常在该文件中会出现以下配置&…...
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南
精益数据分析(97/126):邮件营销与用户参与度的关键指标优化指南 在数字化营销时代,邮件列表效度、用户参与度和网站性能等指标往往决定着创业公司的增长成败。今天,我们将深入解析邮件打开率、网站可用性、页面参与时…...
中医有效性探讨
文章目录 西医是如何发展到以生物化学为药理基础的现代医学?传统医学奠基期(远古 - 17 世纪)近代医学转型期(17 世纪 - 19 世纪末)现代医学成熟期(20世纪至今) 中医的源远流长和一脉相承远古至…...
在Ubuntu24上采用Wine打开SourceInsight
1. 安装wine sudo apt install wine 2. 安装32位库支持,SourceInsight是32位程序 sudo dpkg --add-architecture i386 sudo apt update sudo apt install wine32:i386 3. 验证安装 wine --version 4. 安装必要的字体和库(解决显示问题) sudo apt install fonts-wqy…...
浪潮交换机配置track检测实现高速公路收费网络主备切换NQA
浪潮交换机track配置 项目背景高速网络拓扑网络情况分析通信线路收费网络路由 收费汇聚交换机相应配置收费汇聚track配置 项目背景 在实施省内一条高速公路时遇到的需求,本次涉及的主要是收费汇聚交换机的配置,浪潮网络设备在高速项目很少,通…...
Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
关于uniapp展示PDF的解决方案
在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项: 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库: npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...
python爬虫——气象数据爬取
一、导入库与全局配置 python 运行 import json import datetime import time import requests from sqlalchemy import create_engine import csv import pandas as pd作用: 引入数据解析、网络请求、时间处理、数据库操作等所需库。requests:发送 …...
