Spring之springMVC
前言
1个最简化版的web框架应具备以下3大功能:
- 接收客户端http请求-
- 获取http请求的参数
- 响应客户端字符串
SpringMVC是Spring对Web框架的1个解决方案
- SpringMVC提供了一个总的前端控制器Servlet,用于接收Tomcat的http请求;
- 定义了一套路由策略(URL--->Handler的映射)和处理器(handler);
- 将handler处理的结果使用视图解析技术响应给前端;
SpringMVC其实是三层架构中表示层的解决方案,springMVC把之前自己写的servlet也就是表示层,拆分成了2大部分:
- 前端分发器(Dispatcher):本质是1个由springMVC提供的servlet,对前端请求进行统一分发;
- 处理器(Controller):我们自己写的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>springartifactId> <groupId>com.zhanggengroupId> <version>1.0-SNAPSHOTversion> parent> <modelVersion>4.0.0modelVersion> <artifactId>day05-mvcartifactId> <packaging>warpackaging> <name>day05-mvc Maven Webappname> <url>http://www.example.comurl> <properties> <project.build.sourceEncoding>UTF-8project.build.sourceEncoding> <maven.compiler.source>1.7maven.compiler.source> <maven.compiler.target>1.7maven.compiler.target> properties> <dependencies> <dependency> <groupId>org.springframeworkgroupId> <artifactId>spring-webmvcartifactId> <version>5.1.6.RELEASEversion> dependency> <dependency> <groupId>com.fasterxml.jackson.coregroupId> <artifactId>jackson-databindartifactId> <version>2.9.10version> dependency> <dependency> <groupId>org.projectlombokgroupId> <artifactId>lombokartifactId> <version>1.18.20version> dependency> dependencies> <build> <finalName>day05-mvcfinalName> <pluginManagement> <plugins> <plugin> <artifactId>maven-clean-pluginartifactId> <version>3.1.0version> plugin> <plugin> <artifactId>maven-resources-pluginartifactId> <version>3.0.2version> plugin> <plugin> <artifactId>maven-compiler-pluginartifactId> <version>3.8.0version> plugin> <plugin> <artifactId>maven-surefire-pluginartifactId> <version>2.22.1version> plugin> <plugin> <artifactId>maven-war-pluginartifactId> <version>3.2.2version> plugin> <plugin> <artifactId>maven-install-pluginartifactId> <version>2.5.2version> plugin> <plugin> <artifactId>maven-deploy-pluginartifactId> <version>2.8.2version> plugin> plugins> pluginManagement> build> project>pom.xml
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"> <servlet> <servlet-name>dispatcherServletservlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class> <init-param> <param-name>contextConfigLocationparam-name> <param-value>classpath:spring-mvc.xmlparam-value> init-param> servlet> <servlet-mapping> <servlet-name>dispatcherServletservlet-name> <url-pattern>/url-pattern> servlet-mapping> web-app>web.xml
------------------------------------------------------------------------------------
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"> <context:component-scan base-package="com.zhanggen.controller"/> <mvc:annotation-driven/> beans>spring-mvc.xml
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; } }UserController.java
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/"/>
spring-mvc.xml
-
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; } }UserController.java
二、接收和响应请求
在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"; } }index.html
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.
//接收时间类型的参数 @ResponseBody @RequestMapping(path = "/user/demo8") public String demo8(@DateTimeFormat(pattern = "yyyy-MM-dd") Date myDate) { System.out.println(myDate); return "ok"; }接收时间类型的参数
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 ConverterDateConvert.java{ @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>
spring-mvc.xml
5.
<dependency>
<groupId>commons-fileuploadgroupId>
<artifactId>commons-fileuploadartifactId>
<version>1.4version>
dependency>
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10048576"/>
bean>
spring-mvc.xml
@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 ListUser.javahobby; }
<filter> <filter-name>characterEncodingFilterfilter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilterfilter-class> <init-param> <param-name>encodingparam-name> <param-value>utf-8param-value> init-param> filter> <filter-mapping> <filter-name>characterEncodingFilterfilter-name> <url-pattern>/*url-pattern> filter-mapping>web.xml
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指定前端参数名称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"; }别名和默认值
//接收请求体中包含参数信息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"; }包含参数信息
三、统一异常处理
使用Spring mvc后,由前端处理器(dispatcher)-----》controler层-----》service层-----》dao层;
这些设计的好处之一是,当3层中的代码出现了任何异常, 去都可以向上抛出;
最终由springMVC统一转交给自定义的统一异常处理类来处理,实现异常的统一处理;
package com.zhanggen.handler; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice //该注解可以声明当前类是一个用于统一处理3层异常的类 public class CommonExceptionHandler { @ResponseBody @ExceptionHandler(Exception.class) //标注该方法可以处理哪些类型的异常 public String handleException() { return "后台出错了"; } @ResponseBody @ExceptionHandler(NullPointerException.class) public String handleNullPointerException() { return "空指针异常"; } @ResponseBody @ExceptionHandler(ArithmeticException.class) public String handleArithmeticException() { return "数学异常"; } }CommonExceptionHandler.java
四、拦截器
拦截器是SpringMVC提供的一种技术,它的功能似于过滤器过滤器,会在以下时机执行;
- 进入controller之前
- 离开controller之后
- 响应离开服务时
1.引入拦截器依赖
<dependency>
<groupId>javax.servletgroupId>
<artifactId>servlet-apiartifactId>
<version>2.5version>
dependency>
pom.xml
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("请求即将离开服务器"); } }MyInterceptor.java
3.配置拦截器
class="com.zhanggen.interceptor.MyInterceptor">
spring-mvc.xml
五、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 ListUserController.javafindAll() { System.out.println("请求到达了findAll方法"); List userList = new ArrayList (); 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; }根据id查询
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; }根据多个条件查询
@Controller :该注解可以将当前类创建的对象放到spring的IOC容器,以便前端控制器调用;
@RequestMapping(value = "/user", method = RequestMethod.POST) :此注解标注的方法和URL的关系
@GetMapping :此注解标注的方法只接收get请求
@PostMapping:此注解标注的方法只接收post请求
@DeleteMapping:此注解标注的方法只接收delete请求
@PutMapping:此注解标注的方法只接收put请求
@RequestBody :此注解标注的方法可以接收请求体包含参数信息的Ajax请求
@ResponseBody:此注解标注的方法或者类可以响应json格式信息
@RestController 该注解标注在类上,相当于@Controller+@ResponseBody功能
@PathVariable: 接收动态URL路径中包含的变量信息