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。经过测试,发现其…...
SLO-Warden:云原生时代SLO自动化管理的工程实践
1. 项目概述:当SLO成为运维的“紧箍咒”在云原生和微服务架构成为主流的今天,服务的稳定性和可靠性不再是锦上添花,而是业务的生命线。对于运维工程师和SRE(站点可靠性工程师)而言,我们每天都在和各种指标、…...
FanControl深度技术解析:构建精准智能的风扇控制体系
FanControl深度技术解析:构建精准智能的风扇控制体系 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trending/fa/…...
Midscene.js技术架构深度解析:构建企业级视觉驱动自动化测试平台的技术挑战与解决方案
Midscene.js技术架构深度解析:构建企业级视觉驱动自动化测试平台的技术挑战与解决方案 【免费下载链接】midscene AI-powered, vision-driven UI automation for every platform. 项目地址: https://gitcode.com/GitHub_Trending/mid/midscene 在当今多平台、…...
构建高可用代理池:开源工具agentpull的架构解析与实战部署
1. 项目概述:一个轻量级、可编程的代理拉取工具最近在折腾一些自动化任务和分布式爬虫时,经常遇到一个头疼的问题:如何高效、稳定地管理海量的代理IP资源。无论是数据采集、社交媒体运营还是安全测试,一个可靠的代理池都是基础设施…...
3分钟掌握FSearch:Linux系统文件搜索效率提升300%的终极方案
3分钟掌握FSearch:Linux系统文件搜索效率提升300%的终极方案 【免费下载链接】fsearch A fast file search utility for Unix-like systems based on GTK3 项目地址: https://gitcode.com/gh_mirrors/fs/fsearch 还在为Linux系统中寻找文件而烦恼吗ÿ…...
3个步骤彻底告别电脑风扇噪音:Windows平台最精细的风扇控制解决方案
3个步骤彻底告别电脑风扇噪音:Windows平台最精细的风扇控制解决方案 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHu…...
英雄联盟终极工具箱:5个实用技巧让你游戏效率翻倍
英雄联盟终极工具箱:5个实用技巧让你游戏效率翻倍 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit League Akari英雄联盟工具箱是一…...
5分钟掌握百度网盘高速下载神器:完全免费的开源解析工具终极指南
5分钟掌握百度网盘高速下载神器:完全免费的开源解析工具终极指南 【免费下载链接】baidu-wangpan-parse 获取百度网盘分享文件的下载地址 项目地址: https://gitcode.com/gh_mirrors/ba/baidu-wangpan-parse 还在为百度网盘非会员下载速度只有几十KB而烦恼吗…...
MusicGPT:基于大语言模型的AI音乐导师项目架构与实现
1. 项目概述:当AI成为你的私人音乐导师最近在GitHub上看到一个挺有意思的项目,叫gabotechs/MusicGPT。光看名字,你可能会觉得这又是一个用GPT来生成音乐旋律或者歌词的玩具。但实际深入进去,你会发现它的野心和实用性远超想象。它…...
AutoHotkey V2扩展库:从脚本小子到全能开发者的进化之路
AutoHotkey V2扩展库:从脚本小子到全能开发者的进化之路 【免费下载链接】ahk2_lib 项目地址: https://gitcode.com/gh_mirrors/ah/ahk2_lib 你是否曾因AutoHotkey的功能局限而感到束手束脚?🤔 当简单的热键脚本无法满足复杂的业务需…...
