Spring Boot 获取 HttpServletRequest 的方法

本文介绍 Spring Boot 2 获取 HttpServletRequest 的方法。


目录

  • 概述
  • 方法
    • Controller 方法参数
    • 属性自动注入
    • 手动方法调用
    • 借助 @ModelAttribute 注解
  • 总结

概述

借助 Spring MVC 开发 Web 应用的过程中经常需要使用 HttpServletRequest 对象获取 HTTP 请求相关的信息,如:客户端 IP、HTTP Header 中的属性等。
使用 HttpServletRequest 对象的场景大体分为两类:

  • 在 Spring 注入的 Bean 中使用;
  • 在非 Spring 注入的 Bean 中使用,如普通的 Java 对象或类的静态方法。

方法

Controller 方法参数

原理:
在 Controller 方法开始处理请求时,Spring 会将 HttpServletRequest 对象自动赋值到方法参数中。除 HttpServletRequest 对象外,还有很多其它参数可以通过此方法获取,参考:https://docs.spring.io/spring/docs/current/spring-framework-reference/web.html#mvc-ann-methods

代码示例:

@GetMapping("/method1")
public String method1(HttpServletRequest request) {
    System.out.println("Request URI: " + request.getRequestURI());
    return "Invoke HttpServletRequest by method param.";
}

线程安全性分析:
HttpServletRequest 对象作为方法参数,相当于局部变量,是 线程安全 的。

缺点:

  • 代码冗余,每个 Controller 方法中都要有 HttpServletRequest request 参数;
  • HttpServletRequest 对象的获取必须从 Controller 开始,如果使用 HttpServletRequest 对象的地方在函数调用层级比较深的地方,那么整个函数调用链上的所有方法都要有 HttpServletRequest request 参数。实际上在请求处理的全过程中,HttpServletRequest 对象始终存在,相当于线程内部的一个全局变量。
属性自动注入

代码示例:

@Autowired
private HttpServletRequest autowiredRequest;

@GetMapping("/method2")
public String method2() {
    System.out.println("Request URI: " + autowiredRequest.getRequestURI());
    return "Invoke HttpServletRequest by @Autowired.";
}

线程安全性分析:
使用 @Autowired 注解时,Spring 实际注入的并非 HttpServletRequest 对象,而是一个代理 proxy(代理实现参考 AutowireUtils.ObjectFactoryDelegatingInvocationHandler),当方法中需要使用 HttpServletRequest 对象时通过此代理获取,所以虽然 Controller 是个单例类,但通过此方法使用 HttpServletRequest 对象是 线程安全 的。

优点:

  • 不局限于 Controller 中,可以在通过 @Component@Service@Repository 注解注入的其它 Bean 中使用;
  • 注入的对象不局限于 HttpServletRequest 对象,还可以注入其它 scoperequestsession 的对象,如 HttpServletResponse、HttpSession 等,并保证线程安全;
  • 相比于【Controller 方法参数】方法,减少了代码冗余,只需要在需要使用 HttpServletRequest 对象的方法所属 Bean 中注入。

缺点:

  • 如果存在很多 Bean 需要使用 HttpServletRequest 对象,则每个 Bean 都要维护一个使用 @Autowired 注解注入的 HttpServletRequest request 属性,也存在一定代码冗余,可以定义一个公共父类维护这些自动注入的属性,但是继承本身也存在一定问题。
手动方法调用

代码示例:

@GetMapping("/method3")
public String method3() {
    HttpServletRequest request =
            ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
    System.out.println("Request URI: " + request.getRequestURI());
    return "Invoke HttpServletRequest by ServletRequestAttributes.";
}

线程安全性分析:
与【属性自动注入】线程安全性分析一致,属于 线程安全

优点:

  • 可以在非 Spring 注入的 Bean 中使用,如普通 Java 对象或静态方法。

缺点:

  • 代码比较繁琐。
借助 @ModelAttribute 注解

代码示例:

@Autowired
private HttpServletRequest bindRequest;

@ModelAttribute
public void bindRequest(HttpServletRequest request) {
    this.bindRequest = request;
}

@GetMapping("/method4")
public String method4() {
    System.out.println("Request URI: " + bindRequest.getRequestURI());
    return "Invoke HttpServletRequest by @ModelAttribute.";
}

线程安全性分析:
@ModelAttribute 注解修饰 Controller 方法时,作用是在 Controller 中每个处理请求的方法执行前执行,而 Controller 是单例,所以当同时处理多个请求时 HttpServletRequest 对象属性可能会被覆盖,所以是 线程不安全 的!


总结

全量代码:

package tutorial.spring.boot.mvc.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

@RestController
public class DemoController {

    @Autowired
    private HttpServletRequest autowiredRequest;

    @Autowired
    private HttpServletRequest bindRequest;

    @GetMapping("/method1")
    public String method1(HttpServletRequest request) {
        System.out.println("Request URI: " + request.getRequestURI());
        return "Invoke HttpServletRequest by method param.";
    }

    @GetMapping("/method2")
    public String method2() {
        System.out.println("Request URI: " + autowiredRequest.getRequestURI());
        return "Invoke HttpServletRequest by @Autowired.";
    }

    @GetMapping("/method3")
    public String method3() {
        HttpServletRequest request =
                ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
        System.out.println("Request URI: " + request.getRequestURI());
        return "Invoke HttpServletRequest by ServletRequestAttributes.";
    }

    @ModelAttribute
    public void bindRequest(HttpServletRequest request) {
        this.bindRequest = request;
    }

    @GetMapping("/method4")
    public String method4() {
        System.out.println("Request URI: " + bindRequest.getRequestURI());
        return "Invoke HttpServletRequest by @ModelAttribute.";
    }
}

你可能感兴趣的:(Spring Boot 获取 HttpServletRequest 的方法)