SpringMVC

SpringMVC简介

SpringMVC是一种基于Java实现MVC模型的轻量级Web框架。
优点:
  使用简单,开发便捷(相比于Servlet)
  灵活性强

入门案例

【第一步】创建web工程(Maven结构);在pom.xml设置tomcat服务器,加载web工程(tomcat插件)

 1 <build>
 2     <plugins>
 3         <plugin>
 4             <groupId>org.apache.tomcat.maven</groupId>
 5             <artifactId>tomcat7-maven-plugin</artifactId>
 6             <version>2.1</version>
 7             <configuration>
 8                 <port>80</port>
 9                 <path>/</path>
10             </configuration>
11         </plugin>
12     </plugins>
13 </build>

【第二步】在pom.xml导入坐标(SpringMVC+Servlet)

 1 <dependencies>
 2     <dependency>
 3         <groupId>javax.servlet</groupId>
 4         <artifactId>javax.servlet-api</artifactId>
 5         <version>3.1.0</version>
 6         <scope>provided</scope>
 7     </dependency>
 8     <dependency>
 9         <groupId>org.springframework</groupId>
10         <artifactId>spring-webmvc</artifactId>
11         <version>5.2.10.RELEASE</version>
12     </dependency>
13 </dependencies>

【第三步】定义处理请求的功能类(UserController)

 1 //定义表现层控制器bean
 2 @Controller
 3 public class UserController {
 4     //设置映射路径为/save,即外部访问路径
 5     @RequestMapping("/save")
 6     //设置当前操作返回结果为指定json数据(本质上是一个字符串信息)
 7     @ResponseBody
 8     public String save(){
 9         System.out.println("user save ...");
10         return "{'info':'springmvc'}";
11     }
12 }

  @RequestMapping注解:
    位置:SpringMVC控制器方法定义上方。
    作用:设置当前控制器方法请求访问路径。

  注:对于SpringMVC而言,Controller方法返回值默认表示要跳转的页面,没有对应的页面就会报错。如果不想跳转页面而是响应数据,那么就需要在方法上使用@ResponseBody注解。
  @ResponseBody注解:
    位置:SpringMVC控制器方法定义上方。
    作用:设置当前控制器返回值作为响应体

【第四步】编写SpringMVC配置类,加载处理请求的Bean。

1 //springmvc配置类,本质上还是一个spring配置类
2 @Configuration
3 @ComponentScan("com.test.controller")
4 public class SpringMvcConfig {
5 }

【第五步】加载SpringMVC配置,并设置SpringMVC请求拦截的路径

 1 //web容器配置类
 2 public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
 3     //加载springmvc配置类,产生springmvc容器(本质还是spring容器)
 4     protected WebApplicationContext createServletApplicationContext() {
 5         //初始化WebApplicationContext对象
 6         AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
 7         //加载指定配置类
 8         ctx.register(SpringMvcConfig.class);
 9         return ctx;
10     }
11 
12     //设置由springmvc控制器处理的请求映射路径
13     protected String[] getServletMappings() {
14         return new String[]{"/"};
15     }
16 
17     //加载spring配置类
18     protected WebApplicationContext createRootApplicationContext() {
19         return null;
20     }
21 }

AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类。
AbstractDispatcherServletInitializer提供三个接口方法供用户实现:
  createServletApplicationContext()方法,创建Servlet容器时,加载SpringMVC对应的bean并放入WebApplicationContext对象范围中,而WebApplicationContext的作用范围为ServletContext范围,即整个web容器范围。
  getServletMappings()方法,设定SpringMVC对应的请求映射路径,设置为/表示拦截所有请求,任意请求都将转入到SpringMVC进行处理。
  createRootApplicationContext()方法,如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行,使用方式同createServletApplicationContext()。

入门案例工作流程分析

启动服务器初始化过程

1. 服务器启动,执行ServletContainersInitConfig类,初始化web容器
2. 执行createServletApplicationContext方法,创建了WebApplicationContext对象
3. 加载SpringMvcConfig配置类
4. 执行@ComponentScan加载对应的bean
5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法
6. 执行getServletMappings方法,定义所有的请求都通过SpringMVC

单次请求过程

1. 发送请求localhost/save
2. web容器发现所有请求都经过SpringMVC,将请求交给SpringMVC处理
3. 解析请求路径/save
4. 由/save匹配执行对应的方法save()
5. 执行save()
6. 检测到有@ResponseBody直接将save()方法的返回值作为响应求体返回给请求方

Controller加载控制

Controller加载控制与业务bean加载控制

SpringMVC相关bean:表现层bean
Spring控制的bean:
  业务bean(Service)
  功能bean(DataSource等)
SpringMVC相关bean加载控制:
  SpringMVC加载的bean对应的包均在com.test.controller包内
Spring相关bean加载控制:
  方式一:Spring加载的bean设定扫描范围为com.test,排除掉controller包内的bean

1 @Configuration
2 @ComponentScan(value = "com.test",
3                excludeFilters = @ComponentScan.Filter(
4                    type = FilterType.ANNOTATION, //ANNOTATION:按注解过滤
5                    classes = Controller.class  //需要过滤的注解类型
6                )
7               )
8 public class SpringConfig {
9 }

    属性:
      excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes)。
      includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)。

    需要过滤的注解类型:

      

  方式二:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等(开发主要用)

1 @Configuration
2 @ComponentScan({"com.test.service","com.test.dao"})
3 public class SpringConfig {
4 }

  方式三:不区分Spring与SpringMVC的环境,加载到同一个环境中

 1 public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer { 
 2     protected WebApplicationContext createServletApplicationContext() { 
 3         AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
 4         ctx.register(SpringMvcConfig.class);
 5         return ctx;  
 6     }   
 7     protected WebApplicationContext createRootApplicationContext() {  
 8         AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();      
 9         ctx.register(SpringConfig.class);        
10         return ctx;  
11     }   
12     protected String[] getServletMappings() { 
13         return new String[]{"/"}; 
14     }
15 }

简化格式:

 1 public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
 2     protected Class<?>[] getServletConfigClasses() {
 3         return new Class[]{SpringMvcConfig.class}
 4     };
 5     protected String[] getServletMappings() {
 6         return new String[]{"/"};
 7     }
 8     protected Class<?>[] getRootConfigClasses() {
 9         return new Class[]{SpringConfig.class};
10     }
11 }

请求与响应

请求映射路径【重点】

名称:@RequestMapping
类型:方法注解 类注解
位置:SpringMVC控制器方法定义上方
作用:设置当前控制器方法请求访问路径,如果设置在类上统一设置当前控制器方法请求访问路径前缀
范例:

 1 @Controller
 2 //类上方配置的请求映射与方法上面配置的请求映射连接在一起,形成完整的请求映射路径
 3 @RequestMapping("/user")
 4 public class UserController {
 5     //请求路径映射
 6     @RequestMapping("/save") //此时save方法的访问路径是:/user/save
 7     @ResponseBody
 8     public String save(){
 9         System.out.println("user save ...");
10         return "{'module':'user save'}";
11     }
12 }

请求参数

发送普通类型参数【重点】

GET请求传递普通参数

普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数

1 //普通参数:请求参数与形参名称对应即可完成参数传递
2 @RequestMapping("/commonParam")
3 @ResponseBody
4 public String commonParam(String name ,int age){
5     System.out.println("普通参数传递 name ==> "+name);
6     System.out.println("普通参数传递 age ==> "+age);
7     return "{'module':'common param'}";
8 }
get请求中文乱码处理

注:tomcat 8.5版本之后GET请求就不再出现中文乱码问题。
tomcat 8.5版本之前乱码解决:在pom.xml添加tomcat7插件处配置UTF-8字符集,解决GET请求中文乱码问题。

 1 <build>
 2     <plugins>
 3       <plugin>
 4         <groupId>org.apache.tomcat.maven</groupId>
 5         <artifactId>tomcat7-maven-plugin</artifactId>
 6         <version>2.1</version>
 7         <configuration>
 8           <port>80</port><!--tomcat端口号-->
 9           <path>/</path> <!--虚拟目录-->
10           <uriEncoding>UTF-8</uriEncoding><!--访问路径编解码字符集-->
11         </configuration>
12       </plugin>
13     </plugins>
14   </build>

POST请求传递普通参数

普通参数:form表单post请求传参,表单参数名与形参变量名相同,定义形参即可接收参数

1 //普通参数:请求参数与形参名称对应即可完成参数传递
2 @RequestMapping("/commonParam")
3 @ResponseBody
4 public String commonParam(String name ,int age){
5     System.out.println("普通参数传递 name ==> "+name);
6     System.out.println("普通参数传递 age ==> "+age);
7     return "{'module':'common param'}";
8 }
POST请求中文乱码处理

为web容器添加过滤器并指定字符集,Spring-web包中提供了专用的字符过滤器

 1 public class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
 2     protected Class<?>[] getRootConfigClasses() {
 3         return new Class[0];
 4     }
 5     protected Class<?>[] getServletConfigClasses() {
 6         return new Class[]{SpringMvcConfig.class};
 7     }
 8     protected String[] getServletMappings() {
 9         return new String[]{"/"};
10     }
11 
12     //乱码处理
13     @Override
14     protected Filter[] getServletFilters() {
15         CharacterEncodingFilter filter = new CharacterEncodingFilter();
16         filter.setEncoding("UTF-8");
17         return new Filter[]{filter};
18     }
19 }

五种类型参数传递

普通参数【重点】

普通参数:当请求参数名与形参变量名不同,使用@RequestParam绑定参数关系

1 //普通参数:请求参数名与形参名不同时,使用@RequestParam注解关联请求参数名称与形参名称之间的关系
2 @RequestMapping("/commonParamDifferentName")
3 @ResponseBody
4 public String commonParamDifferentName(@RequestParam("name") String userName , int age){
5     System.out.println("普通参数传递 userName ==> "+userName);
6     System.out.println("普通参数传递 age ==> "+age);
7     return "{'module':'common param different name'}";
8 }

名称:@RequestParam
类型:形参注解
位置:SpringMVC控制器方法形参定义前面
作用:绑定请求参数与处理器方法形参间的关系
参数:
  required:是否为必传参数
  defaultValue:参数默认值

POJO类型参数【重点】

POJO参数:请求参数名与形参对象属性名相同,定义POJO类型形参即可接收参数

1 public class User {
2     private String name;
3     private int age;
4     ...
5 }
1 //POJO参数:请求参数与形参对象中的属性对应即可完成参数传递
2 @RequestMapping("/pojoParam")
3 @ResponseBody
4 public String pojoParam(User user){
5     System.out.println("pojo参数传递 user ==> "+user);
6     return "{'module':'pojo param'}";
7 }

注:请求参数key的名称要和POJO中属性的名称一致,否则无法封装。

嵌套POJO类型参数

POJO对象中包含POJO对象

 1 public class User {
 2     private String name;
 3     private int age;
 4     private Address address;
 5     ...
 6 }
 7 public class Address {
 8     private String province;
 9     private String city;10 }

嵌套POJO参数:请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数

1 //嵌套POJO参数:嵌套属性按照层次结构设定名称即可完成参数传递
2 @RequestMapping("/pojoContainPojoParam")
3 @ResponseBody
4 public String pojoContainPojoParam(User user){
5     System.out.println("pojo嵌套pojo参数传递 user ==> "+user);
6     return "{'module':'pojo contain pojo param'}";
7 }

注:请求参数key的名称要和POJO中属性的名称一致,否则无法封装。

数组类型参数

 数组参数:请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型即可接收参数

1 //数组参数:同名请求参数可以直接映射到对应名称的形参数组对象中
2 @RequestMapping("/arrayParam")
3 @ResponseBody
4 public String arrayParam(String[] likes){
5     System.out.println("数组参数传递 likes ==> "+ Arrays.toString(likes));
6     return "{'module':'array param'}";
7 }

集合类型参数

集合保存普通参数:请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

1 //集合参数:同名请求参数可以使用@RequestParam注解映射到对应名称的集合对象中作为数据
2 @RequestMapping("/listParam")
3 @ResponseBody
4 public String listParam(@RequestParam List<String> likes){
5     System.out.println("集合参数传递 likes ==> "+ likes);
6     return "{'module':'list param'}";
7 }

json数据参数传递

json普通数组(["","","",...])
json对象({key:value,key:value,...})
json对象数组([{key:value,...},{key:value,...}])

传递json普通数组

1.添加json数据转换相关坐标

1 <dependency>
2     <groupId>com.fasterxml.jackson.core</groupId>
3     <artifactId>jackson-databind</artifactId>
4     <version>2.9.0</version>
5 </dependency>

2.设置发送json数据(请求body中添加json数据)

3.开启自动转换json数据的支持

1 @Configuration
2 @ComponentScan("com.test.controller")
3 //开启json数据类型自动转换
4 @EnableWebMvc
5 public class SpringMvcConfig {
6 }

  注:@EnableWebMvc注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换。

4.在Controller中编写方法接收json参数

1 //集合参数:json格式
2 //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
3 //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的集合对象中作为数据
4 @RequestMapping("/listParamForJson")
5 @ResponseBody
6 public String listParamForJson(@RequestBody List<String> likes){
7     System.out.println("list common(json)参数传递 list ==> "+likes);
8     return "{'module':'list common for json param'}";
9 }

传递json对象

POJO参数:json数据与形参对象属性名相同,定义POJO类型形参即可接收参数

1 //POJO参数:json格式
2 //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
3 //2.使用@RequestBody注解将外部传递的json数据映射到形参的实体类对象中,要求属性名称一一对应
4 @RequestMapping("/pojoParamForJson")
5 @ResponseBody
6 public String pojoParamForJson(@RequestBody User user){
7     System.out.println("pojo(json)参数传递 user ==> "+user);
8     return "{'module':'pojo for json param'}";
9 }

传递json对象数组

POJO集合参数:json数组数据与集合泛型属性名相同,定义List类型形参即可接收参数

1 //集合参数:json格式
2 //1.开启json数据格式的自动转换,在配置类中开启@EnableWebMvc
3 //2.使用@RequestBody注解将外部传递的json数组数据映射到形参的保存实体类对象的集合对象中,要求属性名称一一对应
4 @RequestMapping("/listPojoParamForJson")
5 @ResponseBody
6 public String listPojoParamForJson(@RequestBody List<User> list){
7     System.out.println("list pojo(json)参数传递 list ==> "+list);
8     return "{'module':'list pojo for json param'}";
9 }

@RequestBody与@RequestParam区别

区别:
  @RequestParam用于接收url地址传参,表单传参【application/x-www-form-urlencoded】
  @RequestBody用于接收json数据【application/json】
应用:
  后期开发中,发送json格式数据为主,@RequestBody应用较广
  如果发送非json格式数据,选用@RequestParam接收请求参数

日期类型参数传递【重点】

接收形参时,根据不同的日期格式设置不同的接收方式

名称:@DateTimeFormat
类型:形参注解
位置:SpringMVC控制器方法形参前面
作用:设定日期时间型数据格式
属性:pattern:日期时间格式字符串

 1 //使用@DateTimeFormat注解设置日期类型数据格式,默认格式yyyy/MM/dd
 2 @RequestMapping("/dataParam")
 3 @ResponseBody
 4 public String dataParam(Date date,
 5                   @DateTimeFormat(pattern="yyyy-MM-dd") Date date1,
 6                   @DateTimeFormat(pattern="yyyy/MM/dd HH:mm:ss") Date date2){
 7     System.out.println("参数传递 date ==> "+date);
 8     System.out.println("参数传递 date1(yyyy-MM-dd) ==> "+date1);
 9     System.out.println("参数传递 date2(yyyy/MM/dd HH:mm:ss) ==> "+date2);
10     return "{'module':'data param'}";
11 }

工作原理

其内部依赖Converter接口

1 public interface Converter<S, T> {
2     @Nullable
3     T convert(S var1);
4 }

  请求参数年龄数据(String→Integer)
  json数据转对象(json → POJO)
  日期格式转换(String → Date)
注:传递日期类型参数必须在配置类上使用@EnableWebMvc注解。其功能之一:根据类型匹配对应的类型转换器。

响应

响应页面【了解】

 1 @Controller
 2 public class UserController {
 3     //响应页面/跳转页面
 4     //返回值为String类型,设置返回值为页面名称,即可实现页面跳转
 5     @RequestMapping("/toJumpPage")
 6     public String toJumpPage(){
 7         System.out.println("跳转页面");
 8         return "page.jsp";
 9     }
10 }
page.jsp:
1 <%@ page contentType="text/html;charset=UTF-8" language="java" %>
2 <html>
3     <head>
4         <title>Title</title>
5     </head>
6     <body>
7         <h2>Hello Spring MVC!</h2>
8     </body>
9 </html>

文本数据【了解】

1 //响应文本数据
2 //返回值为String类型,设置返回值为任意字符串信息,即可实现返回指定字符串信息,需要依赖@ResponseBody注解
3 @RequestMapping("/toText")
4 @ResponseBody
5 public String toText(){
6     System.out.println("返回纯文本数据");
7     return "response text";
8 }

json数据【重点】

对象转json

 1 //响应POJO对象
 2 //返回值为实体类对象,设置返回值为实体类类型,即可实现返回对应对象的json数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
 3 @RequestMapping("/toJsonPOJO")
 4 @ResponseBody
 5 public User toJsonPOJO(){
 6     System.out.println("返回json对象数据");
 7     User user = new User();
 8     user.setName("itcast");
 9     user.setAge(15);
10     return user;
11 }

对象集合转json数组

 1 //响应POJO集合对象
 2 //返回值为集合对象,设置返回值为集合类型,即可实现返回对应集合的json数组数据,需要依赖@ResponseBody注解和@EnableWebMvc注解
 3 @RequestMapping("/toJsonList")
 4 @ResponseBody
 5 public List<User> toJsonList(){
 6     System.out.println("返回json集合数据");
 7     User user1 = new User();
 8     user1.setName("张三");
 9     user1.setAge(15);
10 
11     User user2 = new User();
12     user2.setName("李四");
13     user2.setAge(12);
14 
15     List<User> userList = new ArrayList<User>();
16     userList.add(user1);
17     userList.add(user2);
18 
19     return userList;
20 }

  注:需要添加jackson-databind依赖以及在SpringMvcConfig配置类上添加@EnableWebMvc注解

工作原理

类型转换器:HttpMessageConverter接口

1 public interface HttpMessageConverter<T> {
2     boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
3     boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
4     List<MediaType> getSupportedMediaTypes();
5     T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
6         throws IOException, HttpMessageNotReadableException;
7     void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
8         throws IOException, HttpMessageNotWritableException;
9 }

最终实现类:MappingJackson2HttpMessageConverter,依赖jackson-databind坐标。需要添加jackson-databind依赖才能实现转换。

REST风格

REST简介

REST介绍

REST(Representational State Transfer),表现形式状态转换。
传统风格资源描述形式:
  http://localhost/user/getById?id=1
  http://localhost/user/saveUser
REST风格描述形式:
  http://localhost/user/1
  http://localhost/user
优点:
  隐藏资源的访问行为,无法通过地址得知对资源是何种操作。
  书写简化。

RESTful介绍

按照REST风格访问资源时使用行为动作区分对资源进行了何种操作。
  http://localhost/users    查询全部用户信息   GET(查询)
  http://localhost/users/1   查询指定用户信息   GET(查询)
  http://localhost/users    添加用户信息     POST(新增/保存)
  http://localhost/users    修改用户信息     PUT(修改/更新)
  http://localhost/users/1   删除用户信息     DELETE(删除)
根据REST风格对资源进行访问称为RESTful。

注:
上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范。
描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts……

RESTful入门案例

 1 @Controller
 2 public class UserController {
 3 
 4     //设置当前请求方法为POST,表示REST风格中的添加操作
 5     @RequestMapping(value = "/users",method = RequestMethod.POST)
 6     @ResponseBody
 7     public String save(){
 8         System.out.println("user save...");
 9         return "{'module':'user save'}";
10     }
11 
12     //设置当前请求方法为DELETE,表示REST风格中的删除操作
13     //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
14     @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
15     @ResponseBody
16     public String delete(@PathVariable Integer id){
17         System.out.println("user delete..." + id);
18         return "{'module':'user delete'}";
19     }
20 
21     //设置当前请求方法为PUT,表示REST风格中的修改操作
22     @RequestMapping(value = "/users",method = RequestMethod.PUT)
23     @ResponseBody
24     public String update(@RequestBody User user){
25         System.out.println("user update..."+user);
26         return "{'module':'user update'}";
27     }
28 
29     //设置当前请求方法为GET,表示REST风格中的查询操作
30     //@PathVariable注解用于设置路径变量(路径参数),要求路径上设置对应的占位符,并且占位符名称与方法形参名称相同
31     @RequestMapping(value = "/users/{id}" ,method = RequestMethod.GET)
32     @ResponseBody
33     public String getById(@PathVariable Integer id){
34         System.out.println("user getById..."+id);
35         return "{'module':'user getById'}";
36     }
37 
38     //设置当前请求方法为GET,表示REST风格中的查询操作
39     @RequestMapping(value = "/users",method = RequestMethod.GET)
40     @ResponseBody
41     public String getAll(){
42         System.out.println("user getAll...");
43         return "{'module':'user getAll'}";
44     }
45 }

@RequestMapping
类型:方法注解
位置:SpringMVC控制器方法定义上方
作用:设置当前控制器方法请求访问路径
属性:
  value(默认):请求访问路径
  method:http请求动作,标准动作(GET/POST/PUT/DELETE)

@PathVariable
类型:形参注解
位置:SpringMVC控制器方法形参定义前面
作用:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应。

@RequestBody、@RequestParam、@PathVariable区别和应用

区别:
  @RequestParam用于接收url地址传参或表单传参。
  @RequestBody用于接收json数据。
  @PathVariable用于接收路径参数,使用{参数名称}描述路径参数。
应用:
  后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广。
  如果发送非json格式数据,选用@RequestParam接收请求参数。
  采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值。

REST快速开发【重点】

问题1:每个方法的@RequestMapping注解中都定义了访问路径/books,重复性太高。
问题2:每个方法的@RequestMapping注解中都要使用method属性定义请求方式,重复性太高。
问题3:每个方法响应json都需要加上@ResponseBody注解,重复性太高。

解决问题1:在Controller类上使用@RequestMapping定义共同的访问路径。

 1 @Controller
 2 @RequestMapping("/books")
 3 public class BookController {
 4     
 5     @RequestMapping(method = RequestMethod.POST)
 6     public String save(@RequestBody Book book){
 7         System.out.println("book save..." + book);
 8         return "{'module':'book save'}";
 9     }
10     @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)
11     public String delete(@PathVariable Integer id){
12         System.out.println("book delete..." + id);
13         return "{'module':'book delete'}";
14     }
15     @RequestMapping(method = RequestMethod.PUT)
16     public String update(@RequestBody Book book){
17         System.out.println("book update..."+book);
18         return "{'module':'book update'}";
19     }
20     @RequestMapping(value = "/{id}" ,method = RequestMethod.GET)
21     public String getById(@PathVariable Integer id){
22         System.out.println("book getById..."+id);
23         return "{'module':'book getById'}";
24     }
25 
26     @RequestMapping(method = RequestMethod.GET)
27     public String getAll(){
28         System.out.println("book getAll...");
29         return "{'module':'book getAll'}";
30     }
31 }

解决问题2:使用@GetMapping @PostMapping @PutMapping @DeleteMapping代替@RequestMapping(method=RequestMethod.XXX)
  名称:@GetMapping @PostMapping @PutMapping @DeleteMapping
  类型:方法注解
  位置:基于SpringMVC的RESTful开发控制器方法定义上方
  作用:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求
  属性:value(默认):请求访问路径

 1 @Controller   
 2 @RequestMapping("/books")
 3 public class BookController {
 4 
 5 //    @RequestMapping( method = RequestMethod.POST)
 6     @PostMapping//使用@PostMapping简化Post请求方法对应的映射配置
 7     public String save(@RequestBody Book book){
 8         System.out.println("book save..." + book);
 9         return "{'module':'book save'}";
10     }
11 
12 //    @RequestMapping(value = "/{id}" ,method = RequestMethod.DELETE)
13     @DeleteMapping("/{id}")  //使用@DeleteMapping简化DELETE请求方法对应的映射配置
14     public String delete(@PathVariable Integer id){
15         System.out.println("book delete..." + id);
16         return "{'module':'book delete'}";
17     }
18 
19 //    @RequestMapping(method = RequestMethod.PUT)
20     @PutMapping   //使用@PutMapping简化Put请求方法对应的映射配置
21     public String update(@RequestBody Book book){
22         System.out.println("book update..."+book);
23         return "{'module':'book update'}";
24     }
25 
26 //    @RequestMapping(value = "/{id}" ,method = RequestMethod.GET)
27     @GetMapping("/{id}")    //使用@GetMapping简化GET请求方法对应的映射配置
28     public String getById(@PathVariable Integer id){
29         System.out.println("book getById..."+id);
30         return "{'module':'book getById'}";
31     }
32 
33 //    @RequestMapping(method = RequestMethod.GET)
34     @GetMapping      //使用@GetMapping简化GET请求方法对应的映射配置
35     public String getAll(){
36         System.out.println("book getAll...");
37         return "{'module':'book getAll'}";
38     }
39 }

解决问题3:在Controller类上使用@RestController注解,等同于@Controller与@ResponseBody两个注解组合功能
  名称:@RestController
  类型:类注解
  位置:基于SpringMVC的RESTful开发控制器类定义上方
  作用:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能

1 @RestController     //使用@RestController注解替换@Controller与@ResponseBody注解,简化书写
2 @RequestMapping("/books")
3 public class BookController {
4     ...//方法省略
5 }

设置对静态资源的访问放行

jsp等页面应由tomcat处理,但由于SpringMVC容器初始化类ServletContainersInitConfig的getServletMappings方法配置导致拦截所有请求都经过SpringMVC,所以报错。

 使用WebMvcConfigurationSupport添加资源过滤:

 1 @Configuration
 2 public class SpringMvcSupport extends WebMvcConfigurationSupport {
 3     //设置静态资源访问过滤,当前类需要设置为配置类,并被扫描加载
 4     @Override
 5     protected void addResourceHandlers(ResourceHandlerRegistry registry) {
 6         //当访问/pages/????时候,从/pages目录下查找内容
 7         registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
 8         registry.addResourceHandler("/js/**").addResourceLocations("/js/");                
 9         registry.addResourceHandler("/css/**").addResourceLocations("/css/");       
10     }
11 } 

拦截器

拦截器简介

拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。
作用:
  1.在指定的方法调用前后执行预先设定的代码。
  2.阻止原始方法的执行。

拦截器和过滤器的区别

归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术。
拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强。

入门案例

拦截器代码实现

定义拦截器

在controller包下定义一个类,实现HandlerInterceptor接口:

 1 @Component //注意当前类必须受Spring容器控制
 2 //定义拦截器类,实现HandlerInterceptor接口
 3 public class ProjectInterceptor implements HandlerInterceptor {
 4     @Override
 5     //原始方法调用前执行的内容
 6     //返回值类型可以拦截控制的执行,true放行,false终止
 7     public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
 8         System.out.println("preHandle..."+contentType);
 9         return true;
10     }
11 
12     @Override
13     //原始方法调用后执行的内容
14     public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
15         System.out.println("postHandle...");
16     }
17 
18     @Override
19     //原始方法调用完成后执行的内容
20     public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
21         System.out.println("afterCompletion...");
22     }
23 }

配置加载拦截器

定义配置类,继承WebMvcConfigurationSupport,实现addInterceptor方法(注意:扫描加载配置);添加拦截器并设定拦截的访问路径,路径可以通过可变参数设置多个:

 1 @Configuration
 2 public class SpringMvcSupport extends WebMvcConfigurationSupport {
 3     @Autowired
 4     private ProjectInterceptor projectInterceptor;
 5 
 6     @Override
 7     protected void addInterceptors(InterceptorRegistry registry) {
 8         //配置拦截器
 9         registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
10     }
11 }

另:可使用标准接口WebMvcConfigurer简化开发(缺点:该方法侵入式较强,直接与Spring强绑定)

 1 @Configuration
 2 @ComponentScan({"com.test.controller"})
 3 @EnableWebMvc
 4 //实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
 5 public class SpringMvcConfig implements WebMvcConfigurer {
 6     @Autowired
 7     private ProjectInterceptor projectInterceptor;
 8 
 9     @Override
10     public void addInterceptors(InterceptorRegistry registry) {
11         //配置多拦截器
12         registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
13     }
14 }

拦截器流程分析

拦截器参数

前置处理

1 //原始方法调用前执行的内容
2 //返回值类型可以拦截控制的执行,true放行,false终止
3 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
4     System.out.println("preHandle..."+contentType);
5     return true;
6 }

参数:
  request:请求对象
  response:响应对象
  handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
返回值:返回值为false,被拦截的处理器将不执行

后置处理

1 //原始方法调用后执行的内容
2 public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
3     System.out.println("postHandle...");
4 }

参数:modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整。
注意:如果处理器方法出现异常了,该方法不会执行。

完成后处理

1 //原始方法调用完成后执行的内容
2 public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
3     System.out.println("afterCompletion...");
4 }

参数:ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理。

拦截器链配置

配置第二个拦截器:

 1 @Configuration
 2 @ComponentScan({"com.test.controller"})
 3 @EnableWebMvc
 4 //实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
 5 public class SpringMvcConfig implements WebMvcConfigurer {
 6     @Autowired
 7     private ProjectInterceptor projectInterceptor;
 8     @Autowired
 9     private ProjectInterceptor2 projectInterceptor2;
10 
11     @Override
12     public void addInterceptors(InterceptorRegistry registry) {
13         //配置多拦截器
14         registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
15         registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");
16     }
17 }

多个连接器工作流程分析

当配置多个拦截器时,形成拦截器链。
拦截器链的运行顺序参照拦截器添加顺序为准。
当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行。
当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作。

 

posted @ 2023-07-25 20:28  溯鸣  阅读(30)  评论(0编辑  收藏  举报