文章目录
- SpringMVC
- 1、SpringMVC简单入门
- 2、请求与响应
- 2.1、请求映射路径
- 2.2、接收请求参数
- 2.3、接收请求体
- 2.4、Convertor接口
- 2.5、接收时间日期
- 2.6、响应数据
- 3、REST风格
- RESTful快速开发
- 4、异常处理器
- 5、拦截器
- 5.1、拦截器制作
- 5.2、拦截器三个方法的参数
- 5.3、拦截器链
SpringMVC
SpringMVC和Servlet技术栈等同,都用于controller层/web层开发。但是SpringMVC的开发难度比Servlet更简单。
1、SpringMVC简单入门
- 首先,导入SpringMVC坐标,以及servlet坐标
-
创建SpringMVC控制类,将这个类变成bean。 然后在这个类中写表现层的东西。使用@RequestMapping定义请求资源路径,使用@ResponseBody定义响应体。
-
新建一个springmvc的配置文件类加载Controller的bean。这里注意springmvc的配置文件和spring的配置文件要区分开,springmvc只导入表现层的bean,spring载入dao层和service层的bean。
- 需要定义一个配置类ServletContainerInitConfig配置Tomcat。(更规范地来说,就是配置web容器。需要加载springmvc的配置类以及spring的配置类。)==这个类也是web程序的启动类。==里面会对springmvc和spring的配置类进行加载,创建这两个配置的IoC容器。
其实这些东西都是很固定的,要用的时候只需要复制粘贴就可以了,只有一些是需要根据具体的需求进行修改的。
spring的IoC容器不要加载springmvc的bean,springmvc的IoC容器不要加载spring的bean。要实现这种功能,有两种方案:
- 精准控制两个容器的包扫描范围。这种方法比较简单。
- spring加载全部,不过排除掉controller的bean。springmvc只加载controller。
2、请求与响应
2.1、请求映射路径
注解@RequestMapping(“/brand”)是请求映射路径,用于设置当前控制器方法的资源请求路径。这个注解既可以用在controller类上,也可以用在方法上。类上的可以不用但是方法上的一定要用。controller中一个方法就是一个资源。
注意,不同的controller中资源的路径不要相同,如果有两个资源路径相同,当一个请求进来后,springmvc不知道要将该请求映射到哪个资源。事实上,如果写了两个相同路径的资源,springmvc在编译的时候就会报错了。
2.2、接收请求参数
(1)接收少量请求参数
直接在方法的参数上设置形参,这个形参就代表着请求的参数。
形参需要跟请求参数匹配:
- 方式一:形参的名字和请求参数的键名字一样,会自动匹配。
- 方式二:如果形参的名字和请求参数的键名字不一样,需要在形参之前使用
@RequestParam("请求参数键名")
来建立请求参数与形参之间的关系。
(2)接收大量请求参数
-
第一种:方法形参写一个pojo实体类(实体类的属性名字必须和请求参数的名字完全一致)。框架会自动将请求参数装配到实体的属性中,非常方便。(如果pojo中的属性有引用数据类型,请求参数需要写成引用对象名.引用对象属性的形式。)
-
第二种:方法参数写一个数组。请求参数的形式需要写成:
键相同,值不同。如此一来,所有的值会自动转换为一个数组。
-
第三种:方法参数写一个集合。形参前面需要写一个注解@RequestParam,这样一来,所有请求键值对会被视为一个大的请求参数,当方法参数又是集合类型时,springmvc会把键值对中的值装入集合中。如果不写注解,集合会被识别成pojo实体类,请求参数会被设置成集合的属性(报错,集合中不会有这些属性),而不是集合的数据。请求参数的写法跟上边的数组一样,键值对的所有键名需要一致。
2.3、接收请求体
首先先导入一个json依赖,用于实体类、数组类、集合类与json数据的相互转换。
由于springmvc不知道请求携带的是json数据,所以还要告诉springmvc要进行json数据转换。在springmvc的配置类上使用注解@EnableWebMvc
。
json数据在请求体中。所以,接收请求数据时,方法的形参前需要写个注解@RequestBody
。(要注意,实体类不要写构造方法,默认无参就可以了。getter和setter写上。)
2.4、Convertor接口
springmvc之所以能将请求参数/请求体中的数据转成Java中的对象,其实是使用了各种内置的springmvc转换器对象。
当 Spring MVC 框架内置的类型转换器不能满足需求时,开发者可以开发自己的类型转换器类。
自定义类型转换器类需要实现 Converter<S,T> 接口,重写 convert(S) 接口方法。之后,需要将该类注册成bean。
也可以采取另一种方式,使用匿名内部类来实现接口。详见下边的接收时间日期的第二种方式,就使用了这种方式。
如此一来,当在自动转换时,springmvc就多出了一个自动转换的选择。我们的自动转换类也能生效啦。
2.5、接收时间日期
实际上jdk8以前的日期时间处理都是用Date和Calendar类。不过这两玩意实在是太难用了,一直被喷,所以jdk8出现了新的日期时间处理LocalDate/LocalDateTime。
-
第一种,将请求参数转成Date类。
在方法参数中使用Date类型来作为参数类型。当请求参数中携带日期格式的值时,会自动转为Date对象。值得注意的是,请求参数中日期的格式应该为
yyyy/MM/dd
,如果要用其他格式,需要为方法参数加上注解@DateTimeFormat
-
第二种,将请求参数转成LocalDate/LocalDateTime。springmvc的内置转换类是没有这种转换的,所以需要自定义转换器。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.core.convert.converter.Converter; import org.springframework.http.converter.HttpMessageConverter; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @Configuration public class DateConfig { @Bean public Converter<String, LocalDate> localDateConverter() { //匿名内部类,实现了Convertor接口 return new Converter<>() { @Override public LocalDate convert(String source) { return LocalDate.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd")); } }; } @Bean public Converter<String, LocalDateTime> localDateTimeConverter() { //匿名内部类,实现了Convertor接口 return new Converter<>() { @Override public LocalDateTime convert(String source) { return LocalDateTime.parse(source, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")); } }; } }
2.6、响应数据
接收请求数据写在形参中,那么响应数据写在方法的返回值中。只需要将对象返回,之前导入的坐标会自动将对象转化为json数据响应。
主要是@ResponseBody在发挥作用。如果不写这个注解返回的响应就是文本了。
@ResponseBody会将返回的响应自动转化为json数据。
springmvc能够自动将return的数据转为json数据,是通过一个接口(不是Convertor接口)完成的。
3、REST风格
传统风格描述资源格式:
http://localhost/user/selectById?id=1
REST风格:
http://localhost/user/1
REST风格的优点是隐藏。既然做了隐藏,又该如何知道请求想要表达的是增、删、改、查中的哪种操作呢?
其实总结起来就是:请求资源限定为四种格式:GET、POST、PUT、DELETE。
客户端发出请求的时候,需要标明是哪一种格式。到服务器的表现层,会自动匹配到对应格式的资源。
这里就有两步:
-
客户端发出请求给特定控制器(控制器的路径是要写上的),标明是哪一种格式(目前是直接在postman里直接设置进行测试)
-
服务端对应的控制器接收请求,在控制器中匹配是哪一种格式,找到对应方法。
==这里,匹配是哪一种格式是由请求映射路径的注解@RequestMapping来进行的。现在@RequestMapping就不能只是简单地写资源路径,还要写上REST风格的行为动作(注解中加上一个参数method=),以便和请求匹配。==请求如果有路径变量,请求路径映射注解中要写上路径变量的占位符,而且方法形参中要写上@PathVariable注解。
RESTful快速开发
把@ResponseBody和@RequestMapping(”控制器路径“)从各个方法上提出来写到类名上,把@ResponseBody和@Controller合二为一叫做@RestController。把REST风格的行为动作在各个方法上写成@PostMapping()、@DeleteMapping(”/{路径变量名}“)等等。这样就大幅简化了开发。
注意:对于Post和Put这些没有路径变量的行为动作,请求体中是可以有json数据的。同样,直接在方法形参上注解@RequestBody就可以了。
4、异常处理器
各个层级都会出现异常,那么异常代码书写在哪一层?所有的异常均抛出到表现controller层进行处理。
如果每有一个异常就写代码进行处理,太过臃肿,使用aop思想处理表现层中的异常。
spring中定义了一个异常处理器统一集中地处理表现层中异常。在controller包中定义一个类:ProjectExceptionAdvice类,注解是@RestControllerAdvice然后在这个类中写方法拦截异常。
注意:这个类要被springmvc的配置类加载到。然后要在这个类的方法中写一个注解:@ExceptonHandler,里边写上表现层抛出的异常类型,根据异常类型到这个方法中执行。
5、拦截器
5.1、拦截器制作
在访问控制器资源的时候,首先进入拦截器方法进行处理,访问完控制器资源之后,也要进入拦截器方法进行一定处理。
- 制作拦截器功能类
制作一个拦截器类,放在controller包下。而且要把这个类变成一个bean然后被springmvc加载。
实现拦截器接口,重写三个方法。第一个方法preHandle()在访问控制器资源前执行,第二个方法postHandle()在访问控制器资源后执行,第三个方法afterCompletion()在第二个方法执行后执行。
==拦截器三个方法的参数就是拦截到的请求以及响应。==图片中没有写出来。
如果三个方法中,第一个方法return true,就能继续访问controller资源,然后进入第二个方法和第三个方法,return false,不能访问controller资源。return true就是放行,return false就是拒绝放行。
- 注册拦截器到springmvc中并配置拦截路径
定义一个配置类,继承WebMvcConfigurationSupport
,实现addInterceptor
方法,在方法中添加上边写的拦截器并设定拦截路径。拦截的路径可以写多个,并且可以用*通配符。比如/books/ *
表示拦截访问books下所有资源的请求。
注意使用@Configuration将这个类加载到SpringMVC的容器中。
除了以上方式外,有一种侵入式比较强但是比较方便的方法。同样要写一个拦截器类,然后让springmvc配置类实现WebMvcConfigurer
接口,然后重写方法添加拦截器并设置拦截路径。
5.2、拦截器三个方法的参数
5.3、拦截器链
如果有多个拦截器,可以设置这些拦截器的执行顺序。这就形成了一个拦截器链。
如果创建了拦截器1、拦截器2、拦截器3,这三个拦截器按照拦截器2、拦截器1、拦截器3的顺序进行注册。
当一个请求进来,它的经过顺序为:
- 2pre
- 1pre
- 3pre
- 访问controller的资源
- 3post
- 1post
- 2post
- 3after
- 1after
- 2after
如果请求在1pre被拒绝放行,它会跳到1after后即2after继续执行。
一个递归的操作。