基本概念 mvc(model,view,controller),其中m是封装数据传递的实体类,v是前端页面,c相当于servlet的基本功能,处理请求,返回响应。在springmvc之前ssm中的第二个s是指的structs,structs的页面配置比较的繁琐,对原生的servlet依赖较强,另外还有严重的安全漏洞,所以现在的s指的是springmvc
基础案例 操作步骤 1.首先导入相关的依赖,springmvc的和spring核心的,另外还需要还需要引入servlet的相关依赖 2.创建控制类,控制类用@Controller来进行声明,其中@RequestMapping注解用来声明请求路径,@ResponseBody用来使路径不跳转而是直接写入到HTTP response body中去。同时@ResponseBody可以直接将pojo的json格式返回给页面(这个过程是jackson来实现的) 3.初始化springmvc的环境,需要设置一个配置类,使用方法和之前的spring ioc相同 4.初始化servlet容器,加载mvc的环境,并设置mvc的处理请求。需要创建类继承自其AbstractDispatcherServletInitializer,其中的createServletApplicationContext方法用来指定context类型。getServletMappings方法用来设置需要处理那些请求
实现代码 pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd" > <modelVersion > 4.0.0</modelVersion > <groupId > com.eldpepar</groupId > <artifactId > springmvc</artifactId > <version > 1.0-SNAPSHOT</version > <name > springmvc</name > <packaging > war</packaging > <properties > <project.build.sourceEncoding > UTF-8</project.build.sourceEncoding > <maven.compiler.target > 1.8</maven.compiler.target > <maven.compiler.source > 1.8</maven.compiler.source > <spring.version > 5.2.10.RELEASE</spring.version > </properties > <dependencies > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-webmvc</artifactId > <version > ${spring.version}</version > </dependency > <dependency > <groupId > javax.servlet</groupId > <artifactId > javax.servlet-api</artifactId > <version > 4.0.1</version > <scope > provided</scope > </dependency > <dependency > <groupId > org.springframework</groupId > <artifactId > spring-context</artifactId > <version > ${spring.version}</version > </dependency > </dependencies > <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-war-plugin</artifactId > <version > 3.3.2</version > </plugin > <plugin > <groupId > org.apache.tomcat.maven</groupId > <artifactId > tomcat7-maven-plugin</artifactId > <version > 2.2</version > <configuration > <uriEncoding > UTF-8</uriEncoding > <port > 8099</port > <path > /</path > </configuration > </plugin > </plugins > </build > </project >
UserControl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.eldpepar.base.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.ResponseBody;@Controller public class UserControl { @RequestMapping("/hello") @ResponseBody public String hello () { return "hello" ; } }
MvcConfig.java
1 2 3 4 5 6 7 8 9 10 package com.eldpepar.base;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration @ComponentScan("com.eldpepar.base") public class MvcConfig { }
ServletInit.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 package com.eldpepar.base;import org.springframework.web.context.WebApplicationContext;import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;public class ServletInit extends AbstractDispatcherServletInitializer { @Override protected WebApplicationContext createServletApplicationContext () { AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); ctx.register(MvcConfig.class); return ctx; } @Override protected String[] getServletMappings() { return new String[]{"/" }; } @Override protected WebApplicationContext createRootApplicationContext () { return null ; } }
工作流程 | 启动服务初始化过程
1.服务器启动,执行ServletInit类,初始化web容器 2.执行createServletApplicationContext方法,创建了WebApplicationContext对象 3.加载MvcConfig 4.执行@ComponScan加载对应的bean 5.加载UserController,没个@RequestMapping的名称对应一个具体方法。如果需要整个类访问相同的路径,可以在类中加入@RequestMapping 6.执行getServletMappings方法,定义所有请求通过springmvc
| 单次请求过程 1.发送相应的请求 2.web容器发现所有请求都要经过springmvc,将请求控制交给springmvc处理 3.解析请求路径 4.由请求路径匹配到相应的方法 5.执行请求的方法 6.检测到由@ResponseBody直接将方法的返回值作为响应体返回给请求方
bean加载控制 在spring加载bean的时候,之前是通过扫描整个包的方式进行加载。但在springmvc中,mvc相关的bean需要mvc进行加载,这个时候就需要进行排除。通常情况下由以下方式来进行排除
1.spring加载bean的时候仍然设定扫描整个包,人工的排除controller包内的bean。需要特别注意的是如果在同一个包的情况下,controller中的扫描和spring的扫描只能存在一个,否则过滤失效
SpringConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package com.eldpepar.base;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;import org.springframework.stereotype.Controller;@Configuration @ComponentScan(value = "com.eldpepar.base", excludeFilters = @ComponentScan.Filter( type = FilterType.ANNOTATION, classes = Controller.class )) public class SpringConfig { }
2.spring加载的bean设置扫描范围为精确范围
SpringConfig.java
1 2 3 4 5 6 7 8 9 10 package com.eldpepar.base;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;@Configuration @ComponentScan({"com.eldpepar.base.dao","com.eldpepar.base.domain"}) public class SpringConfig { }
加载优化 可以通过继承AbstractDispatcherServletInitializer下的子类来优化ServletInit初始化的代码。子类只需要指定相应的类即可ServletInit.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package com.eldpepar.base;import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;public class ServletInit extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{MvcConfig.class}; } @Override protected String[] getServletMappings() { return new String[]{"/" }; } }
请求与响应 如果需要请求携带参数,需要在请求方法中设置形参,后台就可以获取到输入的参数了。需要特别注意的是post请求中,psotman工具使用的是Body下的x-www来进行提交,form-data不仅可以提交表单,还可以提交文件。
post中文乱码处理 需要添加一个过滤方法,在过滤方法中指定编码方式ServletInit.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package com.eldpepar.base;import org.springframework.web.filter.CharacterEncodingFilter;import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;import javax.servlet.Filter;public class ServletInit extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class<?>[] getRootConfigClasses() { return new Class[]{SpringConfig.class}; } @Override protected Class<?>[] getServletConfigClasses() { return new Class[]{MvcConfig.class}; } @Override protected String[] getServletMappings() { return new String[]{"/" }; } @Override protected Filter[] getServletFilters() { CharacterEncodingFilter filter = new CharacterEncodingFilter(); filter.setEncoding("UTF-8" ); return new Filter[]{filter}; } }
参数传递 请求的参数一般有普通参数、pojo类型参数、嵌套pojo类型参数、数组类型参数、集合类型参数
普通参数 请求参数名如果与形参不同,使用@RequestParam绑定参数关系
1 2 3 4 5 6 7 8 @RequestMapping("/getUser") @ResponseBody public String getUser (@RequestParam("name") String userName, int age) { System.out.println("userName->" + userName); System.out.println("age->" + age); return "success" ; }
pojo参数 只需要参数名与类的属性名相同,定义pojo类型形参就可以接受参数
1 2 3 4 5 6 7 @RequestMapping("/getUserPojo") @ResponseBody public String getUserPojo (User user) { System.out.println("user->" + user); return "success" ; }
pojo嵌套 如果pojo中带有其他的pojo参数,传递参数的时候,需要用“对象名.参数”的方法来传递。例如address.city
1 2 3 4 5 6 7 @RequestMapping("/getUserPojoNest") @ResponseBody public String getUserPojoNest (User user) { System.out.println("user嵌套->" + user); return "success" ; }
数组参数 请求是,需要将参数的名称设置为数组的名称,并且定义多个参数名相同的参数,这样就可以接收到数组的形参
1 2 3 4 5 6 7 @RequestMapping("/getArr") @ResponseBody public String getArr (String[] likes) { System.out.println("数组参数->" + Arrays.toString(likes)); return "success" ; }
集合参数 如果需要传递集合参数,传递的方法和数组的方法是相同的。但需要在后台加上@RequestParam
1 2 3 4 5 6 7 @RequestMapping("/getList") @ResponseBody public String getList (@RequestParam List<String> likes) { System.out.println("集合参数传递->" +likes); return "success" ; }
json参数 传递json参数首先需要加入jackson-databind依赖,然后开启@EnableWebMvc。postman请求的时候需要设置Body->raw,并且选择json。在相应方法参数里需要开启@RequestBody。详细的参数传递规则可以查询json格式规范
相关依赖
1 2 3 4 5 6 <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-databind</artifactId > <version > 2.9.0</version > </dependency >
MvcConfig.java
1 2 3 4 5 6 7 8 9 10 11 12 package com.eldpepar.base;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.web.servlet.config.annotation.EnableWebMvc;@Configuration @ComponentScan("com.eldpepar.base") @EnableWebMvc public class MvcConfig { }
MvcConfig.java
1 2 3 4 5 6 7 @RequestMapping("/list") @ResponseBody public String list (@RequestBody String[] city) { System.out.println("city->" + Arrays.toString(city)); return "success" ; }
时间日期 传递指定格式日期时间的时候,同样需要在相应方法的参数中设置日期转化。需要使用@DataTimeFormat实现。转化的本质是Convert接口帮我们实现的,要想使用这个接口需要开启@EnableWebMvc。
1 2 3 4 5 6 7 @RequestMapping("/getData") @ResponseBody public String getData (@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss") Date date) { SimpleDateFormat format = new SimpleDateFormat("今天是 " + "yyyy 年 MM 月 dd 日 HH 点 mm 分 ss 秒" ); System.out.println(format.format(date)); return "success" ; }
REST风格 REST风格通过请求的类型来区分不同的请求,特点是请求的路径较短,比较直观。如果需要接受变量,则需要在方法参数中使用@PathVariable。此外springmvc还提供了了get、post、delete、put等方法的专属Mapping。为了简化ResponseBody和Controller提供了RestController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @RequestMapping(value = "/users", method = RequestMethod.GET) @ResponseBody public String save (@RequestBody User user) { System.out.println("user save successful" ); return "user save successful" ; }@RequestMapping(value = "/users", method = RequestMethod.PUT) @ResponseBody public String update (@RequestBody User user) { System.out.println("user update successful" ); return "user update successful" ; }@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE) @ResponseBody public String delete (@PathVariable Integer id) { System.out.println("user delete successful" ); return "user delete successful" ; }
优化代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 @RestController @RequestMapping("/books") public class BookController { @PostMapping public String save (@RequestBody Book book) { System.out.println("Book save successful" ); return "Book save successful" ; } @DeleteMapping("/{id}") public String delete (@PathVariable Integer id) { System.out.println("Book delete successful" ); return "Book delete successful" ; } @PutMapping public String update (@RequestBody Book book) { System.out.println("Book update successful" ); return "Book update successful" ; } @GetMapping("/{id}") public String getById (@PathVariable Integer id) { System.out.println("Book getById successful" ); return "Book getById successful" ; } @GetMapping public String getAll () { System.out.println("Book getAll successful" ); return "Book getAll successful" ; } }