赞
踩
MVC是一种软件架构的思想,将软件按照模型、视图、控制器来划分。
M:Model,模型层,指工程中的JavaBean,作用是处理数据
JavaBean分为两类:
一类称为实体类Bean:专门存储业务数据的,如 Student、User 等
一类称为业务处理 Bean:指 Service 或 Dao 对象,专门用于处理业务逻辑和数据访问。
V:View,视图层,指工程中的html或jsp等页面,作用是与用户进行交互,展示数据
C:Controller,控制层,指工程中的servlet,作用是接收请求和响应浏览
MVC的工作流程: 用户通过视图层发送请求到服务器,在服务器中请求被Controller接收,Controller 调用相应的Model层处理请求,处理完毕将结果返回到Controller,Controller再根据请求处理的结果 找到相应的View视图,渲染数据后最终响应给浏览器
SpringMVC是一种基于Java实现MVC模型的轻量级Web框架
SpringMVC是Spring的一个后续产品,是Spring的一个子项目
SpringMVC 通过一套注解,让一个简单的Java类成为处理请求的控制器,而无须实现任何接口。同时它还支持RESTful编程风格的请求。
(1)用户发送请求至前端控制器DispatcherServlet
(2)DispatcherServlet收到请求调用HandlerMapping处理器映射器。
(3)处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。
(4)DispatcherServlet调用HandlerAdapter处理器适配器。
(5)HandlerAdapter经过适配调用具体的Handler处理器(Controller,也叫后端控制器)。Controller执行完成返回ModelAndView。
(6)HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet。
(7)DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
(8)ViewReslover解析后返回具体View视图。
(9)DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。
(10)DispatcherServlet响应用户。
(方式有多种,这里只是其中一种)
(1)创建普通maven项目
(2)在项目的main文件夹下新建文件夹webapps
(3)选择File--->Project Structure
(4)选择Facets--->+--->web
(5)选择项目中的对应模块
(6)修改Deployment Descriptors下web.xml路径
(7)修改Web Resource Directory路径
(1)导入SpringMVC相关坐标
- <dependencies>
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-webmvc</artifactId>
- <version>5.3.8</version>
- </dependency>
- </dependencies>
(2)在webapps下创建hello.jsp视图页面
- <%@ page contentType="text/html;charset=UTF-8" language="java" %>
- <html>
- <head>
- <title>Title</title>
- </head>
- <body>
- 你好,SpringMVC!
- </body>
- </html>
(3)在com.gcxy.controller包下创建HelloController控制器
- package com.gcxy.controller;
-
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
-
- @Controller
- public class HelloController {
- @RequestMapping("/hello")
- public String hello(){
- System.out.println("hello···");
- return "hello.jsp";
- }
- }
(4)在resources文件夹下创建SpringMVC核心文件spring-mvc.xml,并添加注解扫描
(5)web.xml中配置SpringMVC核心控制器DispathcerServlet
- <servlet>
- <servlet-name>dispatcherServlet</servlet-name>
- <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
- <init-param>
- <param-name>contextConfigLocation</param-name>
- <param-value>classpath:spring-mvc.xml</param-value>
- </init-param>
- <load-on-startup>1</load-on-startup>
- </servlet>
- <servlet-mapping>
- <servlet-name>dispatcherServlet</servlet-name>
- <url-pattern>/</url-pattern>
- </servlet-mapping>
(6)配置Tomcat并启动
选择Run--->Edit Configurations
(7)客户端向路径 http://localhost:8080/mvc_demo_war_exploded/hello发起请求测试
(1)添加servlet依赖,并新建com.gcxy.config包
- <dependency>
- <groupId>javax.servlet</groupId>
- <artifactId>javax.servlet-api</artifactId>
- <version>4.0.1</version>
- </dependency>
(2)在config包下创建配置类SpringMvcConfig替换spring-mvc.xml
- package com.gcxy.config;
-
- import org.springframework.context.annotation.ComponentScan;
- import org.springframework.context.annotation.Configuration;
-
- @Configuration
- @ComponentScan("com.cqgcxy.controller")
- public class SpringMvcConfig {
- }
(3)在config包下创建配置类ServletContainersInitConfig替换web.xml
- package com.gcxy.config;
-
- import org.springframework.web.context.WebApplicationContext;
- import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
- import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
-
- public class ServletContainersInitConfig extends AbstractDispatcherServletInitializer {
- @Override
- protected WebApplicationContext createServletApplicationContext() {
- AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
- ctx.register(SpringMvcConfig.class);
- return ctx;
- }
-
- @Override
- protected String[] getServletMappings() {
- return new String[]{"/"};
- }
-
- protected WebApplicationContext createRootApplicationContext() {
- return null;
- }
- }
(4)启动Tomcat,并测试
测试结果如下:
用于建立请求URL和处理请求方法之间的对应关系
类上,请求URL的第一级访问目录。此处不写的话,就相当于应用的根目录
方法上,请求URL的第二级访问目录,与类上的使用@ReqquestMapping标注的一级目录一起组成访问虚拟路径
value:用于指定请求的URL。它和path属性的作用是一样的
method:用于指定请求的方式
params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的key和value必须和配置的一模一样
例如:
params = {"accountName"},表示请求参数必须有accountName
SpringMVC有默认组件配置,默认组件都是Dispatcherservlet.properties配置文件中配置的,该配置文件地址
org/springframework/web/ servlet/DispatcherServlet.properties,该文件中配置了默认的视图解析器,如下:
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceviewResolver
翻看该解析器源码,可以看到该解析器的默认设置,如下:
- public static final String REDIRECT_URL_PREFIX = "redirect:"; //重定向前缀
- public static final String FORWARD_URL_PREFIX = "forward:"; //请求转发前缀
-
- private String prefix = ""; //视图名称前缀
- private String suffix = ""; //视图名称后缀
页面跳转
直接返回字符串
通过ModelAndView对象返回
回写数据
直接返回字符串
返回对象或集合
json转换
通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行如下配置:
- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
- <property name="messageConverters">
- <list>
- <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
- </bean>
- </list>
- </property>
- </bean>
同时使用<mvc:annotation-driven>默认底层就会集成jackson进行对象或集合的json格式字符串的转换。因此,我们可以使用mvc的注解驱动代替上述为处理器适配器配置消息转换参数的配置。
<mvc:annotation-driven/>
在SpringMVC项目搭建的基础上完成下面步骤
(1)配置视图解析器
xml方式:
- <bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
- <property name="prefix" value="/WEB-INF/views/"></property>
- <property name="suffix" value=".jsp"></property>
- </bean>
- //在spring配置类中加入视图解析器的配置
- @Bean
- public InternalResourceViewResolver internalResourceViewResolver(){
- InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
- viewResolver.setPrefix("/WEB-INF/views/");
- viewResolver.setSuffix(".jsp");
- return viewResolver;
- }
- @Controller
- public class HelloController {
- @RequestMapping("/hello")
- public String hello(){
- System.out.println("hello···");
- return "hello";
- }
- }
(2)返回字符串
直接返回字符串:此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转
资源地址:/WEB-INF/views/hello.jsp
返回带有前缀的字符串:
转发:forward:/WEB-INF/views/hello.jsp
重定向:redirect:/index.jsp
- @RequestMapping("/forward")
- public String forword(){
- System.out.println("forward···");
- return "forward:/WEB-INF/views/index.jsp";
- }
- @RequestMapping("/redirect")
- public String redirect(){
- System.out.println("redirect···");
- return "redirect:/login.jsp";
- }
(3)返回ModelAndView对象
修改hello.jsp页面
- <%@ page contentType="text/html;charset=UTF-8" language="java" %>
- <html>
- <head>
- <title>Title</title>
- </head>
- <body>
- 你好,SpringMVC!${username}
- </body>
- </html>
- @RequestMapping("/hello2")
- public ModelAndView hello2(){
- //Model:模型,用于封装数据
- //View:视图,用于展示数据
- ModelAndView modelAndView = new ModelAndView();
- modelAndView.addObject("username","pep7chiao");
- modelAndView.setViewName("hello");
- return modelAndView;
- }
-
- @RequestMapping("/hello3")
- public ModelAndView hello3(ModelAndView modelAndView){
- modelAndView.addObject("username","pep7chiao");
- modelAndView.setViewName("hello");
- return modelAndView;
- }
-
- @RequestMapping("/hello4")
- public String hello4(Model model){
- model.addAttribute("username","messi");
- return "hello";
- }
-
- @RequestMapping("/hello5")
- public String hello5(HttpServletRequest reqest){ //HttpServletRequest需要添加依赖
- reqest.setAttribute("username","ronaldo");
- return "hello";
- }
(1)直接返回字符串
通过SpringMVC框架注入的response对象,使用response.getWriter().print(“hello world”)回写数据,此时不需要视图跳转,业务方法返回值为void。
- @RequestMapping("/data1")
- public void data1(HttpServletResponse response) throws IOException {
- response.setContentType("text/html;charset=utf-8");
- response.getWriter().print("天天开心,拒绝焦虑");
- }
- @RequestMapping(value = "/data2",produces = "text/html;charset=utf-8")
- @ResponseBody
- public String data2(){
- return "拒绝焦虑";
- }
(2)返回对象或集合
导入json相关依赖
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.9.0</version>
- </dependency>
xml方式:spring核心配置文件中加以下配置
<mvc:annotation-driven/>
@EnableWebMvc
- package com.gcxy.entity;
-
- public class Phone {
- // phone_id bigint
- private Long phoneId;
- // brand_id bigint
- private Long brandId;
- // model_number varchar
- private String modelNumber;
- // capacity int
- private Integer capacity;
-
- public Long getPhoneId() {
- return phoneId;
- }
-
- public void setPhoneId(Long phoneId) {
- this.phoneId = phoneId;
- }
-
- public Long getBrandId() {
- return brandId;
- }
-
- public void setBrandId(Long brandId) {
- this.brandId = brandId;
- }
-
- public String getModelNumber() {
- return modelNumber;
- }
-
- public void setModelNumber(String modelNumber) {
- this.modelNumber = modelNumber;
- }
-
- public Integer getCapacity() {
- return capacity;
- }
-
- public void setCapacity(Integer capacity) {
- this.capacity = capacity;
- }
-
- @Override
- public String toString() {
- return "Phone{" +
- "phoneId=" + phoneId +
- ", brandId=" + brandId +
- ", modelNumber='" + modelNumber + '\'' +
- ", capacity=" + capacity +
- '}';
- }
- }
- @RequestMapping("/data3")
- @ResponseBody
- public Phone data3() {
- Phone phone = new Phone();
- phone.setPhoneId(1L);
- phone.setBrandId(1L);
- phone.setModelNumber("mate60");
- phone.setCapacity(256);
- return phone;
- }
name=value&name=value.....
基本类型参数
POJO类型参数
数组类型参数
集合类型参数
注意:当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定
在SpringMVC多种数据类型响应的基础上完成下面步骤
(1)Controller中的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8888/phone/param1?phoneId=1&modelNumber=mate60
- @Controller
- @RequestMapping("/phone")
- public class PhoneController {
- @RequestMapping("/param1")
- @ResponseBody
- public void param1(int phoneId,String modelNumber){
- System.out.println(phoneId);
- System.out.println(modelNumber);
- }
- }
(2)当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显示的绑定。
http://localhost:8888/phone/param2?phoneId=1&mNumber=mate60
- @RequestMapping("/param2")
- @ResponseBody
- public void param2(int phoneId,@RequestParam("mNumber") String modelNumber){
- System.out.println(phoneId);
- System.out.println(modelNumber);
- }
(3)注解@RequestParam还有如下参数可以使用:
value::请求参数名称
required::指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错
defaultValue:当没有指定请求参数时,则使用指定的默认值赋值
http://localhost:8888/phone/param3?phoneId=1
- @RequestMapping("/param3")
- @ResponseBody
- public void param3(int phoneId,@RequestParam(value = "mNumber",required = false,defaultValue = "mate60") String modelNumber){
- System.out.println(phoneId);
- System.out.println(modelNumber);
- }
Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8888/phone/param4?phoneId=1&modelNumber=mate60
- package com.cqgcxy.entity;
-
- public class Phone {
- private Long phoneId;
- private String modelNumber;
- //忽略getter和setter方法
- }
- @RequestMapping("/param4")
- @ResponseBody
- public void param4(Phone phone){
- System.out.println(phone.getPhoneId());
- System.out.println(phone.getModelNumber());
- }
Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。
http://localhost:8888/phone/param5?nets=4G&nets=5G
- @RequestMapping("/param5")
- @ResponseBody
- public void param5(String[] nets){
- System.out.println(Arrays.asList(nets));
- }
http://localhost:8888/phone/param6?nets=4G&nets=5G
- @RequestMapping("/param6")
- @ResponseBody
- public void param6(@RequestParam List<String> nets){
- System.out.println(nets);
- }
获取集合参数时,要将集合参数包装到一个POJO中
- package com.gcxy.vo;
-
- import com.cqgcxy.entity.Phone;
- import java.util.List;
-
- public class PhoneStoreDTO {
-
- private String storeName;
- private List<Phone> phoneList;
-
- //getter和setter方法
- }
- <form action="${pageContext.request.contextPath}/phone/param7" method="post">
- <p>
- 店名:<input type="text" name="storeName">
- </p>
- <p>
- 手机编号1:<input type="text" name="phoneList[0].phoneId">
- </p>
- <p>
- 手机型号1:<input type="text" name="phoneList[0].modelNumber">
- </p>
- <p>
- 手机编号2:<input type="text" name="phoneList[1].phoneId">
- </p>
- <p>
- 手机型号2:<input type="text" name="phoneList[1].modelNumber">
- </p>
- <p>
- <input type="submit" value="提交">
- </p>
- </form>
- @RequestMapping(value = "/param7",method = RequestMethod.POST)
- @ResponseBody
- public void param7(PhoneStoreDTO phoneStoreDTO){
- System.out.println(phoneStoreDTO);
- }
乱码问题:当表单提交post请求时,数据会出现乱码,可以设置一个过滤器来进行编码的过滤。
xml方式:
- <filter>
- <filter-name>characterEncodingFilter</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>
- </filter>
- <filter-mapping>
- <filter-name>characterEncodingFilter</filter-name>
- <servlet-name>dispatcherServlet</servlet-name>
- </filter-mapping>
- //ServletContainersInitConfig配置类中加入
- @Override
- protected Filter[] getServletFilters() {
- CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
- characterEncodingFilter.setEncoding("UTF-8");
- return new Filter[]{characterEncodingFilter};
- }
当前端使用json格式传输数据时,方法参数位置使用@RequestBody可以直接接收集合数据,而无需使用POJO进行包装。
使用Postman传输json格式数据
- <dependency>
- <groupId>com.fasterxml.jackson.core</groupId>
- <artifactId>jackson-databind</artifactId>
- <version>2.9.0</version>
- </dependency>
- @RequestMapping(value = "/param8",method = RequestMethod.POST)
- @ResponseBody
- public void param8(@RequestBody List<Phone> phoneList){
- System.out.println(phoneList);
- }
Restful是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件。主要用于客户端和服务器交互类的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。
Restful风格的请求是使用“url+请求方式”表示一次请求目的,HTTP协议里面四个表示操作方式的动词如下:
GET:用于获取资源
POST:用于新建资源
PUT:用于更新资源
DELETE:用于删除资源
例如:
/user/1 GET :得到id =1的user
/user/1 DELETE:删除id = 1的user
/user/1 PUT:更新id = 1的user
/user POST:新增user
上述url地址/user/1中的1就是要获得的请求参数,在SpringMVC中可以使用占位符进行参数绑定。地址/user/1可以写成/user/{id},占位符{id}对应的就是1的值。在业务方法中我们可以使用@PathVariable注解进行占位符的匹配获取工作。
http://localhost:8888/phone/param9/7
- @RequestMapping("/param9/{phoneId}")
- @ResponseBody
- public void param9(@PathVariable("phoneId") Integer phoneId){
- System.out.println(phoneId);
- }
SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。
但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。
注意:@DateTimeFormat 用于声明一个对象属性或者方法参数会被格式化为日期或时间。和@RequestParam注解结合使用时,Spring 会调用 FormattingConversionService.convert(Object, TypeDescriptor, TypeDescriptor) 将日期时间字符串转换成日期时间类型。
自定义类型转换器的开发步骤:
(1)
- //定义转换器类实现Converter接口
- public class DateConverter implements Converter<String, Date> {
- public Date convert(String dateStr) {
- SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
- Date date = null;
- try {
- date = format.parse(dateStr);
- } catch (ParseException e) {
- e.printStackTrace();
- }
- return date;
- }
- }
- //在springmvc配置类中配置自定义类型转换器
- @Bean
- public WebMvcConfigurer webMvcConfigurer(){
- return new WebMvcConfigurer() {
- public void addFormatters(FormatterRegistry registry) {
- registry.addConverter(new DateConverter());
- }
- };
- }
http://localhost:8888/phone/param10/2022-10-10%202008:08:08
- @RequestMapping("/param10/{date}")
- @ResponseBody
- public void param10(@PathVariable("date") Date date){
- System.out.println(date);
- }
(2)Spring MVC 接收 LocalDate、LocalTime 和 LocalDateTime Java 8 时间类型参数
- org.springframework.web.bind.annotation.RequestParam
- org.springframework.format.annotation.DateTimeFormat
@RequestParam 比较常见,用于标注 Controller 中方法的参数;
@DateTimeFormat 用于声明一个对象属性或者方法参数会被格式化为日期或时间。两个注解结合使用时,Spring 会调用 FormattingConversionService.convert(Object, TypeDescriptor, TypeDescriptor) 将日期时间字符串转换成日期时间类型。
示例如下:
将 2020-01-29 转换成 LocalDate
- //http://localhost:8888/phone/param11/2022-02-02
- @RequestMapping("/param11/{date}")
- @ResponseBody
- public void param11(@DateTimeFormat(iso = DateTimeFormat.ISO.DATE) @PathVariable("date") LocalDate date){
- System.out.println(date);
- }
- //http://localhost:8888/phone/param12?date=2022-02-02
- @RequestMapping("/param12")
- @ResponseBody
- public void param12(@RequestParam @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate date){
- System.out.println(date);
- }
- //http://localhost:8888/phone/param13?time=08:08:08
- @RequestMapping("/param13")
- @ResponseBody
- public void param13(@RequestParam @DateTimeFormat(pattern = "HH:mm:ss") LocalTime time){
- System.out.println(time);
- }
- //http://localhost:8888/phone/param14?dateTime=2022-08-08 08:08:08
- @RequestMapping("/param14")
- @ResponseBody
- public void param14(@RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime dateTime){
- System.out.println(dateTime);
- }
注意:
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)
使用@CookieValue可以获得指定Cookie的值
(1)@RequestHeader
使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)
@RequestHeader注解的属性如下:
value:请求头的名称
required:是否必须携带此请求头
- @RequestMapping("/param15")
- @ResponseBody
- public void param15(@RequestHeader(value = "user-Agent",required = false) String userAgent){
- System.out.println(userAgent);
- }
(2)@CookieValue
使用@CookieValue可以获得指定Cookie的值
@CookieValue注解的属性如下:
value:指定cookie的名称
required:是否必须携带此cookie
- @RequestMapping("/param16")
- @ResponseBody
- public void param16(@CookieValue("JSESSIONID") String jSessionId){
- System.out.println(jSessionId);
- }
(1)导入fileupload和io坐标
- <dependency>
- <groupId>commons-fileupload</groupId>
- <artifactId>commons-fileupload</artifactId>
- <version>1.2.2</version>
- </dependency>
- <dependency>
- <groupId>commons-io</groupId>
- <artifactId>commons-io</artifactId>
- <version>2.4</version>
- </dependency>
(2)配置文件上传解析器
- @Bean
- public MultipartResolver multipartResolver(){
- CommonsMultipartResolver commonsMultipartResolver = new CommonsMultipartResolver();
- commonsMultipartResolver.setMaxUploadSize(5242880);
- commonsMultipartResolver.setMaxUploadSizePerFile(5242880);
- commonsMultipartResolver.setDefaultEncoding("UTF-8");
- return commonsMultipartResolver;
- }
(3)编写文件上传代码
- @RequestMapping("/upload")
- @ResponseBody
- public void upload(MultipartFile file) throws IOException {
- String originalFilename = file.getOriginalFilename();
- System.out.println("文件名称:"+originalFilename);
- file.transferTo(new File("D:\\upload\\"+originalFilename));
- }
测试结果如下:
ResponseEntity用于控制器方法的返回值类型,该控制器方法的返回值就是响应到浏览器的响应报文
使用ResponseEntity实现下载文件的功能
-
- @RequestMapping("/down")
- public ResponseEntity<byte[]> testDown(HttpSession session) throws IOException {
- //获取ServletContext对象
- ServletContext servletContext = session.getServletContext();
- //获取服务器中文件的真实路径
- String realPath = servletContext.getRealPath("img");
- realPath = realPath + File.separator + "测试.xlsx";
- //创建输入流
- InputStream is = new FileInputStream(realPath);
- //创建字节数组,is.available()获取输入流所对应文件的字节数
- byte[] bytes = new byte[is.available()];
- //将流读到字节数组中
- is.read(bytes);
- //创建HttpHeaders对象设置响应头信息
- MultiValueMap<String, String> headers = new HttpHeaders();
- //设置要下载方式以及下载文件的名字
- headers.add("Content-Disposition", "attachment;filename=测试.xlsx");
- //设置响应状态码
- HttpStatus statusCode = HttpStatus.OK;
- //创建ResponseEntity对象
- ResponseEntity<byte[]> responseEntity = new ResponseEntity<>(bytes, headers, statusCode);
- //关闭输入流
- is.close();
- return responseEntity;
-
- }
SpringMVC中的拦截器用于拦截控制器方法的执行
SpringMVC中的拦截器需要实现HandlerInterceptor
SpringMVC的拦截器必须在SpringMVC的配置文件中进行配置
Spring MVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。
将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体实现。
7.3 拦截器的方法说明
方法名 | 说明 |
---|---|
preHandle | 方法将在请求处理之前进行调用,该方法的返回值是布尔值Boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor的preHandle方法 |
postHandle | 该方法是在当前请求进行处理之后被调用,前提是preHandle方法的返回值为true时才能被调用,且它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作 |
afterCompletion | 该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true时才能被调用 |
区别 | 过滤器 | 拦截器 |
---|---|---|
使用范围 | 是servlet规范中的一部分,任何Java Web工程都可以使用 | 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用 |
拦截范围 | 在url-pattern中配置了/*之后,可以对所有要访问的资源拦截 | 只会拦截访问的控制器方法,如果访问的是jsp,html,css,image或者js 是不会进行拦截的 |
①若每个拦截器的preHandle()都返回true
此时多个拦截器的执行顺序和拦截器在SpringMVC的配置文件的配置顺序有关:
preHandle()会按照配置的顺序执行,而postHandle()和afterCompletion()会按照配置的反序执行
②若某个拦截器的preHandle()返回了false
preHandle()返回false和它之前的拦截器的preHandle()都会执行,postHandle()都不执行,返回false 的拦截器之前的拦截器的afterCompletion()会执行
在SpringMVC获取请求参数的基础上完成下面步骤
(1)创建拦截器
- package com.gcxy.config.intercepter;
-
- import org.springframework.util.ObjectUtils;
- import org.springframework.util.StringUtils;
- import org.springframework.web.servlet.HandlerInterceptor;
- import org.springframework.web.servlet.ModelAndView;
-
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import javax.servlet.http.HttpSession;
-
- public class LoginIntercepter implements HandlerInterceptor {
- /**
- * 预处理:可以进行编码、安全控制、权限校验等处理
- * 在业务处理器处理请求之前被调用
- */
- @Override
- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
- String authorization = request.getHeader("Authorization");
- if (ObjectUtils.isEmpty(authorization)) {
- response.setCharacterEncoding("utf-8");
- response.setContentType("application/json; charset=utf-8");
- response.getWriter().write("权限不足");
- return false;
- }
- HttpSession session = request.getSession();
- Object obj = session.getAttribute(authorization);
- if(ObjectUtils.isEmpty(obj)){
- response.getWriter().write("token已过期");
- return false;
- }
- return true;
- }
-
- /**
- * 后处理(调用了Service并返回ModelAndView,但未进行页面渲染)
- * 在业务处理器处理请求执行完成后,生成视图之前执行
- */
- @Override
- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
- }
-
- /**
- * 在DispatcherServlet完全处理完请求后被调用,可用于清理资源等
- * 返回处理(已经渲染了页面)
- */
- @Override
- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
- }
- }
(2)配置拦截器
- @Configuration
- @ComponentScan("com.cqgcxy.controller")
- @EnableWebMvc
- public class SpringMvcConfig implements WebMvcConfigurer{
-
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- registry.addInterceptor(new LoginIntercepter()).addPathPatterns(new String[]{"/**"}).excludePathPatterns(new String[]{"/login"});
- }
-
- }
(3)测试拦截器
导入Lombok依赖
- <dependency>
- <groupId>org.projectlombok</groupId>
- <artifactId>lombok</artifactId>
- <version>1.18.20</version>
- </dependency>
- package com.gcxy.entity;
-
- import lombok.Data;
-
- @Data
- public class Account {
- private String username;
- private String password;
- }
- package com.gcxy.controller;
-
- import com.cqgcxy.entity.Account;
- import org.springframework.web.bind.annotation.RequestBody;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.RestController;
-
- import javax.servlet.http.HttpSession;
- import java.util.UUID;
-
- @RestController
- public class AccountController {
-
- @RequestMapping(value = "/login",produces = "text/html;charset=utf-8")
- public String login(@RequestBody Account account, HttpSession session) {
- //模拟登录验证
- if("admin".equals(account.getUsername())&&"123456".equals(account.getPassword())){
- UUID uuid = UUID.randomUUID();
- session.setAttribute(uuid.toString(),account);
- return uuid.toString();
- }
- return "账号或密码错误";
- }
- }
测试结果如下:
1.客户端使用Postman向获取参数类型的资源发起请求:
2.客户端使用Postman向/login发起请求 :
3. 客户端使用Postman请求头中携带上登录时返回的字符串,重新请求获取参数类型中刚刚请求的资源:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。