Spring MVC 核心功能实践指南:从数据传递到文件上传
本文基于个人学习笔记整理,涵盖了Spring MVC框架中数据传递、中文乱码处理、日期转换、拦截器配置、文件上传等核心功能的配置与使用方法。适合初学者快速查阅和实践。
一、数据传递(Controller → JSP)
Spring MVC提供了多种方式将数据从控制器传递到视图层:
1. 使用 ModelAndView
@RequestMapping("/example1")
public ModelAndView example1() {
ModelAndView mav = new ModelAndView();
mav.addObject("message", "Hello from ModelAndView!");
mav.setViewName("result");
return mav;
}
2. 使用 Model/ModelMap
@RequestMapping("/example2")
public String example2(Model model) {
model.addAttribute("message", "Hello from Model!");
return "result";
}
3. 返回JSON数据(@ResponseBody)
@ResponseBody
@RequestMapping("/api/data")
public Result findAccount() {
Result result = new Result();
result.setCode(200);
result.setData(accountService.findAll());
result.setMessage("查询成功");
return result; // 自动转换为JSON
}
注意 :使用@ResponseBody需要添加Jackson依赖:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
二、中文乱码解决方案
1. POST请求乱码处理
在web.xml中配置字符编码过滤器:
<!-- 字符编码过滤器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2. GET请求乱码处理
方案一:修改Tomcat配置(推荐)
在server.xml的Connector标签中添加:
<Connector port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8" />
方案二:代码中手动转换(临时方案)
// 不推荐,仅作了解
String param = request.getParameter("name");
String correctParam = new String(param.getBytes("ISO-8859-1"), "UTF-8");
三、自定义日期转换器
1. 创建日期转换器
@Component
public class DateConverter implements Converter<String, Date> {
private static final String[] patterns = {
"yyyy-MM-dd",
"yyyy-MM-dd HH:mm:ss",
"yyyy/MM/dd",
"yyyy/MM/dd HH:mm:ss"
};
@Override
public Date convert(String source) {
if (source == null || source.trim().isEmpty()) {
return null;
}
for (String pattern : patterns) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
sdf.setLenient(false); // 严格模式
return sdf.parse(source);
} catch (ParseException e) {
// 尝试下一个模式
}
}
throw new IllegalArgumentException("无法识别的日期格式: " + source);
}
}
2. 在Spring配置中注册
<!-- 方法一:传统XML配置 -->
<bean id="conversionService"
class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.example.converter.DateConverter"/>
</set>
</property>
</bean>
<mvc:annotation-driven conversion-service="conversionService"/>
<!-- 方法二:注解配置(推荐) -->
<!-- 在配置类中添加: -->
@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new DateConverter());
}
}
四、拦截器的使用与配置
1. 拦截器 vs 过滤器
| 特性 | 过滤器(Filter) | 拦截器(Interceptor) |
|---|---|---|
| 原理 | 基于Servlet回调函数 | 基于Spring AOP |
| 依赖 | Servlet容器 | Spring框架 |
| 作用范围 | 所有Web资源 | Spring MVC请求 |
| 典型应用 | 编码过滤、安全过滤 | 登录验证、权限控制、日志记录 |
2. 创建自定义拦截器
public class LoginInterceptor implements HandlerInterceptor {
/**
* 前置处理:在控制器执行前调用
*/
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
HttpSession session = request.getSession();
Object user = session.getAttribute("user");
if (user == null) {
// 未登录,重定向到登录页
response.sendRedirect(request.getContextPath() + "/login");
return false; // 中断请求
}
return true; // 放行
}
/**
* 后置处理:在控制器执行后,视图渲染前调用
*/
@Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler,
ModelAndView modelAndView) throws Exception {
// 可修改ModelAndView
}
/**
* 完成处理:在视图渲染完成后调用
*/
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex) throws Exception {
// 资源清理
}
}
3. XML配置拦截器
<mvc:interceptors>
<!-- 全局拦截器(对所有请求生效) -->
<bean class="com.example.interceptor.GlobalInterceptor"/>
<!-- 路径匹配拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/admin/**"/>
<mvc:mapping path="/user/**"/>
<mvc:exclude-mapping path="/user/login"/>
<mvc:exclude-mapping path="/user/register"/>
<bean class="com.example.interceptor.LoginInterceptor"/>
</mvc:interceptor>
<!-- 权限拦截器 -->
<mvc:interceptor>
<mvc:mapping path="/admin/**"/>
<bean class="com.example.interceptor.AuthInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
4. Java Config配置拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/login", "/register", "/css/**", "/js/**");
registry.addInterceptor(new LogInterceptor())
.addPathPatterns("/api/**");
}
}
五、文件上传完整实现
1. 添加依赖
<!-- commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.4</version>
</dependency>
<!-- commons-io -->
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.11.0</version>
</dependency>
2. Spring MVC配置
<!-- 配置文件上传解析器(id必须为multipartResolver) -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 最大上传大小 5MB -->
<property name="maxUploadSize" value="5242880" />
<!-- 内存中最大文件大小 1MB -->
<property name="maxInMemorySize" value="1048576" />
<!-- 默认编码 -->
<property name="defaultEncoding" value="UTF-8" />
<!-- 上传文件临时目录 -->
<property name="uploadTempDir" value="/tmp" />
</bean>
<!-- 静态资源映射 -->
<mvc:resources mapping="/upload/**" location="file:${upload.base.dir}/" />
3. 前端表单
<form action="/file/upload" method="post" enctype="multipart/form-data">
<div>
<label>选择文件:</label>
<input type="file" name="file" multiple>
</div>
<div>
<label>描述:</label>
<input type="text" name="description">
</div>
<button type="submit">上传</button>
</form>
4. 控制器处理
@Controller
@RequestMapping("/file")
public class FileUploadController {
// 从配置文件中读取上传路径
@Value("${upload.path}")
private String uploadPath;
/**
* 单文件上传
*/
@PostMapping("/upload")
public String uploadFile(@RequestParam("file") MultipartFile file,
@RequestParam("description") String description,
Model model) {
if (file.isEmpty()) {
model.addAttribute("message", "请选择要上传的文件");
return "error";
}
try {
// 1. 生成唯一文件名
String originalFilename = file.getOriginalFilename();
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf("."));
String newFilename = UUID.randomUUID().toString() + fileExtension;
// 2. 创建目标文件
File dest = new File(uploadPath + File.separator + newFilename);
// 3. 确保目录存在
if (!dest.getParentFile().exists()) {
dest.getParentFile().mkdirs();
}
// 4. 保存文件
file.transferTo(dest);
// 5. 返回结果
model.addAttribute("filename", newFilename);
model.addAttribute("originalFilename", originalFilename);
model.addAttribute("size", file.getSize());
model.addAttribute("contentType", file.getContentType());
return "upload-success";
} catch (IOException e) {
e.printStackTrace();
model.addAttribute("message", "文件上传失败: " + e.getMessage());
return "error";
}
}
/**
* 多文件上传
*/
@PostMapping("/upload-multiple")
public String uploadMultipleFiles(@RequestParam("files") MultipartFile[] files,
Model model) {
List<String> uploadedFiles = new ArrayList<>();
for (MultipartFile file : files) {
if (!file.isEmpty()) {
try {
String filename = saveFile(file);
uploadedFiles.add(filename);
} catch (IOException e) {
model.addAttribute("message", "文件保存失败: " + e.getMessage());
return "error";
}
}
}
model.addAttribute("uploadedFiles", uploadedFiles);
return "upload-multiple-success";
}
/**
* 文件下载
*/
@GetMapping("/download/{filename}")
public ResponseEntity<Resource> downloadFile(@PathVariable String filename) {
try {
File file = new File(uploadPath + File.separator + filename);
Resource resource = new FileSystemResource(file);
return ResponseEntity.ok()
.header(HttpHeaders.CONTENT_DISPOSITION,
"attachment; filename=\"" + file.getName() + "\"")
.contentType(MediaType.APPLICATION_OCTET_STREAM)
.body(resource);
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
private String saveFile(MultipartFile file) throws IOException {
String originalFilename = file.getOriginalFilename();
String extension = originalFilename.substring(originalFilename.lastIndexOf("."));
String newFilename = System.currentTimeMillis() + extension;
File dest = new File(uploadPath + File.separator + newFilename);
file.transferTo(dest);
return newFilename;
}
}
5. 配置文件上传路径
在application.properties中配置:
roperties
properties
# 文件上传路径
upload.path=D:/uploads
# 或者使用相对路径
# upload.path=./uploads
# 最大文件大小
spring.servlet.multipart.max-file-size=5MB
spring.servlet.multipart.max-request-size=10MB
六、常见问题与解决方案
问题1:文件大小限制异常
错误信息 :org.apache.commons.fileupload.FileUploadBase$SizeLimitExceededException
解决方案:
-
在
multipartResolver中配置更大的maxUploadSize -
或使用Spring Boot的配置项:
spring.servlet.multipart.max-file-size=10MB
spring.servlet.multipart.max-request-size=20MB
问题2:临时目录权限不足
解决方案:明确指定临时目录
@Bean
public MultipartConfigElement multipartConfigElement() {
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setLocation("/tmp/upload");
return factory.createMultipartConfig();
}
问题3:中文文件名乱码
解决方案:在表单中设置编码
<form ... accept-charset="UTF-8">
或在控制器中处理:
// 对文件名进行URL编码
String encodedFilename = URLEncoder.encode(originalFilename, "UTF-8").replaceAll("\\+", "%20");
response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + encodedFilename);
总结
本文通过实际代码示例,详细介绍了Spring MVC中几个核心功能的实现方式:
-
数据传递:掌握三种常用方式,灵活选择
-
中文处理:区分GET/POST,一劳永逸解决乱码
-
日期转换:自定义转换器,简化参数绑定
-
拦截器:理解与过滤器的区别,实现AOP功能
-
文件上传:完整流程,包含异常处理和最佳实践