目录
1、SpringMVC简介
1.1、SpringMVC概述
1.2、SpringMVC快速入门
1.2.1、开发步骤
1.2.2、流程分析
1.3、SpringMVC的组件解析
1.4、Controller加载控制与业务bean加载控制
2、请求
2.1、请求映射路径
2.2、获得请求头
2.2.1、@RequestHeader
2.2.2、@CookieValue
2.3、请求方式
2.3.1、Get / Post 请求传参 & Post请求中文乱码处理
2.4、请求参数
2.4.1、获得普通类型参数
2.4.2、获得POJO类型参数(简单JavaBean)
2.4.3、获得嵌套POJO类型参数
2.4.4、获得数组类型参数
2.4.5、获得集合类型参数
2.5、请求参数——传递json数据
2.5.1、集合参数
2.5.2、POJO参数
2.5.3、集合+POJO参数
2.6、请求参数——日期类型参数传递
2.6.1、类型转换器
2.7、@RequestBody、@RequestParam、@PathVariable的区别
3、响应
3.1、SpringMVC的数据响应方式
3.1.1、响应页面
3.1.2、响应文本数据(字符串)
3.1.3、响应json数据(对象集合转json数组)
3.2、类型转换器HttpMessageConverter
4、配置对静态资源的访问放行
5、自定义类型转换器
6、获得Servlet相关API
7、REST风格
7.1、简介
7.2、入门案例
7.3、RESTful快速开发
8、SSM整合
8.1、SSM整合流程
8.1.1、导入相关坐标
8.1.2、SSM整合
8.1.3、功能模块
8.1.4、测试
8.2、表现层数据封装
9、异常处理
9.1、异常处理的思路
9.2、异常处理的两种方式
9.2.1、简单异常处理器SimpleMappingExceptionResolver
9.2.2、自定义异常处理器
9.3、Spring中的异常处理器
9.4、项目异常处理方案
10、拦截器
10.1、拦截器的概念
10.2、拦截器和过滤器的区别
10.3、拦截器入门案例
10.4、拦截器HandlerInterceptor接口方法说明
10.5、拦截器执行流程
10.6、拦截器参数
10.7、多拦截器执行顺序
10.7.1、拦截器链配置方式
10.7.2、拦截器链的运行顺序
11、文件上传
11.1、文件上传三要素
11.2、文件上传原理
11.3、单文件上传步骤
11.4、多文件上传实现
SpringMVC与Servlet技术功能等同,是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于SpringFrameWork的后续产品,已经融合在Spring Web Flow中。
SpringMVC已经成为目前最主流的MVC框架之一,并且随着Spring 3.0 的发布,全面超越Struts2,成为最优秀的MVC框架。它通过一套注解,让一个简单的Java类称为处理请求的控制器,而无需实现任何接口。同时,他还支持RESTful编程风格的请求。
SpringMVC的优点:相比Servlet使用更简单,开发更便捷,灵活性强。
需求:客户端发起请求,服务器端接收请求,执行逻辑并进行视图跳转。
pom.xml:
...
javax.servlet
javax.servlet-api
3.1.0
provided
org.springframework
spring-webmvc
5.2.10.RELEASE
...
SSM
org.apache.tomcat.maven
tomcat7-maven-plugin
2.1
80
/
@Controller //将控制器加入到容器中
@RequestMapping("/user")
public class UserController {
// 请求地址: http://localhost:80/user/login ,访问时会执行该方法
@RequestMapping(value="/login",method = RequestMethod.GET,params = {"username"})
public String save(){
System.out.println("Controller save running....");
return "success.jsp"; //跳转到success.jsp
}
// 设置当前操作的访问路径
@RequestMapping("/save")
// 设置当前操作的返回值类型
@ResponseBody
public String save() {
System.out.println("user save ...");
return "{'module':'springmvc'}";
}
}
/**
* 创建SpringMVC的配置文件,加载controller对应的bean
*/
@Configuration
@ComponentScan("com.clp.controller")
public class SpringMvcConfig {
}
AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类。
AbstractDispatcherServletInitializer提供3个接口方法供用户实现:
createRootApplicationContext()方法:
如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行。使用方式同createServletApplicationContext()
/**
* 定义一个servlet容器启动的配置类,在里面加载spring的配置
*/
public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer {
// 加载SpringMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
// 设置哪些请求归属SpringMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; // 所有请求归SpringMVC进行处理
}
// 加载Spring容器的配置
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
}
上述的简化配置:
class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
@Override
protected Class>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
}
/*
注:在web.xml中配置的方式:
DispatcherServlet
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:spring-mvc.xml
1
DispatcherServlet
/
*/
启动服务器初始化过程:
单次请求过程:
因为功能不同,如何避免Spring错误地加载到SpringMVC的bean?——加载Spring控制的bean的时候排除掉SpringMVC控制的bean。
- @ComponentScan注解属性:
excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes);
includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)。
@Configuration
@ComponentScan(value="com.clp",
excludeFilters = @ComponentScan.Filter(
type = FilterType.ANNOTATION, // 按注解过滤
classes = Controller.class // 排除使用@Controller注解的类
)
)
@EnableTransactionManagement
public class SpringConfig {
}
@RequestMapping
作用:用于建立请求URL和处理请求方法之间的对应关系;
作用位置:
类上:请求URL的第一级访问目录,此处不写的话,就相当于应用的根目录。
方法上:请求URL的第二级访问目录,与类上的使用@RequestMapping标注的一极目录一起组成访问虚拟路径。
属性:
value:用于指定请求的URL,它和path属性的作用是一样的。
method:用于指定请求的方式。
params:用于指定限制请求参数的条件,它支持简单的表达式。要求请求参数的key和value必须和配置一模一样。
/**
* 定义Controller
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save() {
System.out.println("user save ...");
return "{'module':'user save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String dalete() {
System.out.println("user delete ...");
return "{'module':'user delete'}";
}
}
浏览器访问:http://127.0.0.1/user/save,后端会执行save()方法
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)。@RequestHeader注解的属性如下:
@RequestMapping(value="/quick20")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public void save20(@RequestHeader(value="User-Agent",required = false) String user_agent) throws IOException {
System.out.println(user_agent);
}
使用@CookieValue可以获得指定Cookie的值。@CookieValue注解的属性如下:
@RequestMapping(value="/quick21")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public void save21(@CookieValue(value="JSESSIONID") String jsessionid) throws IOException {
System.out.println(jsessionid);
}
@Configuration
@ComponentScan("com.clp.controller")
public class SpringMvcConfig {
}
/*********************************************************************************/
/**
* 定义一个servlet容器启动的配置类,在里面加载spring的配置
*/
public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer {
// 加载SpringMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
// 设置哪些请求归属SpringMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; // 所有请求归SpringMVC进行处理
}
// 加载Spring容器的配置
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
@Override
protected Filter[] getServletFilters() {
// Post请求中文乱码处理:配置字符过滤器,为web容器添加过滤器并指定字符集,spring-web包中提供了专用的字符过滤器
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
/*********************************************************************************/
/**
* 定义Controller
*/
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/save")
@ResponseBody
public String save() {
System.out.println("user save ...");
return "{'module':'user save'}";
}
@RequestMapping("/delete")
@ResponseBody
public String dalete() {
System.out.println("user delete ...");
return "{'module':'user delete'}";
}
/**
* Get请求:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数。
* 例如:http://127.0.0.1/user/commonParam?name=zhangsan&age=18
* @param name
* @param age
* @return
*/
@RequestMapping("/commonParam")
@ResponseBody
public String commonParam(String name, int age) {
System.out.println("普通参数传递 name:" + name + ", age: " + age);
return "{'module':'common param'}";
}
}
注:通过配置方式获得过滤器:
web.xml:
...
CharacterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
CharacterEncodingFilter
/*
...
服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:
参数种类:
普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数。
Controller的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显式地绑定。注解@RequestParam有如下参数可以使用:
/*
* 普通参数:请求参数名与形参名不同
* 例如:http://127.0.0.1/user/commonParamDifferentName?name=zhangsan&age=18
*/
@RequestMapping("/commonParamDifferentName")
@ResponseBody
// @RequestParam的作用是绑定Get请求参数与形参的对应关系
public String commonParamDifferentName(@RequestParam("name") String userName, int age) {
System.out.println("【普通参数传递】 userName:" + userName + ", age:" + age);
return "'module':'common param different name'";
}
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
public class User {
private String name;
private int age;
// getter()/setter()...
}
/*
* POJO参数
* 例如:http://127.0.0.1/user/commonParamDifferentName?name=zhangsan&age=18
* */
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user) {
System.out.println("【pojo参数传递】 user:" + user);
return "{'module':'pojo param'}";
}
public class Address {
private String city;
private String province;
// getter & setter
}
public class User {
private String name;
private int age;
private Address address;
// getter & setter
}
/*
* 嵌套POJO参数
* 例如:http://127.0.0.1/user/commonParamDifferentName?name=zhangsan&age=18&address.city=beijing&address.province=beijing
* */
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user) {
System.out.println("【pojo参数传递】 user:" + user);
return "{'module':'pojo param'}";
}
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
/**
* 数组参数
* 例如:http://127.0.0.1/user/arrayParam?likes=game&likes=music&likes=travel
*/
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes) {
System.out.println("【数组参数传递】 likes:" + Arrays.toString(likes));
return "{'module':'array param'}";
}
获得集合参数时,一定要在参数前添加@RequestParam注解。
/**
* 集合参数
* 例如:http://127.0.0.1/user/listParam?likes=game&likes=music&likes=travel
*/
@RequestMapping("listParam")
@ResponseBody
public String listParam(@RequestParam List likes) {
System.out.println("【集合参数传递】 likes:" + likes);
return "{'module':'list param'}";
}
解析json数据需要先导入相关坐标:
com.fasterxml.jackson.core
jackson-databind
2.9.0
再在SpringMVC核心配置类中进行如下配置,开启自动转换json数据的支持(@EnableWebMVC注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换):
@Configuration
@ComponentScan("com.clp.controller")
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig {
}
/**
* 集合参数:json格式
* 例如:http://127.0.0.1/user/listParamForJson,请求体:["game", "music", "travel"]
*/
@RequestMapping("/listParamForJson")
@ResponseBody
// @RequestBody的作用是将请求体的内容映射为对应的对象
public String listParamForJson(@RequestBody List likes) {
System.out.println("【list param - json 参数传递】 likes:" + likes);
return "{'module':'list param - json'}";
}
/**
* 集合参数:json格式
* 例如:http://127.0.0.1/user/pojoParamForJson,请求体:{"name":"zhangsan","age":"18","address":{"province":"beijing","city":"beijing"}}
*/
@RequestMapping("/pojoParamForJson")
@ResponseBody
// @RequestBody的作用是将请求体的内容映射为对应的对象
public String pojoParamForJson(@RequestBody User user) {
System.out.println("【pojo param - json 参数传递】 likes:" + user);
return "{'module':'pojo param - json'}";
}
/**
* 集合参数:json格式
* 例如:http://127.0.0.1/user/listPojoParamForJson,请求体:[{"name":"zhangsan","age":"18"},{"name":"lisi","age":"20"}]
*/
@RequestMapping("listPojoParamForJson")
@ResponseBody
// @RequestBody的作用是将请求体的内容映射为对应的对象
public String listPojoParamForJson(@RequestBody List userList) {
System.out.println("【list pojo param - json 参数传递】 likes:" + userList);
return "{'module':'list pojo param - json'}";
}
日期类型数据基于系统不同格式也不尽相同。
/**
* 日期参数
* 例如:http://127.0.0.1/user/dateParam?date1=2088/08/08&date2=2088-08-18&date3=2088/08/28 8:08:08
*/
@RequestMapping("/dateParam")
@ResponseBody
// 接收形参时,根据不同的日期格式设置不同的接收方式
// @DateTimeFormat用于指定日期时间型数据格式。属性pattern:日期时间格式字符串
public String dateParam(Date date1,
@DateTimeFormat(pattern = "yyyy-MM-dd") Date date2,
@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss") Date date3) {
System.out.println("【参数传递】 date1:" + date1);
System.out.println("【参数传递】 date2:" + date2);
System.out.println("【参数传递】 date3:" + date3);
return "{'module':'date param'}";
}
Converter接口:
public interface Converter {
@Nullable
T convert(S var1);
}
- 请求参数年龄数据(String -> Integer);
- 日期格式转换(String -> Date)。
区别:
应用:
1、页面跳转
直接返回字符串
通过ModelAndView对象返回
2、回写数据
直接返回字符串
返回对象或集合
1、返回字符串形式
直接返回字符串“此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转。
/**
* 响应页面 / 跳转页面
*/
@RequestMapping("/toJumpPage")
public String toJumpPage() {
System.out.println("跳转页面");
return "page.jsp";
}
2、返回ModelAndView对象
package com.itheima.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
@Controller //将控制器加入到容器中
@RequestMapping("/user")
public class UserController {
// 请求地址: http://localhost:80/user/login ,访问时会执行该方法,要求使用GET方式请求
@RequestMapping(value="/login",method = RequestMethod.GET,params = {"username"})
public String save(){
System.out.println("Controller save running....");
return "/success"; //跳转到success.jsp( / 代表在当前web应用下寻找资源)
}
@RequestMapping(value="/quick2")
public ModelAndView save2() {
/**
* Model:模型,作用封装数据
* View:视图,作用展示数据
*/
ModelAndView modelAndView = new ModelAndView();
//设置模型数据(放入request域)
modelAndView.addObject("username","zhangsan");
//设置视图名称
modelAndView.setViewName("success");
return modelAndView; //请求转发
}
@RequestMapping(value="/quick3")
public ModelAndView save3(ModelAndView modelAndView) {
//设置模型数据(放入request域)
modelAndView.addObject("username","zhangsan");
//设置视图名称
modelAndView.setViewName("success");
return modelAndView; //请求转发
}
@RequestMapping(value="/quick4")
public String save4(Model model) {
model.addAttribute("username","lisi");
return "success"; //请求转发
}
@RequestMapping(value="/quick5")
public String save5(HttpServletRequest request) {
request.setAttribute("username","wangwu");
return "success"; //请求转发
}
}
Web基础阶段,客户端访问服务器端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print("hello world")即可,那么在Controller中想直接回写字符串该怎样呢?
@RequestMapping(value="/quick6")
public void save6(HttpServletResponse response) throws IOException {
response.getWriter().print("hello world");
}
@RequestMapping(value="/quick7")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public String save7() throws IOException {
return "hello world";
}
@RequestMapping(value="/quick8")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public String save8() throws IOException {
return "{'username':'zhangsan','age':'18'}";
}
@RequestMapping(value="/quick9")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public String save9() throws IOException {
User user = new User();
user.setUsername("zhangsan");
user.setAge(20);
//使用json的转换工具,将对象转换成json格式字符串,再返回
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(user);
return json;
}
/**
* 响应文本数据
*/
@RequestMapping("/toText")
@ResponseBody
public String toText() {
System.out.println("返回纯文本数据");
return "response text";
}
通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行如下配置:
spring-mvc.xml
...
...
/**
* 响应POJO对象
*/
@RequestMapping("/toJsonPojo")
@ResponseBody // 加上该注解后返回的对象自动转为json格式字符串
public User toJsonPojo() {
System.out.println("返回json对象数据");
User user = new User();
user.setName("zhangsan");
user.setAge(18);
return user;
}
/**
* 响应POJO集合对象
*/
@RequestMapping("/toJsonList")
@ResponseBody // 加上该注解后返回的对象自动转为json格式字符串
public List toJsonList() {
System.out.println("返回json集合数据");
User user1 = new User();
user1.setName("张三");
user1.setAge(18);;
User user2 = new User();
user2.setName("李四");
user2.setAge(20);;
List userList = Arrays.asList(user1, user2);
return userList;
}
在方法上添加@ResponseBody就可以返回json格式的字符串,但是这样配置比较麻烦,配置的代码比较多,因此,我们可以使用mvc的注解驱动代替上述配置。
spring-mvc.xml:
在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。使用
- HttpMessageConverter接口
public interface HttpMessageConverter {
boolean canRead(Class> clazz, @Nullable MediaType mediaType);
boolean canWrite(Class> clazz, @Nullable MediaType mediaType);
List getSupportedMediaTypes();
T read(Class extends T> clazz, HttpInputMessage inputMessage)
throws IOException, HttpMessageNotReadableException;
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
throws IOException, HttpMessageNotReadableException;
}
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
/**
* 放行 非SpringMvc的请求
* @param registry
*/
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
// 当访问/pages/????的时候不要走mvc,走/pages目录下的内容
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
// 其他静态资源
registry.addResourceHandler("/js/**").addResourceLocations("/js/");
registry.addResourceHandler("/css/**").addResourceLocations("/css/");
registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
}
}
@Configuration
@ComponentScan({"com.clp.controller", "com.clp.config"}) // 添加扫描
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig {
}
SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。
自定义类型转换器的开发步骤:
import org.springframework.core.convert.converter.Converter;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateConverter implements Converter {
public Date convert(String dateStr) {
//将日期字符串转换成日期对象,然后返回
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
Date date = null;
try {
date = format.parse(dateStr);
} catch (ParseException e) {
e.printStackTrace();
}
return date;
}
spring-mvc.xml:
...
...
//localhost:80/user/quick18?date=2020-12-12
@RequestMapping(value="/quick18")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public void save17(Date date) throws IOException {
System.out.println(date);
}
SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的参数如下:
@RequestMapping(value="/quick19")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public void save19(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {
System.out.println(request);
System.out.println(response);
System.out.println(session);
}
REST(Representational State Transfer,表现形式状态转换)。根据REST风格对资源进行访问称为RESTful。Restful是一种软件架构风格、设计风格,而不是标准,知识提供了一组设计原则和约束条件。主要用于客户端和服务器交互的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
Restful风格的请求是使用"url+请求方式"表示一次请求的目的,HTTP协议里面四个表示操作方式的动词如下:
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范。描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts...
REST风格的优点:
@Controller
public class UserController {
/*
* @RequestMapping:设置当前控制器方法请求访问路径。
* 属性:
* 1、value(默认):请求访问路径;
* 2、method:http请求动作,标准动作(GET、POST、PUT、DELETE)
*
* @PathVariable:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应。
* */
@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)
@ResponseBody
public String get(@PathVariable Integer id) {
System.out.println("user get ..." + id);
return "{'module':'user get'}";
}
@RequestMapping(value = "/users", method = RequestMethod.GET)
@ResponseBody
public String getAll() {
System.out.println("user getAll...");
return "{'module':'user getAll'}";
}
@RequestMapping(value = "/users", method = RequestMethod.POST)
@ResponseBody
public String post(@RequestBody User user) {
System.out.println("user post ..." + user);
return "{'module':'user post'}";
}
@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id) {
System.out.println("user delete ..." + id);
return "{'module':'user delete'}";
}
@RequestMapping(value = "/users", method = RequestMethod.PUT)
@ResponseBody
public String put(@RequestBody User user) {
System.out.println("user put ..." + user);
return "{'module':'user put'}";
}
}
/*
* @RestController:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能。
* @GetMapping & @PostMapping & @PutMapping & @DeleteMapping:
* 设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求。
* */
//@Controller
//@ResponseBody
@RestController // @RestController = @Controller + @ResponseBody
@RequestMapping("/books")
public class BookController {
// @RequestMapping(method = RequestMethod.POST)
@PostMapping // @PostMapping = @RequestMapping(method = RequestMethod.POST)
public String post(@RequestBody Book book) {
System.out.println("book post ..." + book);
return "{'module':'book post'}";
}
// @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@DeleteMapping("/{id}") // @DeleteMapping("/{id}") = @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public String delete(@PathVariable Integer id) {
System.out.println("book delete ..." + id);
return "{'module':'book delete'}";
}
// @RequestMapping(method = RequestMethod.PUT)
@PutMapping // @PutMapping = @RequestMapping(method = RequestMethod.PUT)
public String put(@RequestBody Book book) {
System.out.println("book put ..." + book);
return "{'module':'book put'}";
}
// @RequestMapping(value = "/{id}}", method = RequestMethod.GET)
@GetMapping("/{id}") // @GetMapping("/{id}") = @RequestMapping(value = "/{id}}", method = RequestMethod.GET)
public String get(@PathVariable Integer id) {
System.out.println("book get ..." + id);
return "{'module':'book get'}";
}
// @RequestMapping(method = RequestMethod.GET)
@GetMapping // @GetMapping = @RequestMapping(method = RequestMethod.GET)
public String getAll() {
System.out.println("book getAll ...");
return "{'module':'book getAll'}";
}
}
1、创建工程
2、SSM整合
- Spring
SpringConfig
- MyBatis
MyBatisConfig
JdbcConfig
jdbc.properties
- SpringMVC
ServletConfig
SpringMvcConfig
3、功能模块
- 表与实体类
- dao(接口+自动代理)
- service(接口+实现类)
业务层接口测试(整合JUnit)
- controller
表现层接口测试(PostMan)
pom.xml:
...
org.springframework
spring-webmvc
5.2.10.RELEASE
org.springframework
spring-jdbc
5.2.10.RELEASE
org.springframework
spring-test
5.2.10.RELEASE
org.mybatis
mybatis
3.5.6
org.mybatis
mybatis-spring
1.3.0
mysql
mysql-connector-java
5.1.47
com.alibaba
druid
1.1.16
junit
junit
4.12
test
javax.servlet
javax.servlet-api
3.1.0
provided
com.fasterxml.jackson.core
jackson-databind
2.9.0
org.aspectj
aspectjweaver
1.9.4
...
org.apache.tomcat.maven
tomcat7-maven-plugin
2.1
80
/
...
jdbc.properties:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmdb
jdbc.username=root
jdbc.password=123456
public class JdbcConfig {
/*
* 简单类型的依赖注入使用@Value
* */
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
// 1、定义一个方法获得要管理的对象
// 2、添加@Bean表示当前方法的返回值是一个bean
/*
* 引用类型的依赖注入:主需要为bean定义方法设置形参即可,容器会根据 类型 自动装配对象。
* */
@Bean
public DataSource dataSource() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
/**
* 创建平台事务管理器
* @param dataSource
* @return
*/
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
DataSourceTransactionManager dtm = new DataSourceTransactionManager();
dtm.setDataSource(dataSource);
return dtm;
}
}
public class MyBatisConfig {
/**
* SqlSessionFactoryBean专门产生SqlSessionFactory
* @return
*/
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setDataSource(dataSource); // 设置数据源
ssfb.setTypeAliasesPackage("com.clp.domain"); // 设置实体类别名扫描包
return ssfb;
}
/**
* 扫描Dao层的映射信息
* @return
*/
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.clp.dao");
return msc;
}
}
@Configuration
@ComponentScan({"com.clp.service"})
@PropertySource("jdbc.properties")
@Import({JdbcConfig.class, MyBatisConfig.class})
@EnableTransactionManagement // 开启事务管理器
public class SpringConfig {
}
@Configuration
@ComponentScan({"com.clp.controller"})
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig {
}
/**
* 定义一个servlet容器启动的配置类,在里面加载spring的配置(需继承AbstractDispatcherServletInitializer,这样tomcat可以探测到这个类)
*/
public class ServletConfig extends AbstractDispatcherServletInitializer {
/*
* 注:Web容器(Tomcat)启动时,会加载SpringMvcConfig和SpringConfig配置类。
* 这两个配置类产生的容器对象不是同一个对象。
* SpringMVC容器能访问Spring容器,而Spring容器不能访问SpringMVC容器。
* */
// 加载SpringMVC容器配置
@Override
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
// 拦截请求:设置哪些请求归属SpringMVC处理
@Override
protected String[] getServletMappings() {
return new String[]{"/"}; // 所有请求归SpringMVC进行处理
}
// 加载Spring容器的配置
@Override
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
// 设置过滤器,处理中文乱码问题
@Override
protected Filter[] getServletFilters() {
// Post请求中文乱码处理:配置字符过滤器,为web容器添加过滤器并指定字符集,spring-web包中提供了专用的字符过滤器
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
return new Filter[]{filter};
}
}
public interface BookDao {
@Insert("insert into book values(null, #{type}, #{name}, #{description})")
void save(Book book);
@Update("update book set type=#{type}, name=#{name}, description=#{description} where id = #{id}")
void update(Book book);
@Delete("delete from book where id = #{id}")
void delete(Integer id);
@Select("select * from book where id = #{id}")
Book getById(Integer id);
@Select("select * from book")
List getAll();
}
@Transactional
public interface BookService {
boolean save(Book book);
boolean update(Book book);
boolean delete(Integer id);
Book getById(Integer id);
List getAll();
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public boolean save(Book book) {
bookDao.save(book);
return true;
}
@Override
public boolean update(Book book) {
bookDao.update(book);
return true;
}
@Override
public boolean delete(Integer id) {
bookDao.delete(id);
return true;
}
@Override
public Book getById(Integer id) {
return bookDao.getById(id);
}
@Override
public List getAll() {
return bookDao.getAll();
}
}
@RestController // @RestController = @Controller + @ResponseBody
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
// @RequestMapping(method = RequestMethod.POST)
@PostMapping // @PostMapping = @RequestMapping(method = RequestMethod.POST)
public boolean post(@RequestBody Book book) {
return bookService.save(book);
}
// @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@DeleteMapping("/{id}") // @DeleteMapping("/{id}") = @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public boolean delete(@PathVariable Integer id) {
return bookService.delete(id);
}
// @RequestMapping(method = RequestMethod.PUT)
@PutMapping // @PutMapping = @RequestMapping(method = RequestMethod.PUT)
public boolean put(@RequestBody Book book) {
return bookService.update(book);
}
// @RequestMapping(value = "/{id}}", method = RequestMethod.GET)
@GetMapping("/{id}") // @GetMapping("/{id}") = @RequestMapping(value = "/{id}}", method = RequestMethod.GET)
public Book get(@PathVariable Integer id) {
return bookService.getById(id);
}
// @RequestMapping(method = RequestMethod.GET)
@GetMapping // @GetMapping = @RequestMapping(method = RequestMethod.GET)
public List getAll() {
return bookService.getAll();
}
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
@Autowired
private BookService bookService;
@Test
public void testGetById() {
Book book = bookService.getById(1);
System.out.println(book);
}
@Test
public void testGetAll() {
List bookList = bookService.getAll();
System.out.println(bookList);
}
}
前端接收数据格式——封装特殊消息到message(msg)属性中。
- 设置统一数据返回结果类:
public class Result {
private Object data; // 数据
private Integer code; // 编码
private String msg; // 消息
}
注意:Result类中的字段并不是固定的,可以根据需要自行增减。
提供若干个构造方法,方便操作。
- 设置统一数据返回结果编码
public class Code {
public static final Integer SAVE_OK = 20011;
public static final Integer DELETE_OK = 20021;
public static final Integer UPDATE_OK = 20031;
public static final Integer GET_OK = 20041;
public static final Integer SAVE_ERR = 20010;
public static final Integer DELETE_ERR = 20020;
public static final Integer UPDATE_ERR = 20030;
public static final Integer GET_ERR = 20040;
}
注意:Code类的常量设计也不是固定的,可以根据需要自行增减,例如将查询再进行细分为GET_OK,GET_ALL_OK,GET_PAGE_OK。
- 根据情况设定合理的Result
@RestController // @RestController = @Controller + @ResponseBody
@RequestMapping("/books")
public class BookController {
@Autowired
private BookService bookService;
// @RequestMapping(method = RequestMethod.POST)
@PostMapping // @PostMapping = @RequestMapping(method = RequestMethod.POST)
public Result post(@RequestBody Book book) {
boolean flag = bookService.save(book);
return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);
}
// @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@DeleteMapping("/{id}") // @DeleteMapping("/{id}") = @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
public Result delete(@PathVariable Integer id) {
boolean flag = bookService.delete(id);
return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag);
}
// @RequestMapping(method = RequestMethod.PUT)
@PutMapping // @PutMapping = @RequestMapping(method = RequestMethod.PUT)
public Result put(@RequestBody Book book) {
boolean flag = bookService.update(book);
return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag);
}
// @RequestMapping(value = "/{id}}", method = RequestMethod.GET)
@GetMapping("/{id}") // @GetMapping("/{id}") = @RequestMapping(value = "/{id}}", method = RequestMethod.GET)
public Result get(@PathVariable Integer id) {
Book book = bookService.getById(id);
return new Result(
book != null ? Code.GET_OK : Code.GET_ERR,
book,
book != null ? "查询成功" : "查询失败"
);
}
// @RequestMapping(method = RequestMethod.GET)
@GetMapping // @GetMapping = @RequestMapping(method = RequestMethod.GET)
public Result getAll() {
List bookList = bookService.getAll();
return new Result(
bookList != null ? Code.GET_OK : Code.GET_ERR,
bookList,
bookList != null ? "查询成功" : "查询失败"
);
}
}
系统中异常包括两类:预期异常和运行时异常RintimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。
系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理。如下图:
使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResolver。
SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置。
spring-mvc.xml:
...
...
实现Spring的异常处理接口HandleExceptionResolver自定义自己的异常处理器。
自定义异常处理步骤:
package com.exception;
public class MyException extends Exception{
}
/************************************************************************************/
package com.exception;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyExceptionResolver implements HandlerExceptionResolver {
@Override
/**
* 参数Exception:异常对象。
* 返回值ModelAndView:跳转的错误视图信息。
*/
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView modelAndView = new ModelAndView();
if(ex instanceof MyException) {
modelAndView.addObject("info","自定义异常");
}else if(ex instanceof ClassCastException) {
modelAndView.addObject("info","类型转换异常");
}
modelAndView.setViewName("error.jsp");
return null;
}
}
spring-mvc.xml:
...
...
出现异常现象的常见位置与常见诱因如下:
思考:
Spring中的异常处理器可以集中、统一地处理项目中出现的异常。
// 声明这个类是异常处理器,用于AOP处理异常,需要SpringMvcConfig配置扫描该类所在的包(@ComponentScan)
@RestControllerAdvice
public class ProjectExceptionAdvice {
@ExceptionHandler(Exception.class) // 拦截异常
// 方法的返回值将会作为响应结果
public Result doException(Exception ex) {
System.out.println("获取异常");
return new Result(666, null);
}
}
BookController:
@PostMapping // @PostMapping = @RequestMapping(method = RequestMethod.POST)
public Result post(@RequestBody Book book) {
boolean flag = bookService.save(book);
int i = 1 / 0; // 手动产生异常
return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);
}
- 项目异常分类
- 业务异常(BusinessException)
规范的用户行为产生的异常
不规范的用户行为操作产生的异常
- 系统异常(SystenException)
项目运行过程中可预计且无法避免的异常
- 其他异常(Exception)
编程人员未预期到的异常
- 项目异常处理方案:
- 业务异常(BusinessException)
发送对应消息传递给用户,提醒规范操作
- 系统异常(SystemException)
发送固定消息传递给用户,安抚用户
发送特定消息给运维人员,提醒用户
记录日志
- 其他异常(Exception)
发送固定消息传递给用户,安抚用户
发送特定消息给编程人员,提醒维护(纳入预期范围内)
记录日志
步骤:
public class SystemException extends RuntimeException{
private Integer code; // 异常编号
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public SystemException(Integer code, String message) {
super(message);
this.code = code;
}
public SystemException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
public class BusinessException extends RuntimeException{
private Integer code; // 异常编号
public Integer getCode() {
return code;
}
public void setCode(Integer code) {
this.code = code;
}
public BusinessException(Integer code, String message) {
super(message);
this.code = code;
}
public BusinessException(Integer code, String message, Throwable cause) {
super(message, cause);
this.code = code;
}
}
public class Code {
public static final Integer SYSTEM_ERR = 50001;
public static final Integer BUSINESS_ERR = 60001;
public static final Integer SYSTEM_UNKNOWN_ERR = 59999;
}
@Service
public class BookServiceImpl implements BookService {
@Autowired
private BookDao bookDao;
@Override
public boolean save(Book book) {
if (true) {
throw new BusinessException(Code.BUSINESS_ERR, "请不要使用你的技术挑战我的耐心!");
}
// 将可能出现的异常进行包装,转换成自定义异常
try {
int i = 1 / 0;
} catch (Exception e) {
throw new SystemException(Code.SYSTEM_ERR, "服务器访问超时,请重试!", e);
}
bookDao.save(book);
return true;
}
}
// 声明这个类是异常处理器,用于AOP处理异常,需要SpringMvcConfig配置扫描该类所在的包(@ComponentScan)
@RestControllerAdvice
public class ProjectExceptionAdvice {
// 处理系统异常
@ExceptionHandler(SystemException.class)
public Result doSystemException(SystemException ex) {
// 记录日志
// 发送消息给运维
// 发送邮件给开发人员
return new Result(ex.getCode(), null, ex.getMessage());
}
// 处理业务异常
@ExceptionHandler(BusinessException.class)
public Result doBusinessException(BusinessException ex) {
return new Result(ex.getCode(), null, ex.getMessage());
}
// 处理其他异常
@ExceptionHandler(Exception.class) // 拦截异常
// 方法的返回值作为出现异常的方法的返回值
public Result doException(Exception ex) {
return new Result(Code.SYSTEM_UNKNOWN_ERR, null, "系统繁忙,请稍后再试!");
}
}
拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
拦截器的作用:
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体体现。
区别 | 过滤器(Filter) | 拦截器(Interceptor) |
---|---|---|
使用范围 | 是servlet规范中的一部分,任何Java Web工程都可以使用。 | 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用。 |
拦截范围 | 在url-pattern中配置了/*之后,可以对所有要访问的资源拦截。 | 仅针对SpringMVC的访问进行增强。在 |
自定义拦截器很简单,只有如下三步:
/**
* 拦截器(interceptor)包在controller包下,因为已经配置了扫描controller包,所以不用再配置interceptor包了
*/
@Component
public class ProjectInterceptor implements HandlerInterceptor{
// 被拦截的原始操作之前运行的方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle");
return true; // return false 会终止原始操作的执行。因此下面两个方法不会再执行
}
// 被拦截的原始操作之后运行的方法
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
return;
}
// 被拦截的原始操作之后并且在postHandle()方法后执行的方法
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
return;
}
}
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
/**
* 配置拦截器,并设定拦截的访问路径,路径可以通过可变参数设置多个
*/
@Override
protected void addInterceptors(InterceptorRegistry registry) {
// addPathPatterns()方法的参数表示拦截的url字符串匹配则进行拦截
registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
}
}
@Configuration
@ComponentScan({"com.clp.controller", "com.clp.config"}) // 添加扫描
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig {
}
/*
- xml配置方式:
spring-mvc.xml:
...
...
*/
- 上述配置的简化版(注意:侵入式较强):
@Configuration
@ComponentScan({"com.clp.controller"})
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private ProjectInterceptor projectInterceptor;
/**
* 配置拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// addPathPatterns()方法的参数表示拦截的url字符串匹配则进行拦截
registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
}
}
方法名 | 说明 |
---|---|
preHandle() | 方法将在请求处理之前进行调用,该方法的返回值是布尔值boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor的preHandle方法。 |
postHandle() | 该方法是在当前请求进行处理之后被调用,前提是preHandle方法的返回值为true时才能被调用,且它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。 |
afterCompletion() | 该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true时才能被调用。 |
- 前置处理
@Component
public class ProjectInterceptor implements HandlerInterceptor{
// 被拦截的原始操作之前运行的方法
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HandlerMethod hm = (HandlerMethod) handler;
String contentType = request.getHeader("Content-Type");
System.out.println("preHandle" + contentType);
return true; // return false 会终止原始操作的执行。因此下面两个方法不会再执行
}
}
- 参数
request:请求对象
response:响应对象
handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
- 返回值
若返回值为false:被拦截的处理器将不执行
- 后置处理
// 被拦截的原始操作之后运行的方法
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle");
return;
}
- 参数:
modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整。
- 完成后处理
// 被拦截的原始操作之后并且在postHandle()方法后执行的方法
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion");
return;
}
- 参数:
ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理。
@Configuration
@ComponentScan({"com.clp.controller"})
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private ProjectInterceptor projectInterceptor;
@Autowired
private ProjectInterceptor2 projectInterceptor2;
/**
* 配置拦截器
*/
@Override
public void addInterceptors(InterceptorRegistry registry) {
// addPathPatterns()方法的参数表示拦截的url字符串匹配则进行拦截
// 拦截器链的执行顺序与添加顺序有关
registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");
registry.addInterceptor(projectInterceptor2).addPathPatterns("/books", "/books/*");
}
}
form.jsp:
...
...
当form表单修改为多部分表单时,request.getParameter()将失效;
enctype="application/x-www-form-urlencoded"时,form表单的正文内容格式是:key=value&key=value...;
当form表单的enctype取值为multipart/form-data时,请求正文内容就变成多部分形式:
pom.xml:
commons-fileupload
commons-fileupload
1.3.1
commons-io
commons-io
2.3
spring-mvc.xml:
...
...
@RequestMapping(value="/quick22")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public void save22(String username, MultipartFile uploadFile) throws IOException {
System.out.println(username);
//获得文件名称
String originalFilename = uploadFile.getOriginalFilename();
//保存文件
uploadFile.transferTo(new File("D:\\upload\\"+originalFilename));
}
多文件上传,只需要将页面修改为多个文件上传项,将方法参数MultipartFile类型修改为MultipartFile[]即可。
...
...
@RequestMapping(value="/quick22")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public void save22(String username1,String username2, MultipartFile uploadFile1, MultipartFile uploadFile2) throws IOException {
System.out.println(username);
//获得文件名称
String originalFilename1 = uploadFile1.getOriginalFilename();
//保存文件
uploadFile1.transferTo(new File("D:\\upload\\"+originalFilename1));
//获得文件名称
String originalFilename2 = uploadFile1.getOriginalFilename();
//保存文件
uploadFile2.transferTo(new File("D:\\upload\\"+originalFilename2));
}
@RequestMapping(value="/quick23")
@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写
public void save23(String username, MultipartFile[] uploadFiles) throws IOException {
System.out.println(username);
for(MultipartFile multipartFile : uploadFiles) {
//获得文件名称
String originalFilename = multipartFile.getOriginalFilename();
//保存文件
multipartFile.transferTo(new File("D:\\upload\\"+originalFilename));
}
}