前言
1个最简化版的web框架应具备以下3大功能:
- 接收客户端http请求-
- 获取http请求的参数
- 响应客户端字符串
SpringMVC是Spring对Web框架的1个解决方案
- SpringMVC提供了一个总的前端控制器Servlet,用于接收Tomcat的http请求;
- 定义了一套路由策略(URL--->Handler的映射)和处理器(handler);
- 将handler处理的结果使用视图解析技术响应给前端;
SpringMVC替代并细化了之前Servler的功能,SpringMVC把之前自己写的Servlet也就是Controller层,拆分成了2大部分:
- 前端分发器(Dispatcher):本质是1个由springMVC提供Servlet,对前端请求进行统一分发;
- 处理器: 我们自己写的1个类,用于接收前端分发器分发的HTTP请求,处理业务逻辑;
- web.xml配置文件,锁定1个DispatcherServlet分发器;
- 分发器将Tomcat接收到的所有http请求统一起来,通过@RequestMapping注解,匹配到各个处理器,分发不同的http请求;(路由系统)
- 各个处理类通过@RequestMapping 就是处理业务逻辑的类;
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>spring</artifactId> <groupId>com.zhanggen</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>day05-mvc</artifactId> <packaging>war</packaging> <name>day05-mvc Maven Webapp</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <!--springmvc核心--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.6.RELEASE</version> </dependency> <!--jackson--> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.10</version> </dependency> <!--lombok--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.20</version> </dependency> </dependencies> <build> <finalName>day05-mvc</finalName> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.1.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.22.1</version> </plugin> <plugin> <artifactId>maven-war-plugin</artifactId> <version>3.2.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> </project>
2.2.
DispatcherServlet
前端分发器dispatcherServlet,明确这个分发器可以分发的URL;
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> <!--Tomcat配置文件中声明servlet(前端控制器)--> <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> </servlet> <servlet-mapping> <servlet-name>dispatcherServlet</servlet-name> <!--在当前项目中就表示拦截所有--> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
------------------------------------------------------------------------------------
spring mvc的前端配置器配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--SpringMvc的配置文件--> <!--注解扫描--> <context:component-scan base-package="com.zhanggen.controller"/> <!--注解驱动--> <mvc:annotation-driven/> </beans>
2.3.controller层(处理器)
package com.zhanggen.controller; import com.zhanggen.domain.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller //将当前类创建的对象放到spring的IOC容器 public class UserController { //给当前方法绑定1个访问路径:http://127.0.0.1:8080/user/demo1?name=zhanggen&age=18 @RequestMapping("/user/demo1") //将当前对象的返回值,转换成json格式返回 @ResponseBody public User demo1(String name, Integer age) { //直接向spring Mvc要(不在接收参数) User user = new User(name, age); //直接返回对象 return user; } }
3.Spring Mcv执行流程
但是对应一些静态文件,比如js、html、css、jpg等,是不需要由springMVC的dispatcher分发器进行路由分发的,而是直接定位到对应的静态资源,
通过修改springMVC的配置文件告诉springMVC哪些是静态资源的URL路径,当用户访问这些路径时进行放行;
<!--释放静态资源--> <mvc:resources mapping="/index.html" location="/"/> <mvc:resources mapping="/css/*" location="/css/"/> <mvc:resources mapping="/js/*" location="/js/"/> <mvc:resources mapping="/fonts/*" location="/fonts/"/> <mvc:resources mapping="/img/*" location="/img/"/> <mvc:resources mapping="/js/*" location="/js/"/>
-
value: 等同于path,用于为当前方法绑定访问路径
-
method:用于限制请求类型,如果省略此选项,代表不对请求类型做限制
package com.zhanggen.controller; import com.zhanggen.domain.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.ResponseBody; @Controller //将当前类创建的对象放到spring的IOC容器 public class UserController { //给当前方法绑定1个访问路径:http://127.0.0.1:8080/user/demo1?name=zhanggen&age=18 @RequestMapping( path ={"/user/demo1"}) //将当前对象的返回值,转换成json格式返回 @ResponseBody public User demo1(String name, Integer age) { //直接向spring Mvc要(不在接收参数) User user = new User(name, age); //直接返回对象 return user; } @ResponseBody //支持为1个方法绑定多个URL路径 @RequestMapping(path = {"/user/demo2","/user/demo3"}) public User demo2(String name, Integer age) { //直接向spring Mvc要(不在接收参数) User user = new User(name, age); //直接返回对象 return user; } @ResponseBody //method:用于现在当前方法可以处理,哪种请求方式 //method:默认不写可以支持所有请求 @RequestMapping(path = "/user/demo3",method = {RequestMethod.GET,RequestMethod.POST}) public User demo3(String name, Integer age) { //直接向spring Mvc要(不在接收参数) User user = new User(name, age); //直接返回对象 return user; } }
二、接收和响应请求
在SpringMVC中,可以使用多种数据类型作为处理器的参数来接收前端传入的参数;
前端代码如下
package com.zhanggen.controller; import com.zhanggen.domain.User; import org.springframework.format.annotation.DateTimeFormat; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.util.Date; import java.util.List; @Controller //将当前类创建的对象放到spring的IOC容器 public class UserController { //给当前方法绑定1个访问路径:http://127.0.0.1:8080/user/demo1?name=zhanggen&age=18 @RequestMapping(path = {"/user/demo1"}) //将当前对象的返回值,转换成json格式返回 @ResponseBody public User demo1(String name, Integer age) { //直接向spring Mvc要(不在接收参数) User user = new User(name, age, null); //直接返回对象 return user; } @ResponseBody //支持为1个方法绑定多个URL路径 @RequestMapping(path = {"/user/demo2", "/user/demo3"}) public User demo2(String name, Integer age) { //直接向spring Mvc要(不在接收参数) User user = new User(name, age, null); //直接返回对象 return user; } @ResponseBody //method:用于现在当前方法可以处理,哪种请求方式 //method:默认不写可以支持所有请求 @RequestMapping(path = "/user/demo3", method = {RequestMethod.GET, RequestMethod.POST}) public User demo3(String name, Integer age) { //直接向spring Mvc要(不在接收参数) User user = new User(name, age, null); //直接返回对象 return user; } //接收简单类型参数:/user/demo5?name=张三&age=18 @ResponseBody @RequestMapping(path = "/user/demo5") public String demo5(String name, Integer age) { System.out.println(name); System.out.println(age); return "ok"; } //接收参数--对象类型参数:/user/demo6?name=张三&age=18 @ResponseBody @RequestMapping(path = "/user/demo6") public String demo6(User user) { System.out.println(user); return "ok"; } //接收数组类型的参数 @ResponseBody @RequestMapping(path = "/user/demo7") public String demo7(String[] names) { for (String name : names) { System.out.println(name); } return "ok"; } //接收时间类型的参数-注解版 @ResponseBody @RequestMapping(path = "/user/demo8") public String demo8(@DateTimeFormat(pattern = "yyyy-MM-dd") Date myDate) { System.out.println(myDate); return "ok"; } //接收时间类型的参数-xml版本 @ResponseBody @RequestMapping(path = "/user/demo9") public String demo9(Date myDate) { System.out.println(myDate); return "ok"; } @ResponseBody @RequestMapping(path = "/user/demo10", method = {RequestMethod.GET, RequestMethod.POST}) public String demo10(MultipartFile picture1) throws IOException { System.out.println("-----------" + picture1); //定义好目标文件 File file = new File("D:/upload/" + picture1.getOriginalFilename()); System.out.println(file); //文件转存 picture1.transferTo(file); return "ok"; } // //接收集合参数:把集合作为对象的1个属性 // @ResponseBody // @RequestMapping(path = "/user/demo11") // public String demo11(User user) { // System.out.println(user); // return "ok"; // } //可以不用把集合参数作为对象的1个属性,指定1个接收参数名称 @ResponseBody @RequestMapping(path = "/user/demo11") public String demo12(@RequestParam List<String> hobby) { for (String s : hobby) { System.out.println(s); } return "ok"; } // @RequestParam注解使用 @ResponseBody @RequestMapping("/user/demo12") public String demo12( //后端修改前端参数的名称usERName为userName @RequestParam("usERName") String userName, // 设置password参数的默认值为123 @RequestParam(defaultValue = "123") String password) { System.out.println(userName); System.out.println(password); return "ok"; } //接收请求体中包含参数信息Ajax请求 @ResponseBody @RequestMapping("/user/demo13") public String demo13(String name, Integer age) { System.out.println(name); System.out.println(age); return "ok"; } //接收请求体中不包含参数信息的Ajax请求 @ResponseBody @RequestMapping("/user/demo14") public String demo14(@RequestBody User user) { System.out.println(user); return "ok"; } }
1.接收
//接收简单类型参数:/user/demo5?name=张三&age=18 @ResponseBody @RequestMapping(path ="/user/demo5") public String demo5(String name,Integer age){ System.out.println(name); System.out.println(age); return "ok"; }
2.接收
//接收参数--对象类型参数:/user/demo6?name=张三&age=18 @ResponseBody @RequestMapping(path = "/user/demo6") public String demo6(User user){ System.out.println(user); return "ok"; }
3.接收
//接收数组类型的参数 @ResponseBody @RequestMapping(path = "/user/demo7") public String demo7(String[] names) { for (String name : names) { System.out.println(name); } return "ok"; }
4.接收
@GetMapping("/order/page") public ResultInfo findByPage( @RequestParam("page") Integer pageNum, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date beginTime, @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date endTime, Integer pageSize, Long number ) { Page<Order> page = orderService.findByPage(pageNum, pageSize, number, beginTime, endTime); return ResultInfo.success(page);
4.2.xml配置方式
通过@DateTimeFormat注解的方式转换时间格式,每次都要在方法中反复声明注解,xml可以一劳永逸地解决这种问题;
4.2.1.自定义转换类
package com.zhanggen.convert; import org.springframework.core.convert.converter.Converter; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; //自定义类型转换器,要求实现Converter<原始类型,目标类型>接口,并重写convert方法 public class DateConvert implements Converter<String, Date> { @Override public Date convert(String s) { Date date = null; try { date = new SimpleDateFormat("yyyy-MM-dd").parse(s); } catch (ParseException e) { e.printStackTrace(); } return date; } }
4.2.2.将转换类配置到spring
<!--注解驱动--> <mvc:annotation-driven conversion-service="conversionService1"/> <!--自定义时间转换服务--> <bean id="conversionService1" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <bean class="com.zhanggen.convert.DateConvert"></bean> </property> </bean>
5.
<!--上传文件--> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
<!--配置文件上传解析器对象:注意这个id必须是multipartResolver--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--单次上传文件大小限制(10Mb=10*1024*1024=1048576),单位是字节--> <property name="maxUploadSize" value="10048576"/> </bean>
@ResponseBody @RequestMapping(path ="/user/demo10",method = {RequestMethod.GET, RequestMethod.POST} ) public String demo10(MultipartFile picture1) throws IOException { System.out.println("-----------"+picture1); //定义好目标文件 File file = new File("D:/upload/" + picture1.getOriginalFilename()); System.out.println(file); //文件转存 picture1.transferTo(file); return "ok"; }
6.获取
package com.zhanggen.domain; import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; import java.util.List; @NoArgsConstructor @AllArgsConstructor @Data public class User { private String name; private Integer age; private List<String> hobby; }
<!--中文乱码过滤器--> <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> <url-pattern>/*</url-pattern> </filter-mapping>
6.3.接收集合参数
//接收集合参数 @ResponseBody @RequestMapping(path = "/user/demo11") public String demo11(User user){ System.out.println(user); return "ok"; }
7.
-
value:默认属性,用于指定前端传入的参数名称
-
7.1.指定获取前端参数
可以不用把集合参数作为对象的1个属性,指定1个接收参数名称;
//可以不用把集合参数作为对象的1个属性,指定1个接收参数名称 @ResponseBody @RequestMapping(path ="/user/demo11") public String demo12(@RequestParam List<String> hobby) { for (String s : hobby) { System.out.println(s); } return "ok"; }
7.2.参数别名和设置默认值
处理器在接收前端参数的时候,必须和前端参数名称保持一致;
通过RequestParam注解可以给修改前端参数设置参数别名,及设置前端参数的默认值;
// @RequestParam注解使用 @ResponseBody @RequestMapping("/user/demo12") public String demo12( //后端修改前端参数的名称usERName为userName @RequestParam("usERName") String userName, // 设置password参数的默认值为123 @RequestParam(defaultValue = "123") String password) { System.out.println(userName); System.out.println(password); return "ok"; }
7.3.分页
//菜品列表分页查询:/dish/page?page=1&pageSize=10 @GetMapping("/dish/page") public ResultInfo findByPage(@RequestParam(value = "page", defaultValue = "1") Integer pageNum, @RequestParam(defaultValue = "10") Integer pageSize, String name) { //调用dishService层返回page对象 Page<Dish> dishPage = dishService.findByPage(name, pageNum, pageSize); //把dishPage对象封装到ResultInfo中 return ResultInfo.success(dishPage); }
@PostMapping("/dish") public ResultInfo save(@RequestBody Dish dish) { dishService.save(dish); return ResultInfo.success(null); }
9.通过servlet的HttpServletRequest接收参数
//根据ID查询 @GetMapping("/getById") public AjaxResult findById(Long id, HttpServletRequest request) { System.out.println(request.getParameter("id")); Review review = reviewService.findById(id); return AjaxResult.success(review); }
10.模板引擎
借助freemarker和Thymeleaf模板引擎, 对HTML模板进行数据渲染响应给前端;
1.pom依赖
引入spring-boot-starter-freemarker
dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <!--apache 对 java io 的封装工具库--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-io</artifactId> <version>1.3.2</version> </dependency> </dependencies>
2.application.yml
配置freemarker
server: port: 8881 #服务端口 spring: application: name: freemarker-demo #指定服务名 freemarker: cache: false #关闭模板缓存,方便测试 settings: template_update_delay: 0 #检查模板更新延迟时间,设置为0表示立即检查,如果时间大于0会有缓存不方便进行模板测试 suffix: .ftl #指定Freemarker模板文件的后缀名
3.controller
使用@controller注解响应给前端的是逻辑视图文件的文件名称
使用@RestController注解响应的是逻辑视图和数据渲染之后的物理视图;
注意把@RestController改成@Controller
package com.heima.freemarker.demo.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.*; /** * @author zhanggen * @since 2022-07-04 */ //@RestController=@Controller+@ResponseBody功能 @Controller public class HelloController { @GetMapping("/hello") public String hello(Model model) { model.addAttribute("name","张根"); Map<String,Object> stuMap1=new HashMap<>(); stuMap1.put("name","Martin"); stuMap1.put("age",19); model.addAttribute("stu",stuMap1); Map<String,Object> stuMap2=new HashMap<>(); stuMap2.put("name","Tom"); stuMap2.put("age",20); List<Map<String,Object>> studentList=new ArrayList<>(); studentList.add(stuMap1); studentList.add(stuMap2); model.addAttribute("studentList",studentList); // 新增日期字段 model.addAttribute("today", new Date()); return "01-basic"; } }
4.模板
把模板文件放到resources/templates
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Hello World!</title> </head> <body> <b>普通文本 String 展示:</b><br><br> Hello ${name} <br> <hr> <b>对象Student中的数据展示:</b><br/> 姓名:${stu.name}<br/> 年龄:${stu.age} <hr> <#--1.集合遍历--> <#--2.if..else分支判断--> <#list studentList as student> <#if (student.age < 20) || (student.name="Martin")> <p style="color: aquamarine"> ${student.name}<---->${student.age}<br/></p> <#else> <p style="color: aqua">${student.name}<---->${student.age+6}</p> </#if> </#list> <#--3.判空、size方法、集合遍历--> <#if studentList??> <p>列表的总长度:${studentList?size} </p> <#list studentList as student> <#if (student.age < 20) || (student.name="Martin")> <p style="color: aquamarine"> ${student.name}<---->${student.age}<br/></p> <#else> <p style="color: aqua">${student.name}<---->${student.age+6}</p> </#if> </#list> </#if> <#--4.日期函数--> <#--5.内建函数 --> ${today?date} <br/> ${today?time} <br/> ${today?datetime} <br/> ${today?string("yyyy年MM月")} </body> </html>
5.显示效果
三、统一异常处理
在SpringMVC中提供了一个通用的异常处理机制,它提供了一个成熟、简洁并且清晰的异常处理方案;
系统的Dao(mapper)、Service、Controller出现了异常,都可以通过throws Exception向上抛出;
最后SpringMVC的前端控制器把抛出的交给统一异常处理器,进行异常统一处理,返回给前端
最终由springMVC统一转交给自定义的统一异常处理类来处理,实现异常的统一处理;
1.后台异常分类
- 可预测的异常,对于他们,我们直接捕获,给前端提示;(可以预测的异常)
- 自定义的业务异常,对于他们,我们直接throw出去,将业务报错,返回给前端;(throw自定义的异常)
package com.itheima.reggie.handler; import com.itheima.reggie.common.CustomException; import com.itheima.reggie.common.ResultInfo; import org.springframework.dao.DuplicateKeyException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; //全局异常处理器 @ControllerAdvice @ResponseBody public class GlobalExceptionHandler { //1、处理唯一值重复的异常 @ExceptionHandler(DuplicateKeyException.class) public ResultInfo handlerDuplicateKeyException(Exception e) { //1. 打印日志 e.printStackTrace(); if (e.getMessage().contains("idx_category_name")) { return ResultInfo.error("分类名称的值重复"); } //2. 给前端提示 return ResultInfo.error("填写的值重复"); } //处理自定义异常 @ExceptionHandler(CustomException.class) public ResultInfo handlerCustomException(Exception e) { //1. 打印日志 e.printStackTrace(); //2. 给前端提示 return ResultInfo.error(e.getMessage()); } //非预期异常 @ExceptionHandler(Exception.class) public ResultInfo handlerException(Exception e) { //1. 打印日志 e.printStackTrace(); //2. 给前端提示 return ResultInfo.error("服务器开小差了,请联系管理员"); } }
3.使用自定义异常处理器
//单条or批量删除 @Override public void bathDelete(String ids) throws CustomException { String[] idList = ids.split(","); for (String id : idList) { long disgID = Long.parseLong(id); Dish dish = dishMapper.selectById(disgID); //1.只允许删除停售状态的 if (dish.getStatus() != 0) { //使用自定义的异常处理器抛出异常 throw new CustomException("无法删除启售状态的菜品"); } //2.删除菜品的同时需要删除菜品的口味信息 //LambdaQueryWrapper构造查询条件 LambdaQueryWrapper<DishFlavor> dishFlavorQueryWrapper = new LambdaQueryWrapper<>(); dishFlavorQueryWrapper.eq(dish.getId() != null, DishFlavor::getDishId, dish.getId()); dishFlavorMapper.delete(dishFlavorQueryWrapper); dishMapper.deleteById(disgID); } }
四、拦截器
拦截器是SpringMVC提供的一种技术,它的功能似于过滤器过滤器,会在以下时机执行;
- 进入controller之前
- 离开controller之后
- 响应离开服务时
1.引入拦截器依赖
<!--servlet--> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> </dependency>
2.定义拦截器类
package com.zhanggen.interceptor; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("进入处理器之前"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("离开处理器之后"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("请求即将离开服务器"); } }
3.配置拦截器
<!--拦截器配置-->
<!--拦截器链-->
<mvc:interceptors>
<mvc:interceptor>
<!--设置拦截规则-->
<!--拦截全部-->
<mvc:mapping path="/**"/>
<!--排除拦截-->
<mvc:exclude-mapping path="/login"/>
<bean class="com.zhanggen.interceptor.MyInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
五、SpringMVC执行流程
1.SpringMVC的4大组件
前端控制器: 相当于1个大管家,负责分发请求(老大)
处理器映射器: 负责根据URL寻找对应的处理器方法(小弟1)
处理器适配器: 负责真正的去调用某个处理器方法(小弟2)
视图解析器: 负责将逻辑视图(/WEB-INF/success.jsp)转换成物理视图(JSP对象)(小弟3)
2.SpringMVC执行流程
1. 用户通过浏览器发送请求至DispatcherServlet
2. DispatcherServlet收到请求调用HandlerMapping
3. HandlerMapping找到具体的处理器链返回给DispatcherServlet
4. DispatcherServlet会根据返回的处理器链调用HandlerAdapter
5. HandlerAdapter经过适配调用具体的Handler(controller)
6. Controller执行完成返回一个执行结果
7. HandlerAdapter将Handler的结果ModelAndView对象返回给DispatcherServlet
8. DispatcherServlet将ModelAndView对象传给ViewReslover
9. ViewReslover解析后得到具体View,并返回给DispatcherServlet
10. DispatcherServlet根据View进行视图渲染(即将模型数据填充至视图中)
11. DispatcherServlet会将渲染后的视图响应给浏览器
六、Restful风格API
原来 | Restful | |
---|---|---|
保存 | /saveUser | POST /user |
修改 | /updateUser?id=1 | PUT /user/id/1 |
删除 | /deleteUser?id=1 | DELETE /user/id/1 |
查询所有 | /findAllUser | GET /user |
查询一个 | /findByUserId?id=1 |
1.标识请求方法
使用以下注解可以表示当前处理器可以处理的请求方法;
- @GetMapping :此注解标注的方法只接收get请求
- @PostMapping:此注解标注的方法只接收post请求
- @DeleteMapping:此注解标注的方法只接收Delete请求
package com.itheima.web.controller; import com.itheima.domain.User; import org.springframework.web.bind.annotation.*; import java.util.ArrayList; import java.util.List; @RestController public class UserController { //保存 @ResponseBody // @RequestMapping(value = "/user", method = RequestMethod.POST) @PostMapping("/user") //此注解标注的方法只接收post请求 public User save(@RequestBody User user) { System.out.println("请求到达了save方法"); System.out.println(user); user.setAge(180); return user; } //查询所有 @ResponseBody // @RequestMapping(value = "/user", method = RequestMethod.GET) @GetMapping("/user") //此注解标注的方法只接收post请求 public List<User> findAll() { System.out.println("请求到达了findAll方法"); List<User> userList = new ArrayList<User>(); userList.add(new User(1, "张弢", 56)); userList.add(new User(2, "张启樵", 43)); return userList; } }
2.接收动态URL路径变量
REST 风格的API提倡把查询参数,体现在URL路径上;
我们可以使用接收动态的URL路径变量呢?
2.1.接收1个动态路径变量
/user/1
/user/2
// 根据id查询:/user/1或/user/2或者/user/3 @ResponseBody @GetMapping("/user/{myId}") public User findById(@PathVariable("myId") int id) { System.out.println(id); User user = new User(id, "张根", 12); return user; }
2.1.接收1个动态路径变量
/user/name/zhangsan/age/18
/user/name/lisi/age/28
//根据主键+其他条件查询:/user/name/zhangsan/age/18或者/user/name/lisi/age/28 @ResponseBody @GetMapping("/user/name/{name}/age/{age}") public User findById( @PathVariable("name") String name, @PathVariable("age") Integer age ) { System.out.println(name); System.out.println(age); User user = new User(1, name, age); return user; }
2.3.示例
//回显菜品信息 /dish/198 @GetMapping("/dish/{id}") public ResultInfo findById(@PathVariable("id") Long id) { Dish dish = dishService.findById(id); return ResultInfo.success(dish); }
@RequestMapping(value = "/user", method = RequestMethod.POST) :此注解标注的方法和URL的关系
@GetMapping :此注解标注的方法只接收get请求
@PostMapping:此注解标注的方法只接收post请求
@DeleteMapping:此注解标注的方法只接收delete请求
@PutMapping:此注解标注的方法只接收put请求
@RequestBody :此注解标注的方法可以接收请求体包含参数信息的Ajax请求
@ResponseBody:此注解标注的方法或者类可以响应JSON格式信息;
@Controller :如果要返回ModelAndView,则需要单独使用@Controller配合视图解析器InternalResourceViewResolver进行视图和模型的数据渲染;(模板渲染项目)
@RestController 该注解标注在类上,相当于@Controller+@ResponseBody功能,返回JSON数据;(前后端分离项目)
@PathVariable: 接收动态URL路径中包含的变量信息