1. 学习目标
1.掌握基于SpringMVC获取请求参数与响应json数据操作
2.熟练应用基于REST风格的请求路径设置与参数传递
3.能够根据实际业务建立前后端开发通信协议并进行实现
4.基于SSM整合技术开发任意业务模块功能
5.能够理解并实现统一结果封装与统一异常处理
6.能够完成前后台功能整合开发
7.掌握拦截器的编写
2. SpringMVC简介
(1) 简介
Web工作流程:通过浏览器访问页面,通过异步提交的方式访问后端,后端通过三层架构的形式进行开发,最后返回json格式给前端,前端解析数据并交给浏览器
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架,用于表现层功能开发
优点:灵活性强、较Servlet来说使用简单、开发便捷
(2) 配置类使用
原始版
public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
protected WebApplicationContext createServletApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringMvcConfig.class);
return ctx;
}
protected String[] getServletMappings() {
return new String[]{"/"};
}
protected WebApplicationContext createRootApplicationContext() {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(SpringConfig.class);
return ctx;
}
}
优化版
public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
//加载Spring配置类
protected Class<?>[] getRootConfigClasses() {
return new Class[]{SpringConfig.class};
}
//加载SpringMVC配置类
protected Class<?>[] getServletConfigClasses() {
return new Class[]{SpringMvcConfig.class};
}
//设置SpringMVC请求地址拦截规则
protected String[] getServletMappings() {
return new String[]{"/"};
}
//设置post请求中文乱码过滤器
@Override
protected Filter[] getServletFilters() {
CharacterEncodingFilter filter = new CharacterEncodingFilter();
filter.setEncoding("UTF-8");
filter.setForceEncoding(true);
return new Filter[]{filter};
}
}
(3) 工作流程
- 服务器启动时,初始化web容器
- 加载SpringMVC配置类
SpringMvcConfig来初始化SpringMVC的容器(此时创建了WebApplicationContext对象) - 加载了
SpringMvcConfig配置类 - 执行
@ComponentScan对应的bean,扫描了指定包及以下类中的注解 - 加载了控制层,以及
@RequestMapping的对应方法 - 执行了getServletMappings方法,设定了拦截请求路径规则
(4) 单次请求过程
- 发送请求,示例:
http://localhost/save - web容器发现请求满足SpringMVC拦截规则,将请求交给SpringMVC处理
- 解析请求路径/save,匹配执行对应方法save()
- 执行save()
- 检测到有@ResponseBody,将save()方法返回值作为响应体返回给请求方
(5) bean加载控制
由于SpringMVC负责加载Controller层,为了防止Spring扫描控制层,我们可以有以下方法解决
1.设置扫描范围
将扫描范围定位精准范围即可
@Configuration
@ComponentScan({"com.Coolipa.service","com.Coolipa.dao"})
public class SpringConfig {
}
2.排除bean
通过excludeFilters进行过滤
@Configuration
@ComponentScan(value="com.itheima",
excludeFilters=@ComponentScan.Filter(
type = FilterType.ANNOTATION,
classes = Controller.class
)
)
public class SpringConfig {
}
-
excludeFilters属性:设置扫描加载bean时,排除的过滤规则
-
type属性:设置排除规则,当前使用按照bean定义时的注解类型进行排除
- ANNOTATION:按照注解排除
- ASSIGNABLE_TYPE:按照指定的类型过滤
- ASPECTJ:按照AspectJ表达式排除,基本上不会用
- REGEX:按照正则表达式排除
- CUSTOM:按照自定义规则排除
只需要知道第一种ANNOTATION即可,其他作为了解
-
classes属性:设置排除的具体注解类,当前设置排除@Controller定义的bean
3. 请求与响应
这里着重强调REST风格的请求与响应,其他作为了解
(1) 设置请求映射路径
通过规范开发,避免每人设置不同的请求路径从而冲突
通过设置模块名作为请求路径前缀,从而避免冲突。
示例:
@Controller
public class UserController {
//设置访问路径
@RequestMapping("/user/save")
//设置返回值类型
@ResponseBody
public String save(){
System.out.println("user save");
return "{'module':'springmvc'}";
}
//设置访问路径
@RequestMapping("/user/insert")
//设置返回值类型
@ResponseBody
public String insert(){
System.out.println("user insert");
return "{'module':'springmvc'}";
}
}
注意:
- 当类上和方法上都添加了
@RequestMapping注解,前端发送请求的时候,要和两个注解的value值相加匹配才能访问到。 - @RequestMapping注解value属性前面加不加
/都可以
(2)不同类型参数传递
<1> Get请求和Post请求参数传递
Get请求传参
普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数(使用Params)
Post请求参数
普通参数:form表单post请求传参,表单参数名与形参变量名相同,定义传参即可接收参数(使用Body的x-www-form-urlencoded)
<2>四种类型参数传递
这里分为普通参数、POJO参数、数组类型参数、集合类型参数四种。
普通参数
若请求参数名与形参变量名不同,使用@RequestParam绑定参数关系
@RequestMapping("/commonParam")
@ResponseBody
public String commonParamDifferentName(@RequestParam("name") String userName , int age){
// ......
return .....;
}
POJO参数
请求参数名与形参对象属性名相同,定义POJO类型形参就能接收参数,若为嵌套类型(POJO嵌入其他POJO),使用.即可
public class User {
private String name;
private int age;
private Address address;
//get和set方法...
}
//POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
@RequestMapping("/pojoParam")
@ResponseBody
public String pojoParam(User user){
//......
return .....;
}
数组类型参数
请求参数名与形参对象属性名相同且为多个,定义数组类型形参即可接收参数
//数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
@RequestMapping("/arrayParam")
@ResponseBody
public String arrayParam(String[] likes){
//.....
return ...;
}
集合类型参数
由于SpringMVC会默认把List看错POJO对象,从而导致把前端数据封装到对象中,因此会导致报错,所以使用
@RequestParam注解
请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系
//集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
@RequestMapping("/listParam")
@ResponseBody
public String listParam(@RequestParam List<String> likes){
//...
return ...;
}
<3> json数据参数传递
使用条件:引入 Jackson(例如 jackson-databind),并确保注册了 MappingJackson2HttpMessageConverter。非 Spring Boot 环境可通过 @EnableWebMvc 或 XML 启用 MVC 以激活消息转换;在 Spring Boot 中无需 @EnableWebMvc。
对于JSON数据类型,我们常见的有三种:
- json普通数组(["value1","value2","value3",...])
- json对象({key1:value1,key2:value2,...})
- json对象数组([{key1:value1,...},{key2:value2,...}])
JSON普通数组
使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
@RequestMapping("/listParamForJson")
@ResponseBody
public String listParamForJson(@RequestBody List<String> likes){
System.out.println("list common(json)参数传递 list ==> "+likes);
return "{'module':'list common for json param'}";
}
JSON对象数据
json数据与形参对象数据名相同,定义POJO类型形参即可接收参数,若是由嵌套POJO类型,在JSON也嵌套即可
@RequestMapping("/pojoParamForJson")
@ResponseBody
public String pojoParamForJson(@RequestBody User user){
System.out.println("pojo(json)参数传递 user ==> "+user);
return "{'module':'pojo for json param'}";
}
JSON对象数组
json数组数据与集合泛型属性名相同,定义List类型形参即可接收参数
@RequestMapping("/listPojoParamForJson")
@ResponseBody
public String listPojoParamForJson(@RequestBody List<User> list){
System.out.println("list pojo(json)参数传递 list ==> "+list);
return "{'module':'list pojo for json param'}";
}
<4>日期型参数传递
在SpringMVC默认支持的字符串转日期的格式为yyyy/MM/dd,如果格式不同,需要使用@DateTimeFormat。
@RequestMapping("/dataParam")
@ResponseBody
public String dataParam(Date date,
@DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
@DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2) {
System.out.println("参数传递 date ==> "+date);
System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
return "{'module':'data param'}";
}
拓展:内部实现原理
SpringMVC提供了许多类型转换接口和实现类,例如Converter
public interface Converter<S, T> {
@Nullable
//该方法就是将从页面上接收的数据(S)转换成我们想要的数据类型(T)返回
T convert(S source);
}
4. REST风格写法
<1> 简介
REST(Representational State Transfer) 表现形式状态转换,是一种软件架构风格
传统风格与REST风格对比
- 传统风格资源描述形式
http://localhost/user/getById?id=1查询id为1的用户信息http://localhost/user/saveUser保存用户信息
- REST风格描述形式
http://localhost/user/1http://localhost/user
优点:隐藏资源访问行为,书写简化
拓充:按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
- 发送GET请求是用来做查询
- 发送POST请求是用来做新增
- 发送PUT请求是用来做修改
- 发送DELETE请求是用来做删除
具体实例:
http://localhost/users查询全部用户信息 GET(查询)http://localhost/users/1查询指定用户信息 GET(查询)http://localhost/users添加用户信息 POST(新增/保存)http://localhost/users修改用户信息 PUT(修改/更新)http://localhost/users/1删除用户信息 DELETE(删除)
<2> 使用写法
原始使用
以http://localhost/users/1为例
@Controller
@RequestMapping("/users")
public class UserController {
//设置当前请求方法为DELETE,表示REST风格中的删除操作
@RequestMapping(value = "/{id}",method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable Integer id) {
System.out.println("user delete..." + id);
return "{'module':'user delete'}";
}
}
简便使用
@RestController
@RequestMapping("/books")
public class BookController{
@Autowired
private BookService bookService;
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println("book getById"+id);
return "{'module':'book getById'}";
}
}
5. 拦截器
拦截器( Interceptor ) 是一种控制方法前后执行代码的工作机制,底层是AOP。
作用:
- 在指定方法调用前后执行预先设定的代码
- 阻止原始方法的执行
(1) 拦截器与过滤器区别
- 归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
- 拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强
(2) 使用拦截器
<1> 直接配置拦截器类
需要写拦截器接口
@Component
//定义拦截器类,实现HandlerInterceptor接口
//注意当前类必须受Spring容器控制
public class ProjectInterceptor implements HandlerInterceptor {
@Override
//原始方法调用前执行的内容
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...");
return true;
}
@Override
//原始方法调用后执行的内容
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...");
}
@Override
//原始方法调用完成后执行的内容
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...");
}
}
此处需要被SpringMVC扫到,且需要在单独写SpringMvcSupport类。注意:继承 WebMvcConfigurationSupport 会禁用默认MVC配置,通常更推荐仅实现 WebMvcConfigurer。
@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
protected void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
}
@Override
protected void addInterceptors(InterceptorRegistry registry) {
//配置拦截器
registry.addInterceptor(projectInterceptor).addPathPatterns("/books/**");
}
}
<2> 直接写在SpringMvcConfig内
@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private ProjectInterceptor projectInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//配置多拦截器
registry.addInterceptor(projectInterceptor).addPathPatterns("/books/**");
}
}
<3> 拦截器参数
前置处理
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return true;
}
参数:
- request:请求对象
- response:响应对象
- handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再次包装
返回值:
- 返回值为false,被拦截的处理器将不执行
后置处理
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...")
}
参数:
- modelAndView:如果处理器执行完成具有返回效果,可以读取到对应数据与页面信息,并进行调整
不需要使用,了解即可
完成后处理
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion")
}
可以通过SpringMVC的异常处理机制替换
(3) 拦截器链
<1> 配置多个拦截器
步骤1:创建拦截器类
实现接口,并重写接口中的方法
@Component
public class ProjectInterceptor2 implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle...222");
return false;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle...222");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion...222");
}
}
步骤2:配置拦截器类
@Configuration
@ComponentScan({"com.itheima.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
@Autowired
private ProjectInterceptor projectInterceptor;
@Autowired
private ProjectInterceptor2 projectInterceptor2;
@Override
public void addInterceptors(InterceptorRegistry registry) {
//配置多拦截器
registry.addInterceptor(projectInterceptor).addPathPatterns("/books/**");
registry.addInterceptor(projectInterceptor2).addPathPatterns("/books/**");
}
}
<2> 多拦截器执行顺序
与栈相同,先进后出,后进先出
- 当配置多个拦截器时,形成拦截器链
- 拦截器链的运行顺序参照拦截器添加顺序为准
- 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行
- 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作
6. 结语
如果这篇文章对你有帮助,希望可以给一个推荐,收藏和评论