Tianyi's Blog Tianyi's Blog
首页
  • 计算机网络
  • 操作系统
  • 计算机科学
  • Nginx
  • Vue框架
  • 环境配置
  • Java
  • JVM
  • Spring框架
  • Redis
  • MySQL
  • RabbitMQ
  • Kafka
  • Mirror Sites
  • Dev Tools
  • Docker
  • Jenkins
  • Scripts
  • Windows
  • 科学上网
  • 旅行
  • 网站日记
  • 软件
  • 电子产品
  • 杂野
  • 分类
  • 友情链接
GitHub (opens new window)

Tianyi

一直向前,永不停止
首页
  • 计算机网络
  • 操作系统
  • 计算机科学
  • Nginx
  • Vue框架
  • 环境配置
  • Java
  • JVM
  • Spring框架
  • Redis
  • MySQL
  • RabbitMQ
  • Kafka
  • Mirror Sites
  • Dev Tools
  • Docker
  • Jenkins
  • Scripts
  • Windows
  • 科学上网
  • 旅行
  • 网站日记
  • 软件
  • 电子产品
  • 杂野
  • 分类
  • 友情链接
GitHub (opens new window)
  • Java

  • Golang

  • JVM的奇妙世界

  • Spring

    • SSM-Java开发工具链
    • SSM-IOC&AOP
    • MyBatis
    • MyBatis-Plus
    • mybatis搭建demo
    • SSM-JUnit、文件上传与拦截器
      • 什么是SSM
        • 组件通用套路
      • TestCase - Junit
        • pom file
        • Demo
      • File Upload
        • 基础步骤
      • Spring MVC拦截器
        • 拦截器最佳使用
        • Demo
        • applicationContext.xml
        • SecurityInterceptor
        • 与Filter Chain 区别
        • 拓展:参数白化(Parameter Allow List)
    • Spring基础
    • Spring Framework6
  • Spring增强封装

  • Redis

  • MySQL

  • RabbitMQ

  • Kafka

  • 分享

  • 后端
  • Spring
tianyi
2023-03-02
目录

SSM-JUnit、文件上传与拦截器

# 什么是SSM

SSM框架是三个核心组件的集成方案:

  1. Spring框架:提供IOC(控制反转)容器,统一管理所有对象的生命周期与依赖关系。
  2. Spring MVC:作为表现层框架,采用MVC架构模式,通过DispatcherServlet实现请求分发,有效隔离业务层、表现层与持久层。
  3. MyBatis:专注持久层的ORM框架,简化数据库操作,支持灵活SQL编写与结果映射。

# 组件通用套路

  1. 引入 pom 依赖,确保依赖之间没有版本冲突
  2. 第一步先确定我引入的组件的生效范围;然后是根据我们的想要的效果以及我们的需求去确定配置
  3. 确定是否遵循最佳实践?

# TestCase - Junit

在单元测试的实施过程中,我们首先需要在POM文件中添加相关依赖项,以确保测试框架的可用性。随后,着手编写测试类是关键步骤。

在测试类编写过程中,以下注解的合理使用至关重要:

  1. @BeforeClass 与 @AfterClass 用于在测试类执行前后进行全局操作,如数据库初始化或资源加载。这些方法仅在测试类初始化和终止时各执行一次。

  2. @Before 与 @After 应用于每个测试方法的执行前后,通常用于:

    • 数据库连接的获取与释放

    • 日志记录的预处理

    • 测试环境的准备与清理

  3. @Test 标记测试方法的核心注解,每个测试方法都需添加该注解以标识其测试性质。

    1. 测试执行完成后,我们不仅需要确认方法的正常运行,更重要的是验证结果是否符合预期。断言(Assert)机制在此阶段发挥重要作用,使用适当的断言类型(assertEquals, assertTrue等)

# pom file

    <!--Junit单元测试框架-->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
    </dependency>
    <!-- Spring-Test是Spring与Junit提供的整合包
        通过这个组件可以在Junit测试时自动对IOC容器进行初始化
     -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>3.1.0</version>
        <!-- scope = test 代表只在单元测试时才会加载这个依赖 -->
        <scope>test</scope>
    </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
配置项 作用
javax.servlet-api 提供 Servlet 接口的类定义,使测试代码能编译和运行。
<scope>test</scope> 确保依赖仅在测试阶段生效,避免与 Servlet 容器的实现冲突,同时减少生产包体积。

# Demo

import com.ssm.entity.Goods;
import org.junit.*;
import org.junit.runner.RunWith;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.ParseException;

import static org.junit.Assert.assertTrue;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class GoodsServiceTest {
    //@BeforeClass代表在初始化测试用例类前执行的操作
    //@BeforeClass必须是static静态方法
    @BeforeClass
    public static void init() {
        System.out.println("正在导入Goods表数据");
    }

    @Resource
    private GoodsService goodsService;


    @Before
    public void doBefore() {
        System.out.println("获取数据库Connection对象");
    }

    @After
    public void doAfter() {
        System.out.println("释放数据库Connection对象");
    }


    @Test
    //@Transactional默认在测试用例执行成功后,为了保证原始数据不被测试数据污染
    //自动进行回滚,要解决这个问题需要设置@Rollback(false)关闭自动回滚
    @Transactional
    @Rollback(false)
    public void initGoods() throws ParseException {
        goodsService.initGoods();
    }

    /**
     * 程序运行成功 绿色对勾
     * 程序运行失败 红色叹号
     * 程序运行逻辑错误 黄色叉号
     */
    @Test
    public void findById() {

        Goods goods = goodsService.findById(9000);
        //Assert 断言,对程序产生的结果进行判断
        assertTrue("未找到Goods对象", goods!=null);
    }

    /*测试用例类执行完成后,执行的操作
    通常用于清除结果*/
    @AfterClass
    public static void destory() {
        System.out.println("清空Goods表数据");
    }
}
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67

# File Upload

文件上传开发套路 1.Maven依赖commons-fileupload 2.applicationContext配置multipartResolver 3.表单设置Post请求与form-data 4.Controller使用@RequestParam MultipartFile接收文件 5.利用MultipartFile.transferTo()方法实现文件另存为

一句话总结: Spring文件上传就是从表单post传文件,后端用MultipartFile接住存起来的事儿。

# 基础步骤

  1. 添加依赖
    Maven引入commons-fileupload库,用于处理文件上传: 【注】Spring MVC 5.x及以上版本已内置文件上传支持,可根据项目需求选择是否引入该依赖

    <dependency>
        <groupId>commons-fileupload</groupId>
        <artifactId>commons-fileupload</artifactId>
        <version>1.4</version>
    </dependency>
    
    1
    2
    3
    4
    5
  2. 配置multipartResolver
    在Spring的applicationContext.xml中定义Bean,并设置参数(如文件大小限制): 此配置使Spring具备处理二进制文件上传的能力

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="10485760"/> <!-- 10MB -->
        <property name="defaultEncoding" value="UTF-8"/>
    </bean>
    
    1
    2
    3
    4
  3. FE 表单

    在HTML表单中使用post请求,并设置编码类型: 表单需指定method="post"和enctype="multipart/form-data",并包含文件输入框:

    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit" value="上传">
    </form>
    
    1
    2
    3
    4

    image-20250302111354485

  4. Controller接收文件
    使用@RequestParam绑定上传文件到MultipartFile对象:

    @PostMapping("/upload")
    public String uploadFile(@RequestParam("file") MultipartFile file) {
        // 处理文件
    }
    
    1
    2
    3
    4
    • 确保参数名与表单文件域name属性一致
    • MultipartFile接口提供了获取文件信息的多种方法

    接收多个文件时,使用MultipartFile[]或多个@RequestParam参数:

    @RequestParam("files") MultipartFile[] files
    
    1
  5. 保存文件
    调用transferTo()将文件保存到服务器指定路径:

    String savePath = "/uploads/";
    File targetFile = new File(savePath + file.getOriginalFilename());
    file.transferTo(targetFile);
    
    1
    2
    3

# Spring MVC拦截器

Spring 拦截器的核心配置管理可分为两大维度:

1. 生命周期管控

  • 依赖管理:优先确保组件正确注入与顺序加载
  • 作用域控制:通过注册路径指定拦截的URL模式
  • 行为定制:可编程设置预处理策略/响应修改规则

2. 执行阶段模型 基于 HandlerInterceptor 的三阶段工作流:

  1. 预处理阶段:Controller方法执行前进行身份验证/参数校验
  2. 后处理阶段:Controller返回后修改模型数据/响应头
  3. 完成回调:视图渲染结束后处理资源清理/日志记录

# 拦截器最佳使用

Q1:过滤器改变Request参数后,Interceptor能否获取到新值? A:可以。因为在同一个线程中,包装后的Request对象会被后续组件共享。

Q2:拦截器的postHandle与afterCompletion的异常处理差异?

try {
    preHandle(); // 返回false则链终止
    handlerMethod.invoke();
    postHandle(); // 视图渲染前(若Controller抛出异常则不执行)
} catch (Exception ex) {
    // 进入已配置的异常处理流程
} finally {
    afterCompletion(); // 总是会执行(类似finally块)
}
1
2
3
4
5
6
7
8
9

# Demo

# applicationContext.xml


    <mvc:interceptors>
        <!--配置拦截器-->
        <mvc:interceptor>
            <!--拦截器拦截的地址,/**代表所有地址
            也可以书写/xxx某个具体的地址
            如果有多个URL需要拦截,可以写多个mvc:mapping标签-->
            <mvc:mapping path="/**"/>
            <mvc:mapping path="/login.html"/>
            <mvc:mapping path="/reg"/>
            <!--
                exclude-mapping是排除地址,必须以/开头,后面增加匹配字符串即可
                exclude-mapping通常是排除掉静态资源
            -->
            <mvc:exclude-mapping path="/**/*.ico"/>
            <mvc:exclude-mapping path="/**/*.jpg"/>
            <mvc:exclude-mapping path="/**/*.gif"/>
            <bean class="com.springmvc.interceptor.SecurityInterceptor"/>
        </mvc:interceptor>
        <!--某个URL遇到多个拦截器时,会按照配置顺序依次执行,如果前面preHandle没有通过,则会全部中断-->
        <mvc:interceptor>
            <mvc:mapping path="/**"/>
            <bean class="com.springmvc.interceptor.SecurityInterceptor1"/>
        </mvc:interceptor>
    </mvc:interceptors>
</beans>
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

# SecurityInterceptor

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class SecurityInterceptor implements HandlerInterceptor {
    public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception {
        System.out.println(request.getRequestURL() + "前置处理");
        String token = request.getHeader("token");
        if(token == null){
            response.sendError(403,"Token Not Found!");
            return false;
        }
        return true;
    }

    public void postHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println(request.getRequestURL() + "处理完成");
    }

    public void afterCompletion(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println(request.getRequestURL() + "已渲染输出");
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 与Filter Chain 区别

  • 过滤器:关注 协议层处理(如安全、编码、跨域),与业务逻辑无关。
  • 拦截器:关注 业务层处理(如参数校验、日志记录),与控制器紧密耦合。
  • 过滤器 和 拦截器 操作的是同一个 HttpServletRequest/Response 对象,但它们的 处理阶段、设计目的和职责范围 完全不同。
  • 处理的东西不同
    • Filter:在任何业务代码执行前处理
    • Interceptor:处理的是装饰后的Request对象
HTTP请求 --> 
   [Filter1] -> [Filter2] -> 
      [DispatcherServlet] -->
         [Interceptor1] -> [Interceptor2] -> 
            Controller
               ↓
         [Interceptor2] postHandle <- [Interceptor1] postHandle
      [DispatcherServlet]渲染视图
   [Filter2] doFilter后续处理
[Filter1] doFilter后续处理
HTTP响应
1
2
3
4
5
6
7
8
9
10
11

Spring Security 是否完全替代了拦截器?:不是。Spring Security 负责安全基础设施,拦截器仍可用于业务逻辑(如记录操作日志)。两者可协同工作。

# 拓展:参数白化(Parameter Allow List)

参数白化(Parameter Allow List):预定义合法参数名/参数结构的集合,所有请求参数需与白名单精确匹配才能进入处理流程。这是零信任安全模型的重要组成部分。指在一个系统或应用程序中,明确规定允许使用的参数集合。只有在这个白名单中的参数才被认为是合法的、可接受的,系统会对其进行正常处理;而不在白名单内的参数则会被视为非法或不可信的,可能会被系统拒绝、过滤或采取其他安全措施进行处理。

白化策略

策略类型 防御粒度 实现成本 动态能力 典型案例
静态接口白名单 接口级别 低 无 基础CMS系统
动态方法白名单 方法级别 中 部分 金融交易API
JSON Schema验证 参数结构 高 强 OpenAPI合规系统
参数名混淆 协议级别 很高 需客户端配合 移动端安全通信
完善页面 (opens new window)
mybatis搭建demo
Spring基础

← mybatis搭建demo Spring基础→

最近更新
01
JDK
02-23
02
BadTasteCode && 优化
09-11
03
Gradle 实践操作指南及最佳实践
09-11
更多文章>
Theme by Vdoing | Copyright © 2021-2025 Tandy | 粤ICP备2023113440号
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式