程序员scholar 程序员scholar
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
首页
  • Java 基础

    • JavaSE
    • JavaIO
    • JavaAPI速查
  • Java 高级

    • JUC
    • JVM
    • Java新特性
    • 设计模式
  • Web 开发

    • Servlet
    • Java网络编程
  • Web 标准

    • HTML
    • CSS
    • JavaScript
  • 前端框架

    • Vue2
    • Vue3
    • Vue3 + TS
    • 微信小程序
    • uni-app
  • 工具与库

    • jQuery
    • Ajax
    • Axios
    • Webpack
    • Vuex
    • WebSocket
    • 第三方登录
  • 后端与语言扩展

    • ES6
    • Typescript
    • node.js
  • Element-UI
  • Apache ECharts
  • 数据结构
  • HTTP协议
  • HTTPS协议
  • 计算机网络
  • Linux常用命令
  • Windows常用命令
  • SQL数据库

    • MySQL
    • MySQL速查
  • NoSQL数据库

    • Redis
    • ElasticSearch
  • 数据库

    • MyBatis
    • MyBatis-Plus
  • 消息中间件

    • RabbitMQ
  • 服务器

    • Nginx
  • Spring框架

    • Spring6
    • SpringMVC
    • SpringBoot
    • SpringSecurity
  • SpringCould微服务

    • SpringCloud基础
    • 微服务之DDD架构思想
  • 日常必备

    • 开发常用工具包
    • Hutoll工具包
    • IDEA常用配置
    • 开发笔记
    • 日常记录
    • 项目部署
    • 网站导航
    • 产品学习
    • 英语学习
  • 代码管理

    • Maven
    • Git教程
    • Git小乌龟教程
  • 运维工具

    • Docker
    • Jenkins
    • Kubernetes
  • 算法笔记

    • 算法思想
    • 刷题笔记
  • 面试问题常见

    • 十大经典排序算法
    • 面试常见问题集锦
关于
GitHub (opens new window)
npm

(进入注册为作者充电)

  • 后端开发

    • Spring Boot多模块项目开发
    • Spring Boot图片资源返回
    • Spring Boot文件上传
    • Spring Boot文件下载
    • 对接第三方文件上传
    • Servlet 原生API
      • 一、Spring Boot如何调用Servlet API
        • 1. 在 Controller 层使用 Servlet API
        • 2. 在 Service 层使用 Servlet API
        • 3. 在工具类中使用 Servlet API
      • 二、Servlet API 的使用
        • 1. HttpServletRequest 的使用
        • 2. HttpServletResponse 的使用
        • 3. HttpSession 的使用
        • 3.1 配置会话超时时间
        • 3.2 session自动验证流程
        • 4. Cookie 的使用
      • 三、使用 Servlet 过滤器(Filter)
      • 四、使用 Servlet 监听器(Listener)
      • 五、在拦截器中使用 ServletAPI
        • 1. 实现拦截器
        • 2. 注册拦截器
      • 五、使用ServletContext
        • 1. 在应用范围内共享数据
        • 2. 获取 Web 应用的元数据
        • 3. ServletContext 的局限性
      • 六、SpringBoot推荐的写法
        • 1. 获取 Web 应用的真实路径
        • 2. 加载 Web 应用的资源
        • 3. 获取应用的初始化参数
    • HttpServletResponse 和 ResponseEntity
    • 后端解决跨域问题
    • 后端拦截器
    • SpringBoot+Vue实现邮件发送与验证码验证
    • 谷歌验证码
    • 利用hutool工具类实现图片验证码
    • 统一返回格式
    • 通用 普通 登录模块
    • 通用 JWT 登录认证模块
    • 通用 普通 注册模块
    • 基于 MyBatis curd
    • 基于 MyBatis-Plus curd
    • Java 常见对象模型
    • 开发枚举的使用
    • MyBatis与MyBatis-Plus日期类型处理
    • 接口日志拦截基础版
    • 接口日志拦截进阶版
    • 文件操作工具类
    • Spring Boot 数据校验
    • 幂等性
  • 前端开发

  • 开发笔记
  • 后端开发
scholar
2024-12-27
目录

Servlet 原生API

# Servlet 原生API

前言

在 Spring Boot 中,尽管它提供了高层次的封装和简化,但仍然保留了对原生 Servlet API 的支持。Spring Boot 应用中,很多场景下仍然需要使用到 Servlet 的原生功能,比如 HttpServletRequest、HttpServletResponse、HttpSession 等。

# 一、Spring Boot如何调用Servlet API

# 1. 在 Controller 层使用 Servlet API

在 Controller 层,Servlet API 作为方法参数直接注入,Spring 会自动将当前请求的对象传递给控制器方法。

@RestController
@RequestMapping("/api")
public class MyController {

    @GetMapping("/example")
    public String handleRequest(HttpServletRequest request, HttpServletResponse response, HttpSession session) {
        // 在 Controller 层,直接使用这些对象
        return "请求处理成功";
    }
}
1
2
3
4
5
6
7
8
9
10

关键点:

  • 方法参数注入:在 Controller 方法中,HttpServletRequest、HttpServletResponse 和 HttpSession 可以作为方法参数直接注入。
  • Spring 自动注入:这些对象由 Spring 自动注入,与当前请求相关联。

# 2. 在 Service 层使用 Servlet API

通过依赖注入,可以在 Service 层中直接获取 Servlet API 对象,通常用于获取请求信息或设置全局响应数据。

使用 @Autowired 注入

@Service
public class UserService {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private HttpSession session;

    public void performAction() {
        // 在 Service 层使用 HttpServletRequest 和 HttpSession
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

关键点:

  • 依赖注入:使用 @Autowired 注入 Servlet API 对象,适用于 Spring 管理的任何 Bean。
  • 线程安全:Spring 为每个请求创建独立的 HttpServletRequest 和 HttpSession,确保线程安全。

# 3. 在工具类中使用 Servlet API

在工具类中,由于无法直接注入 HttpServletRequest 和 HttpServletResponse,可以通过 Spring 提供的 RequestContextHolder 来获取这些对象。

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.stereotype.Component;

@Component
public class ServletUtil {

    public static HttpServletRequest getRequest() {
        // 获取当前请求的 HttpServletRequest
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static HttpServletResponse getResponse() {
        // 获取当前请求的 HttpServletResponse
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getResponse();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

关键点:

  • RequestContextHolder:用于获取当前请求的上下文,可在任意 Spring Bean 中使用。
  • 为什么需要强换:RequestContextHolder.getRequestAttributes() 返回的类型是 RequestAttributes,而获取请求和响应的方法只有在 ServletRequestAttributes 中可用,因此需要强制转换。
  • 适用场景:在工具类中统一处理请求和响应数据,如日志记录、通用的参数处理等。

# 二、Servlet API 的使用

HttpServletRequest 和 HttpServletResponse

  • 这些对象代表了当前请求的 HTTP 请求和响应,生命周期与这次请求相同。当一个请求到达服务器时,Spring 会为该请求生成对应的 HttpServletRequest 和 HttpServletResponse 对象,并在请求处理完毕后销毁。
  • 不管您在哪个层次(Controller、Service、工具类等)使用它们,它们始终指向当前请求对应的对象。

# 1. HttpServletRequest 的使用

HTTP 请求格式

HttpServletRequest 是一个重要的接口,提供了对请求数据的访问。可以获取请求参数、请求头、请求路径、Cookie、Session 等信息。

使用示例:

import javax.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("/api")
public class RequestController {

    @GetMapping("/request-info")
    public String getRequestInfo(HttpServletRequest request) {
        // 1. 获取请求方法,如 GET、POST
        String method = request.getMethod();
        // 2. 获取请求的完整 URL,不包含查询参数
        String requestURL = request.getRequestURL().toString();
        // 3. 获取查询参数部分,例如 ?name=John&age=30
        String queryString = request.getQueryString();
        // 4. 获取指定的请求头信息,例如 User-Agent
        String userAgent = request.getHeader("User-Agent");
        // 5. 获取客户端的 IP 地址
        String clientIp = request.getRemoteAddr();
        // 6. 获取请求中的单个参数值
        String username = request.getParameter("username");
        // 7. 获取多个同名参数的值,返回数组
        String[] interests = request.getParameterValues("interests");
        // 8. 获取所有请求参数的 Map
        Map<String, String[]> paramMap = request.getParameterMap();
        // 9. 获取与当前请求关联的 HttpSession 对象,用于在多个请求之间共享数据
        HttpSession session = request.getSession();

        return String.format(
            "请求方法: %s\n请求URL: %s\n查询参数: %s\nUser-Agent: %s\n客户端IP: %s\n用户名: %s\n兴趣: %s",
            method, requestURL, queryString, userAgent, clientIp, username,
            interests != null ? String.join(", ", interests) : "无"
        );
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

关键点说明:

  • request.getMethod():获取 HTTP 请求方法(如 GET、POST)。
  • request.getRequestURL():获取请求的完整 URL。
  • request.getQueryString():获取请求的查询参数部分。
  • request.getHeader("User-Agent"):获取指定的请求头信息。
  • request.getRemoteAddr():获取客户端的 IP 地址。
  • request.getParameter("paramName"):获取请求中的单个参数值。
  • request.getParameterValues("paramName"):获取多个同名参数的值,返回数组。
  • request.getParameterMap():获取所有请求参数的 Map。
  • request.getSession():获取与当前请求关联的会话对象,用于在多个请求之间共享数据。

# 2. HttpServletResponse 的使用

HTTP 响应格式

HttpServletResponse 用于设置响应数据,如响应状态码、响应头、Cookie、输出流等。

使用示例:

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@RestController
@RequestMapping("/api")
public class ResponseController {

    @GetMapping("/custom-response")
    public void setCustomResponse(HttpServletResponse response) {
        try {
            // 1. 设置 HTTP 响应状态码,例如 200 OK
            response.setStatus(HttpServletResponse.SC_OK);
            // 2. 设置自定义响应头,例如 "Custom-Header: CustomValue"
            response.setHeader("Custom-Header", "CustomValue");
            // 3. 设置响应的内容类型和字符编码
            response.setContentType("application/json;charset=UTF-8");
            // 4. 获取输出流并写入 JSON 数据到响应体中
            response.getWriter().write("{\"message\":\"自定义响应内容\"}");
            // 5. 刷新输出流,确保数据完全发送到客户端
            response.getWriter().flush();
            // 6. 添加一个 Cookie 到响应中
            Cookie cookie = new Cookie("sessionId", "12345");
            cookie.setMaxAge(60 * 60); // 设置 Cookie 的有效期为 1 小时
            response.addCookie(cookie);
            // 7. 如果需要,执行重定向操作
            // response.sendRedirect("/login");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

关键点说明:

  • response.setStatus(HttpServletResponse.SC_OK):设置 HTTP 响应状态码(如 200 OK)。
  • response.setHeader("Custom-Header", "CustomValue"):设置自定义响应头。
  • response.setContentType("application/json;charset=UTF-8"):设置响应的内容类型和字符编码。
  • response.getWriter().write():向响应体中写入数据。
  • response.getWriter().flush():刷新输出流,确保数据完全发送到客户端。
  • response.addCookie(cookie):向响应中添加一个 Cookie。
  • response.sendRedirect("/login"):执行重定向操作。

# 3. HttpSession 的使用

HttpSession 用于在多个请求之间保存用户的数据,如登录状态、购物车等。

提示

HttpSession 是基于会话的对象,它的生命周期通常比单个请求要长,意味着可以在多个请求之间共享数据(例如用户的登录状态、购物车信息等)。当用户第一次访问应用时,服务器会为该用户创建一个新的会话,并分配一个唯一的会话 ID。这个会话 ID 会通过 Cookie 或 URL 参数的形式传递,确保在同一个会话期间,用户的多次请求都关联到同一个 HttpSession 对象。

会话有效期:HttpSession 的默认过期时间(或称超时时间)通常是 30 分钟(即 1800 秒),可以通过配置文件或编程方式进行调整。如果在这个时间段内用户没有进行任何操作(没有新的请求),会话就会失效。

会话过期后:一旦会话过期,HttpSession 中保存的数据(如用户信息、购物车等)将被清除。用户下一次发起请求时,会创建一个新的会话,且之前的数据不再可用。

使用示例:判断用户是否登录

当用户登录成功后,服务器会将用户的一些关键信息(如用户名、用户 ID 等)存储在 HttpSession 中,作为用户已登录的标志。在之后的请求中,服务器会先检查 HttpSession 中是否存在这些信息。如果能够取出这些信息,说明用户已经登录,可以正常处理请求;如果无法取出,说明用户未登录或会话已失效,需要用户重新登录。

@RestController
@RequestMapping("/api")
public class SessionController {

    /**
     * 使用 HttpSession 保存和获取会话数据
     * @param session HttpSession 对象,必须传入以便操作会话数据
     * @return 响应信息
     */
    @GetMapping("/set-session")
    public String setSession(HttpSession session) {
        // 设置会话属性
        session.setAttribute("username", "JohnDoe");
        return "Session data set";
    }

    @GetMapping("/get-session")
    public String getSession(HttpSession session) {
        // 获取会话属性
        String username = (String) session.getAttribute("username");
        if (username == null) {
            return "No session data found";
        }
        return "Session data: " + username;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

关键点:

  • session.setAttribute("key", value):设置会话属性。
  • session.getAttribute("key"):获取会话属性。

# 3.1 配置会话超时时间

你可以通过在 Spring Boot 的配置文件中调整会话的超时时间,例如:

在 application.properties 中配置:

server.servlet.session.timeout=20m  # 设置会话超时时间为 20 分钟
1

在 application.yml 中配置:

server:
  servlet:
    session:
      timeout: 20m  # 设置会话超时时间为 20 分钟
1
2
3
4

你也可以在代码中动态设置会话的超时时间:

@RequestMapping("/set-session-timeout")
public String setSessionTimeout(HttpSession session) {
    session.setMaxInactiveInterval(1200); // 设置会话超时时间为 1200 秒(20 分钟)
    return "Session timeout set to 20 minutes";
}
1
2
3
4
5

# 3.2 session自动验证流程

提示

HttpSession session 是当前用户的会话:当服务器验证 JSESSIONID 成功后,服务器会将这个 JSESSIONID 对应的 HttpSession 对象提供给当前请求。此时,你在控制器方法中使用的 HttpSession session 就是当前用户的会话对象,包含该用户的会话数据。

::

  1. 第一次登录时创建 Session:

    • 当用户第一次登录时,服务器会为该用户创建一个新的 HttpSession 对象,并生成一个唯一的 Session ID。这个 Session ID 会通过 Set-Cookie 响应头返回给客户端,通常以 JSESSIONID 为键名。
    • 浏览器会将这个 Session ID 保存到 Cookie 中。

    示例响应头:

    Set-Cookie: JSESSIONID=abc123def456ghi789; Path=/; HttpOnly
    
    1
  2. 后续请求中自动携带 Session ID:

    • 在后续的每次请求中,浏览器会自动在请求头中携带这个 JSESSIONID,服务器收到请求后会检查这个 Session ID。

    示例请求头:

    Cookie: JSESSIONID=abc123def456ghi789
    
    1
  3. 服务器验证 Session ID:

    • 服务器接收到请求后,会根据 JSESSIONID 查找对应的 HttpSession 对象。通过这个 HttpSession 对象,服务器可以判断用户是否已登录,并获取会话中存储的数据(如用户信息、权限等)。

    如果 JSESSIONID 有效且在服务器的会话存储中存在对应的 HttpSession,则服务器会将请求与该用户的会话关联起来。

  4. 会话有效性:

    • 如果 Session ID 无效(如会话超时、Session ID 被篡改或伪造),服务器会认为当前请求是一个新的会话,不会将其与之前的登录状态关联。此时,用户可能需要重新登录。

注意事项

一个浏览器通常只能为一个应用拥有一个 Session ID,这意味着同一个浏览器窗口(或标签页)中,无法同时登录同一个应用的多个账号。

# 4. Cookie 的使用

Cookie 可以在客户端保存少量数据,如用户偏好设置、会话标识等。Spring Boot 提供了对 Cookie 的支持,可以方便地读取和设置 Cookie。

使用示例:

@RestController
@RequestMapping("/api")
public class CookieController {

    /**
     * 设置 Cookie
     * @param response HttpServletResponse 对象,必须传入以便设置 Cookie
     * @return 响应信息
     */
    @GetMapping("/set-cookie")
    public String setCookie(HttpServletResponse response) {
        // 创建 Cookie 对象
        Cookie cookie = new Cookie("userToken", "123456");
        cookie.setMaxAge(7 * 24 * 60 * 60); // 设置 Cookie 的有效期为 7 天
        cookie.setHttpOnly(true); // 设置 HttpOnly 防止 XSS 攻击
        cookie.setPath("/"); // 设置路径

        // 将 Cookie 添加到响应中
        response.addCookie(cookie);
        return "Cookie set successfully";
    }

    /**
     * 获取 Cookie
     * @param request HttpServletRequest 对象,必须传入以便获取 Cookie
     * @return 响应信息
     */
    @GetMapping("/get-cookie")
    public String getCookie(HttpServletRequest request) {
        // 获取所有的 Cookie
        Cookie[] cookies = request.getCookies();
        if (cookies != null) {
            for (Cookie cookie : cookies) {
                if ("userToken".equals(cookie.getName())) {
                    return "User token: " + cookie.getValue();
                }
            }
        }
        return "No user token found";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

关键点:

  • new Cookie("name", "value"):创建一个新的 Cookie。
  • response.addCookie(cookie):将 Cookie 添加到响应中。
  • request.getCookies():获取所有的 Cookie。

# 三、使用 Servlet 过滤器(Filter)

在 Spring Boot 中,还可以通过过滤器来拦截请求和响应,进行一些预处理或后处理,如记录日志、处理跨域请求等。

配置过滤器的示例:

@Component
public class CustomFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 在请求到达 Controller 之前执行
        System.out.println("Request URL: " + httpRequest.getRequestURL());
        System.out.println("Request Method: " + httpRequest.getMethod());

        // 继续执行下一个过滤器或目标资源
        chain.doFilter(request, response);

        // 在响应返回给客户端之前执行
        System.out.println("Response Status: " + httpResponse.getStatus());
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

关键点:

  • doFilter 方法:doFilter 是过滤器的核心方法,用于拦截请求和响应。chain.doFilter(request, response) 用于继续执行下一个过滤器或目标资源。
  • 应用场景:过滤器通常用于日志记录、安全验证、请求拦截等场景。

# 四、使用 Servlet 监听器(Listener)

监听器可以监听应用生命周期事件、会话创建与销毁事件、请求的创建与销毁事件等。

配置监听器的示例:

@Component
public class CustomListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("应用启动时执行");
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
        System.out.println("应用停止时执行");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

关键点:

  • ServletContextListener:监听应用启动和销毁事件。
  • contextInitialized 和 contextDestroyed 方法:分别在应用启动和停止时执行。

在 Spring Boot 中的拦截器中使用 Servlet API 是一个常见的需求,特别是在需要对请求进行预处理(如权限验证、日志记录等)时。由于拦截器本质上是处理 HTTP 请求的一个切面,所以可以轻松地在其中使用 HttpServletRequest、HttpServletResponse 等 Servlet API。

# 五、在拦截器中使用 ServletAPI

在 Spring Boot 中,拦截器通常通过实现 HandlerInterceptor 接口来实现。HandlerInterceptor 提供了三个方法:preHandle、postHandle 和 afterCompletion,分别用于在请求处理之前、请求处理之后以及请求完成时执行逻辑。在这些方法中可以直接使用 HttpServletRequest 和 HttpServletResponse。

# 1. 实现拦截器

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class MyInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 在请求处理前进行逻辑处理
        System.out.println("拦截器 - 请求路径: " + request.getRequestURI());
        System.out.println("拦截器 - 请求方法: " + request.getMethod());

        // 例如:检查请求头中的认证信息
        String authHeader = request.getHeader("Authorization");
        if (authHeader == null || !authHeader.startsWith("Bearer ")) {
            // 如果认证信息不正确,返回 401 错误
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("Unauthorized access");
            return false; // 拒绝继续处理请求
        }

        return true; // 继续处理请求
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, org.springframework.web.servlet.ModelAndView modelAndView) throws Exception {
        // 请求处理后但在视图渲染之前执行
        System.out.println("拦截器 - 请求处理完成后执行");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        // 请求完成后执行,通常用于资源清理等操作
        System.out.println("拦截器 - 请求完成后执行");
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

# 2. 注册拦截器

为了让拦截器生效,需要将其注册到 Spring Boot 的拦截器链中,通常通过实现 WebMvcConfigurer 并重写 addInterceptors 方法来实现。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class WebConfig implements WebMvcConfigurer {

    @Autowired
    private MyInterceptor myInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册自定义拦截器并指定拦截路径
        registry.addInterceptor(myInterceptor)
                .addPathPatterns("/api/**") // 拦截 /api/ 下的所有请求
                .excludePathPatterns("/api/login"); // 排除某些路径
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  • preHandle:请求处理前执行。这里是使用 HttpServletRequest 和 HttpServletResponse 的主要方法,可以获取请求路径、请求头、请求参数等信息,并根据需要做出响应。例如,如果发现请求中缺少必要的认证信息,可以直接通过 response 返回错误并终止请求处理。

  • postHandle:请求处理后但在视图渲染之前执行,适合在控制器方法执行后添加一些额外的处理逻辑。

  • afterCompletion:请求完成后执行,适合用于资源清理或记录日志等操作。

# 五、使用ServletContext

提示

简单来说,ServletContext 就是一个全局容器,它可以在整个 Web 应用的范围内存储和共享数据,供应用的所有组件(如控制器、过滤器、监听器等)使用。在 Spring Boot 项目中,你可以在任意一个文件中获取和设置这些共享数据。

ServletContext 是一个全局范围的对象,在整个 Web 应用中共享。常见的用途包括:

  1. 在应用范围内共享数据:可以将数据存储在 ServletContext 中,供整个应用程序的所有组件(如 Controllers、Filters、Listeners 等)使用。
  2. 获取 Web 应用的元数据:如应用的上下文路径、服务器信息、Servlet 版本等。

# 1. 在应用范围内共享数据

ServletContext 允许在整个应用范围内共享数据,例如应用启动时加载的一些配置信息,或者需要在不同组件间共享的全局数据。

代码示例:

@RestController
@RequestMapping("/api")
public class ContextController {

    @Autowired
    private ServletContext servletContext;

    /**
     * 设置全局共享数据
     */
    @GetMapping("/set-context")
    public String setContextData() {
        // 将全局数据存储在 ServletContext 中
        servletContext.setAttribute("appConfig", "This is a global config data.");
        return "Context data set successfully.";
    }

    /**
     * 获取全局共享数据
     */
    @GetMapping("/get-context")
    public String getContextData() {
        // 从 ServletContext 中获取共享数据
        String appConfig = (String) servletContext.getAttribute("appConfig");
        return appConfig != null ? "Context data: " + appConfig : "No context data found.";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

关键点:

  • servletContext.setAttribute("key", value):设置应用范围的共享数据,key 为数据标识符,value 为共享数据。
  • servletContext.getAttribute("key"):获取应用范围的共享数据,通过 key 获取存储的数据。
  • 应用场景:适用于需要在整个应用生命周期内共享的全局配置、数据缓存等,例如全局配置参数、系统状态信息等。

# 2. 获取 Web 应用的元数据

通过 ServletContext,可以获取应用的元数据信息,例如应用的上下文路径、服务器名称、服务器信息等。这些信息通常用于应用监控、日志记录、版本管理等场景。

代码示例:

@RestController
@RequestMapping("/api")
public class MetadataController {

    @Autowired
    private ServletContext servletContext;

    /**
     * 获取应用的元数据信息
     */
    @GetMapping("/metadata")
    public String getAppMetadata() {
        // 获取应用的上下文路径
        String contextPath = servletContext.getContextPath();
        // 获取服务器信息
        String serverInfo = servletContext.getServerInfo();
        // 获取 Servlet API 的主版本号和次版本号
        int majorVersion = servletContext.getMajorVersion();
        int minorVersion = servletContext.getMinorVersion();

        return String.format("Context Path: %s, Server Info: %s, Servlet Version: %d.%d",
                contextPath, serverInfo, majorVersion, minorVersion);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

关键点:

  • servletContext.getContextPath():获取应用的上下文路径,通常用于生成完整的 URL 或路径映射。
  • servletContext.getServerInfo():获取服务器信息,如服务器名称和版本,这在监控和日志中尤为常用。
  • servletContext.getMajorVersion() 和 servletContext.getMinorVersion():获取当前应用支持的 Servlet API 版本,便于与应用的兼容性进行检查。

# 3. ServletContext 的局限性

尽管 ServletContext 在某些全局数据共享场景下仍然有效,但在 Spring Boot 中,推荐使用更现代化的方式进行全局配置管理和共享数据,例如:

  • 使用 @Value 或 Environment 读取配置。
  • 使用 Spring 的 ApplicationContext 进行全局 Bean 管理。
  • 使用 Redis、Ehcache 等缓存工具进行全局数据共享和缓存。

# 六、SpringBoot推荐的写法

在 Spring Boot 项目中,虽然底层仍然是基于 Servlet API 构建的,因此 ServletContext 的这些功能依然可以使用,但在 Spring Boot 提供的更高级和现代化的方案下,通常不建议直接使用这些低层次的 API。以下是具体的情况:

# 1. 获取 Web 应用的真实路径

在 Spring Boot 中,虽然你依然可以使用 ServletContext.getRealPath("/") 获取应用的真实路径,但由于 Spring Boot 的应用通常是打包成 JAR 文件运行,getRealPath 返回的路径可能不是你期望的文件系统路径,而是在 JAR 包内部的路径。因此,这种方式在 Spring Boot 中使用时需要谨慎。

推荐的替代方案:使用 Resource 和 ResourceLoader 来访问资源。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

@RestController
public class ResourceController {

    @Autowired
    private ResourceLoader resourceLoader;

    @GetMapping("/load-file")
    public String loadFile() {
        // 使用 ResourceLoader 来加载资源
        Resource resource = resourceLoader.getResource("classpath:static/resource.txt");
        try {
            if (resource.exists()) {
                return "Resource loaded successfully. Path: " + resource.getURI();
            } else {
                return "Resource not found.";
            }
        } catch (IOException e) {
            return "Error loading resource: " + e.getMessage();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

优势:

  • Spring 提供的 Resource 和 ResourceLoader 更加灵活,可以处理多种资源类型(如文件系统、类路径、URL 等)。
  • 在打包成 JAR 后,仍然可以正确加载资源。

# 2. 加载 Web 应用的资源

在 Spring Boot 项目中,如果你需要加载类路径或其他位置的资源,使用 Spring 提供的 Resource 和 ResourceLoader 是最佳实践。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

@RestController
public class ConfigController {

    @Autowired
    private ResourceLoader resourceLoader;

    @GetMapping("/load-config")
    public String loadConfigFile() {
        Resource resource = resourceLoader.getResource("classpath:config/my-config.txt");
        try (BufferedReader reader = new BufferedReader(
                new InputStreamReader(resource.getInputStream(), StandardCharsets.UTF_8))) {
            StringBuilder content = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                content.append(line).append("\n");
            }
            return content.toString();
        } catch (IOException e) {
            return "Error loading resource: " + e.getMessage();
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

# 3. 获取应用的初始化参数

在 Spring Boot 中,初始化参数通常通过 application.properties 或 application.yml 文件配置,而不是直接通过 ServletContext 读取。

推荐的替代方案:使用 Spring 的 @Value 或 @ConfigurationProperties 注解读取配置。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ConfigController {

    @Value("${configParam:默认值}")
    private String configParam;

    @GetMapping("/config")
    public String getConfig() {
        return "Config Param: " + configParam;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

总结

  • Spring Boot 更推荐的做法:在 Spring Boot 项目中,推荐使用 Spring 提供的 ResourceLoader、@Value、@ConfigurationProperties 等方式来处理资源和配置,而不是直接依赖 ServletContext。
  • ServletContext 的适用场景:如果你需要与底层的 Servlet API 进行交互,或者你的项目中有一些遗留代码仍然依赖 ServletContext,那么这些 API 依然可以使用,但要注意与 Spring Boot 的集成问题。

在现代 Spring Boot 项目中,尽量避免直接使用 ServletContext 进行全局数据的管理或资源加载,而是使用 Spring 提供的更高级、更灵活的解决方案。

编辑此页 (opens new window)
上次更新: 2025/03/16, 22:19:39
对接第三方文件上传
HttpServletResponse 和 ResponseEntity

← 对接第三方文件上传 HttpServletResponse 和 ResponseEntity→

Theme by Vdoing | Copyright © 2019-2025 程序员scholar
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式