SpringMVC源码分析 RequestContextHolder使用与源码分析

简介: SpringMVC源码分析 RequestContextHolder使用与源码分析

起因描述


在Springboot项目中,多次看到对controller进行切面AOP时在前置通知方法中通过使用下面的方法拿到Request对象:


//通过使用工具类RequestContextHolder拿到ServletRequestAttributes
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//通过该实例方法获取到request对象
HttpServletRequest request = requestAttributes.getRequest();


接着通过该request对象获取到请求URL以及远程的ip地址,接着进行一系列的日志记录操作存储到数据库中:



提出疑问:为什么通过该工具类调用getRequest()就能够获取到请求对象?



实际应用(RequestContextHolder获取到request)

引入坐标+启动类

通过使用springboot框架来减少配置文件的编写,即拿即用:


<!--  web启动器:包含sping、springmvc、tomcat以及相关web集成工具、日志等  -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <version>2.4.5</version>
</dependency>
<!--  若想使用AOP,需要引入该jar包进行切面  -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.2</version>
</dependency>



接着添加一个Springboot启动类即可运行使用了!(内置了tomcat服务器,并且内部自动完成了spring、springmvc的配置)


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SmartparkingApplication {
    public static void main(String[] args) {
        SpringApplication.run(com.SmartparkingApplication.class, args);
    }
}


接着我们编写一个控制器HelloController来接收指定请求:


import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
 * @ClassName HelloController
 * @Author ChangLu
 * @Date 2021/4/24 11:13
 * @Description TODO
 */
@Controller
public class HelloController {
    @RequestMapping("/hello")
    public String hello(){
        return "hello";  //跳转到template目录下的hello.html,方便快速测试我们可以不使用thymeleaf以及html文件,能够达到日志切面效果即可
    }
}



此时我们启动通过启动类即可运行springboot项目了,特别简单!



AOP切面(使用RequestContextHolder)



@Component
@Aspect
public class LogAspect {
    //对控制器包下的所有类方法进行切面
    @Pointcut("execution(* com.controller.*.*(..))")
    public void point(){}
    //前置通知
    @Before("point()")
    public void before(JoinPoint joinPoint){
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();//通过工具类来获取到
        HttpServletRequest request = requestAttributes.getRequest();
        System.out.println(request.getSession());//session对象
        System.out.println(request.getMethod());//方法名
        System.out.println(request.getRequestURI());//请求路径(仅仅是被代理方法上的uri)
        System.out.println(request.getRequestURL());//完整请求路径
        System.out.println(request.getRemoteAddr());//可看做URI。返回发送请求的客户端或最后一个代理的Internet协议(IP)地址。 对于HTTP Servlet,与CGI变量REMOTE_ADDR的值相同
        System.out.println(request.getRemoteHost());//可看做URL。返回客户端或发送请求的最后一个代理的标准名称。 如果引擎无法或选择不解析主机名(以提高性能),则此方法返回IP地址的点分字符串形式。 对于HTTP Servlet,与CGI变量REMOTE_HOST的值相同。
        System.out.println(request.getRemotePort());//发送客户端的端口号
        System.out.println(request.getRemoteUser());//主机用户
        System.out.println(request.getContextPath());//上下文路径
    }
}



启动之后,分别使用浏览器或手机访问:http://localhost:8080/hello 或 http://172.23.153.250:8080/hello,通过request对象获取到了以下信息:





分析

对于通过ServletRequestAttributes调用getRequest()方法实际上获取的是HttpServletRequest接口的实现类:




通过该request能够获取到内容长度、类型、参数属性、协议、服务器名称、服务器端口、ip地址、发送请求的URI及URL、cookies…


认识RequestContextHolder

介绍

RequestContextHolder:持有上下文的Request容器,在该类中持有两个ThreadLocal保存当前线程下的request,其中常用方法如下:



若是想要获取到request、response,需要调用getRequestAttrebutes()获取到RequestAttrebutes,接着通过该对象来获取,如下:


//首先通过RequestContextHolder请求上下文执行者拿到ServletRequestAttributes
//你可将ServletRequestAttributes看做是HttpServletRequest的容器,通过该容器存储
ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = requestAttributes.getRequest();//获取请求对象
HttpServletResponse response = requestAttributes.getResponse();//获取相应对象


简述:springmvc在执行service(),doGet(),doPost()这类方法时,其方法内部都有一个预处理方法processRequest(request, response),在预处理方法中有initContextHolders(request, localeContext, requestAttributes)初始化上下文执行者,其中包含了对应的三个参数,即把新的RequestAttributes设置进LocalThread保存,实际上保存的类型为ServletRequestAttributes。



getRequestAttributes()=》ServletRequestAttributes
查看下RequestContextHolder的getRequestAttributes()方法:
public abstract class RequestContextHolder  {
  private static final boolean jsfPresent =
    ClassUtils.isPresent("javax.faces.context.FacesContext", RequestContextHolder.class.getClassLoader());
    //有两个ThreadLocal存储的就是请求对象
  private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
    new NamedThreadLocal<>("Request attributes");
  private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
    new NamedInheritableThreadLocal<>("Request context");
    //调用方法获取到ThreadLocal中的RequestAttributes
    @Nullable
    public static RequestAttributes getRequestAttributes() {
       RequestAttributes attributes = requestAttributesHolder.get();
       if (attributes == null) {
          attributes = inheritableRequestAttributesHolder.get();
       }
       return attributes;
    }
}


ThreadLocal:其解决多线程的数据安全问题,保证在多个线程下一个线程能够关联绑定指定的数据。只要线程是活动的并且ThreadLocal实例是可访问的,则每个线程都对其线程局部变量的副本持有隐式引用。 线程消失后,其线程本地实例的所有副本都将进行垃圾回收(除非存在对这些副本的其他引用

通过调用该方法返回获取到一个RequestAttributes该返回值是一个接口,实际返回的是其实现类ServletRequestAttributes



看一下该实现类的方法:



我们可以看到在该实现类中比其接口多出来几个方法(红圈画出来的),这也是为什么我们之前需要进行向下转型的原因!


这样的话我们在进行AOP切面时能够拿到request、response请求响应对象就能够做很多事情了,对一些请求信息进行保存等等的操作…

相关文章
|
12月前
|
存储 算法
TreadLocal源码分析
TreadLocal源码分析
|
12月前
vivid源码分析
vivid源码分析
69 0
|
缓存 Java C++
Spring5源码 - 01 BeanDefination源码分析
Spring5源码 - 01 BeanDefination源码分析
78 0
|
XML 前端开发 数据格式
springmvc源码分析
springmvc源码分析
110 0
springmvc源码分析
|
Java 容器
SpringBoot源码分析系列之三:拦截器的优雅实现
所谓拦截器即为可以拦截HTTP请求的并做一些前置或者后置的通用处理手段,是一种AOP的处理方式,它不依赖于servlet容器,而依赖于web框架SpringMVC。主要用于拦截controller的请求接口。 基于URL实现拦截器 基于注解实现拦截器
|
Web App开发 前端开发 Java
SpringMVC源码分析1:SpringMVC概述
第一章 Web MVC简介 —— 跟开涛学SpringMVC 转自: 跟开涛学SpringMVC webmvcjavaeespring跟开涛学SpringMVC  Web MVC简介 1.1、Web开发中的请求-响应模型:   在Web世界里,具体步骤如下: 1、  Web浏览器(如IE)发起请求,如访问http://sishuok.com 2、  Web服务器(如Tomcat)接收请求,处理请求(比如用户新增,则将把用户保存一下),最后产生响应(一般为html)。
SpringMVC源码分析和启动流程
SpringMvc原理和启动流程
3997 0
|
iOS开发
fishhook源码分析
最早了解到[fishhook](https://github.com/facebook/fishhook)是看了下面两篇文章之后,顿时让我觉得这是一个非常好的东西。总共210行代码,收获了1500+个star,神作啊。 1. [iOS Lazy Binding](http://www.atatech.org/articles/68014),使用fishhook拦截NSSetUncaughtE
2409 0
|
移动开发 Java 开发者
Stresstester源码分析
stresstester-1.0.jar是早期淘宝的一个压力测试工具,很方便开发人员进行本地代码的压力测试,其他专门压力测试工具也有很多,如:jmeter loadrunner 等等,本篇文章主要讲一下stresstester的源码设计
10593 0
|
大数据 DataX 分布式计算
gobblin 源码分析
最近,开始搞些大数据相关的内容,遇到的第一个问题,就是数据入库,小白刚入手,又不想写太多代码,于是从网上找,入库手段很多: DataX,Sqoop,以及Flume 等以及直接使用 Spark 进行入库,想了下当下的场景(不是简单的倒库,要从kafka拉...
1393 0

热门文章

最新文章