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";}}

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

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

相关文章

可信执行环境(Tee)入门综述

SoK: Hardware-supported Trusted Execution Environments [ArXiv22] 摘要引言贡献 范围系统和威胁模型系统模型威胁模型共存飞地对手无特权软件对手系统软件对手启动对手外围对手结构对手侵入性对手 关于侧信道攻击的一点注记 VERIFIABLE LAUNCH信任根&#xff08;RTM&#xf…

8月最新修正版风车IM即时聊天通讯源码+搭建教程

8月最新修正版风车IM即时聊天通讯源码搭建教程。风车 IM没啥好说的很多人在找,IM的天花板了,知道的在找的都知道它的价值,开版好像就要29999,后端加密已解,可自己再加密,可反编译出后端项目源码,已增加启动后端需要google auth双重验证,pc端 web端 wap端 android端 ios端 都有 …

【Java 进阶篇】数据定义语言(DDL)详解

数据定义语言&#xff08;DDL&#xff09;是SQL&#xff08;结构化查询语言&#xff09;的一部分&#xff0c;它用于定义、管理和控制数据库的结构和元素。DDL允许数据库管理员、开发人员和其他用户创建、修改和删除数据库对象&#xff0c;如表、索引、视图等。在本文中&#x…

100万级连接,石墨文档WebSocket网关如何架构?

说在前面 在40岁老架构师 尼恩的读者交流群(50)中&#xff0c;很多小伙伴拿到一线互联网企业如阿里、网易、有赞、希音、百度、滴滴的面试资格。 最近&#xff0c;尼恩指导一个小伙伴简历&#xff0c;写了一个《高并发网关项目》&#xff0c;此项目帮这个小伙拿到 字节/阿里/…

【量化】量化原理浅析

前言 模型在端侧运行时&#xff0c;会追求模型保持原有精度的同时&#xff0c;让模型的运行速度更快。基本方向为模型压缩和加速&#xff0c;着力于减少网络参数量、降低计算复杂度。可通过以下方式实现&#xff1a; 针对网络结构本身进行改进&#xff0c;常用的3x3的卷积的叠加…

Docker-如何获取docker官网x86、ARM、AMD等不同架构下的镜像资源

文章目录 一、概要二、资源准备三、环境准备1、环境安装2、服务器设置代理3、注册docker账号4、配置docker源 四、查找资源1、服务器设置代理2、配置拉取账号3、查找对应的镜像4、查找不同版本镜像拉取 小结 一、概要 开发过程中经常会使用到一些开源的资源&#xff0c;比如经…

基于体系结构-架构真题2022(四十一)

给定关系模式R&#xff08;U,F&#xff09;&#xff0c;其中U为属性集&#xff0c;F是U上的一组函数依赖&#xff0c;那么函数依赖的公理系统中分解规则是指&#xff08;&#xff09;为F所蕴含。 解析&#xff1a; 伪传递是x到y&#xff0c;wy到z&#xff0c;则xw到z 传递是z…

【C/C++笔试练习】——printf在使用%的注意事项、for循环语句的三个条件、运算符优先级、删除公共字符

文章目录 C/C笔试练习1.%符号在printf用作格式说明符的注意事项&#xff08;1&#xff09;输出%5.3s&#xff08;2&#xff09;判断%中小数点含义 2.for循环语句的三个条件&#xff08;3&#xff09;判断循环次数&#xff08;4&#xff09;判断循环次数 3.运算符优先级&#xf…

计算机专业毕业设计项目推荐08-英语在线点读平台(SpringBoot+Vue+MongoDB)

英语在线点读平台&#xff08;SpringBootVueMongoDB&#xff09; **介绍****系统总体开发情况-功能模块****各部分模块实现** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以也比较了解计算机专业的毕业设…

【Linux】【网络】传输层协议:UDP

文章目录 UDP 协议1. 面向数据报2. UDP 协议端格式3. UDP 的封装和解包4. UDP 的缓冲区 UDP 协议 UDP传输的过程类似于寄信。 无连接&#xff1a;知道对端的IP和端口号就直接进行传输&#xff0c;不需要建立连接。不可靠&#xff1a;没有确认机制&#xff0c;没有重传机制&am…

钉钉h5微应用调试 整理

钉钉 H5微应用整理 1.申请H5微应用2.登录3.调试 1.申请H5微应用 https://open.dingtalk.com/ 登录钉钉开发平台。 应用appId、CorpId都可以在网站上自行查找 应用首页地址&#xff08;指手机端显示地址&#xff09; pc端首页地址&#xff08;指电脑端显示地址&#xff09; 我这…

工业交换机常见的故障有哪些?

通常情况下&#xff0c;工业交换机出现故障可以分为两类&#xff1a;软件性能故障和硬件物理故障。软性能故障通常指工业交换机在研发设计阶段出现的问题。 物理层故障主要指交换机本身的硬件故障以及连接交换机的物理线路故障。安防专用工业交换机的交换是根据通信双方传输信…

基于遗传算法解决的多仓库多旅行推销员问题(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

NSS [HXPCTF 2021]includer‘s revenge

NSS [HXPCTF 2021]includer’s revenge 题目描述&#xff1a;Just sitting here and waiting for PHP 8.1 (lolphp). 题目源码&#xff1a;&#xff08;index.php&#xff09; <?php ($_GET[action] ?? read ) read ? readfile($_GET[file] ?? index.php) : inclu…

【TCP】三次握手 与 四次挥手 详解

三次握手 与 四次挥手 1. 三次握手2. 四次挥手三次握手和四次挥手的区别 在正常情况下&#xff0c;TCP 要经过三次握手建立连接&#xff0c;四次挥手断开连接 1. 三次握手 服务端状态转化&#xff1a; [CLOSED -> LISTEN] 服务器端调用 listen 后进入 LISTEN 状态&#xff…

基于Xilinx UltraScale+ MPSOC(ZU9EG/ZU15EG)的高性能PCIe数据预处理平台

PCIE707是一款基于PCIE总线架构的高性能数据预处理FMC载板&#xff0c;板卡具有1个FMC&#xff08;HPC&#xff09;接口&#xff0c;1路PCIe x4主机接口、1个RJ45千兆以太网口、2个QSFP 40G光纤接口。板卡采用Xilinx的高性能UltraScale MPSOC系列FPGA作为实时处理器&#xff0c…

工具篇 | WSL使用入门教程以及基于WSL和natApp内网穿透实践 - 对比VMWare

介绍 在开发工具中&#xff0c;Windows Subsystem for Linux (WSL) 和 VMWare 它们都可以实现了在 Windows 上运行 Linux系统。 文章概览 WSL Vs VMWare 我们将简单比对 WSL 和 VMWare&#xff0c;在性能、资源消耗等方面的差异&#xff0c;以协助您做出更加明确的选择。 …

ATA-8000系列射频功率放大器——应用场景介绍

ATA-8000系列是一款射频功率放大器。其P1dB输出功率500W&#xff0c;饱和输出功率最大1000W。增益数控可调&#xff0c;一键保存设置&#xff0c;提供了方便简洁的操作选择&#xff0c;可与主流的信号发生器配套使用&#xff0c;实现射频信号的放大。 图&#xff1a;ATA-8000系…

Android 编译插桩操纵字节码

本文讲解如何编译插桩操纵字节码。 就使用 ASM 来实现简单的编译插桩效果&#xff0c;通过插桩实现在每一个 Activity 打开时输出相应的 log 日志。实现思路 过程主要包含两步&#xff1a; 1、遍历项目中所有的 .class 文件​ 如何找到项目中编译生成的所有 .class 文件&#…

基于C#的AE二次开发之IQueryFilter接口、ISpatialFilter接口、IQueryDef 接口的查询接口的介绍

一、开发环境 开发环境为ArcGIS Engine 10.2与Visual studio2010。在使用ArcEngine查询进行查询的时候主要使用三种查询接口IQueryFilter&#xff08;属性查询&#xff09; 、ISpatialFilter&#xff08;空间查询&#xff09; 、IQueryDef &#xff08;多表查询&#xff09; 那…