目录

1、SpringMVC简介

1.1、SpringMVC概述

1.2、SpringMVC快速入门

1.2.1、开发步骤

1.2.2、流程分析

1.3、SpringMVC的组件解析

1.4、Controller加载控制与业务bean加载控制

2、请求

2.1、请求映射路径

2.2、获得请求头

2.2.1、@RequestHeader

2.2.2、@CookieValue

2.3、请求方式

2.3.1、Get / Post 请求传参 & Post请求中文乱码处理

2.4、请求参数

2.4.1、获得普通类型参数

2.4.2、获得POJO类型参数(简单JavaBean)

2.4.3、获得嵌套POJO类型参数

2.4.4、获得数组类型参数

2.4.5、获得集合类型参数

2.5、请求参数——传递json数据

2.5.1、集合参数

2.5.2、POJO参数

2.5.3、集合+POJO参数

2.6、请求参数——日期类型参数传递

2.6.1、类型转换器

2.7、@RequestBody、@RequestParam、@PathVariable的区别

3、响应

3.1、SpringMVC的数据响应方式

3.1.1、响应页面

3.1.2、响应文本数据(字符串)

3.1.3、响应json数据(对象集合转json数组)

3.2、类型转换器HttpMessageConverter

4、配置对静态资源的访问放行

5、自定义类型转换器

6、获得Servlet相关API

7、REST风格

7.1、简介

7.2、入门案例

7.3、RESTful快速开发

8、SSM整合

8.1、SSM整合流程

8.1.1、导入相关坐标

8.1.2、SSM整合

8.1.3、功能模块

8.1.4、测试

8.2、表现层数据封装

9、异常处理

9.1、异常处理的思路

9.2、异常处理的两种方式

9.2.1、简单异常处理器SimpleMappingExceptionResolver

9.2.2、自定义异常处理器

9.3、Spring中的异常处理器

9.4、项目异常处理方案

10、拦截器

10.1、拦截器的概念

10.2、拦截器和过滤器的区别

10.3、拦截器入门案例

10.4、拦截器HandlerInterceptor接口方法说明

10.5、拦截器执行流程

10.6、拦截器参数

10.7、多拦截器执行顺序

10.7.1、拦截器链配置方式

10.7.2、拦截器链的运行顺序

11、文件上传

11.1、文件上传三要素

11.2、文件上传原理

11.3、单文件上传步骤

11.4、多文件上传实现


1、SpringMVC简介

1.1、SpringMVC概述

SpringMVC与Servlet技术功能等同,是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架,属于SpringFrameWork的后续产品,已经融合在Spring Web Flow中。

SpringMVC已经成为目前最主流的MVC框架之一,并且随着Spring 3.0 的发布,全面超越Struts2,成为最优秀的MVC框架。它通过一套注解,让一个简单的Java类称为处理请求的控制器,而无需实现任何接口。同时,他还支持RESTful编程风格的请求。

SpringMVC的优点:相比Servlet使用更简单,开发更便捷,灵活性强。

1.2、SpringMVC快速入门

需求:客户端发起请求,服务器端接收请求,执行逻辑并进行视图跳转。

1.2.1、开发步骤

  1. 使用SpringMVC技术需要先导入SpringMVC坐标与Servlet坐标;

    pom.xml:...<dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.10.RELEASE</version></dependency>...<!-- 添加Tomcat插件 --><build><finalName>SSM</finalName><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port>80</port><path>/</path></configuration></plugin></plugins></build>
  2. 创建控制器Controller类和视图页面,使用注解配置Controller类中业务方法的映射地址。
    @Controller //将控制器加入到容器中
    @RequestMapping("/user")
    public class UserController {// 请求地址:  http://localhost:80/user/login ,访问时会执行该方法@RequestMapping(value="/login",method = RequestMethod.GET,params = {"username"})public String save(){System.out.println("Controller save running....");return "success.jsp"; //跳转到success.jsp}// 设置当前操作的访问路径@RequestMapping("/save")// 设置当前操作的返回值类型@ResponseBodypublic String save() {System.out.println("user save ...");return "{'module':'springmvc'}";}
    }
  3. 初始化SpringMVC环境(同Spring环境),设定SpringMVC加载对应的bean。
    /*** 创建SpringMVC的配置文件,加载controller对应的bean*/
    @Configuration
    @ComponentScan("com.clp.controller")
    public class SpringMvcConfig {
    }
  4. 初始化Servlet容器,加载SpringMVC环境,并设置SpringMVC技术处理的请求。
    AbstractDispatcherServletInitializer类是SpringMVC提供的快速初始化Web3.0容器的抽象类。
    AbstractDispatcherServletInitializer提供3个接口方法供用户实现:createRootApplicationContext()方法:如果创建Servlet容器时需要加载非SpringMVC对应的bean,使用当前方法进行。使用方式同createServletApplicationContext()/*** 定义一个servlet容器启动的配置类,在里面加载spring的配置*/
    public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer {// 加载SpringMVC容器配置@Overrideprotected WebApplicationContext createServletApplicationContext() {AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();ctx.register(SpringMvcConfig.class);return ctx;}// 设置哪些请求归属SpringMVC处理@Overrideprotected String[] getServletMappings() {return new String[]{"/"}; // 所有请求归SpringMVC进行处理}// 加载Spring容器的配置@Overrideprotected WebApplicationContext createRootApplicationContext() {AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();ctx.register(SpringConfig.class);return ctx;}
    }上述的简化配置:
    class ServletContainersInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer {@Overrideprotected Class<?>[] getRootConfigClasses() {return new Class[]{SpringConfig.class};}@Overrideprotected Class<?>[] getServletConfigClasses() {return new Class[]{SpringMvcConfig.class};}@Overrideprotected String[] getServletMappings() {return new String[]{"/"};}
    }/*
    注:在web.xml中配置的方式:<!-- springMvc的前端控制器 --><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>
    */

1.2.2、流程分析

启动服务器初始化过程:

  1. 服务器启动,执行ServletContainerInitConfig类,初始化web容器;
  2. 执行createServletApplicationContext()方法,创建了WebApplicationContext对象;
  3. 加载SpringMvcConfig;
  4. 执行@ComponentScan加载对应的bean;
  5. 加载UserController,每个@RequestMapping的名称对应一个具体的方法;
  6. 执行getServletMappings方法,定义所有的请求都通过SpringMvc。

单次请求过程:

  1. 发送请求localhost/save;
  2. web容器发现所有请求都通过SpringMVC,将请求交给SpringMVC处理;
  3. 解析请求路径/save;
  4. 由/save匹配执行对应的方法save();
  5. 执行save();
  6. 检测到有@ResponseBody直接将save()方法的返回值作为响应体返回给请求方。

1.3、SpringMVC的组件解析

  1. 用户发送请求至前端控制器DispatcherServlet;
  2. DispatcherServlet搜到请求调用HandlerMapping处理器映射器;
  3. 处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DIspatcherServlet;
  4. DispatcherServlet调用HandlerAdapter处理器适配器;
  5. HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器);
  6. Controller执行完成返回ModelAndView;
  7. HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet;
  8. DispatcherServlet将ModelAndView传给ViewResolver视图解析器;
  9. ViewResolver解析后返回具体View;
  10. DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。

1.4、Controller加载控制与业务bean加载控制

  • SpringMVC相关bean(表现层bean);
  • Spring控制的bean:① 业务bean(Service);② 功能bean(DataSource等)。

因为功能不同,如何避免Spring错误地加载到SpringMVC的bean?——加载Spring控制的bean的时候排除掉SpringMVC控制的bean。

  • SpringMVC相关bean加载控制:SpringMVC加载的bean对应的包均在com.itheima.controller包内;
  • Spring相关bean加载控制:① 方式1:Spring加载的bean设定扫描范围为com.itheima,排除掉controller包内的bean;② 方式2:Spring加载的bean设定扫描范围为精准范围,例如service包、dao包等;③ 方式3:不区分Spring与SpringMVC的环境,加载到同一个环境中。
    - @ComponentScan注解属性:excludeFilters:排除扫描路径中加载的bean,需要指定类别(type)与具体项(classes);includeFilters:加载指定的bean,需要指定类别(type)与具体项(classes)。@Configuration
    @ComponentScan(value="com.clp",excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION, // 按注解过滤classes = Controller.class // 排除使用@Controller注解的类)
    )
    @EnableTransactionManagement
    public class SpringConfig {
    }

2、请求

2.1、请求映射路径

@RequestMapping作用:用于建立请求URL和处理请求方法之间的对应关系;作用位置:类上:请求URL的第一级访问目录,此处不写的话,就相当于应用的根目录。方法上:请求URL的第二级访问目录,与类上的使用@RequestMapping标注的一极目录一起组成访问虚拟路径。属性:value:用于指定请求的URL,它和path属性的作用是一样的。method:用于指定请求的方式。params:用于指定限制请求参数的条件,它支持简单的表达式。要求请求参数的key和value必须和配置一模一样。/*** 定义Controller*/
@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/save")@ResponseBodypublic String save() {System.out.println("user save ...");return "{'module':'user save'}";}@RequestMapping("/delete")@ResponseBodypublic String dalete() {System.out.println("user delete ...");return "{'module':'user delete'}";}
}浏览器访问:http://127.0.0.1/user/save,后端会执行save()方法

2.2、获得请求头

2.2.1、@RequestHeader

使用@RequestHeader可以获得请求头信息,相当于web阶段学习的request.getHeader(name)。@RequestHeader注解的属性如下:

  • value:请求头的名称。
  • required:是否必须携带此请求头。

    @RequestMapping(value="/quick20")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public void save20(@RequestHeader(value="User-Agent",required = false) String user_agent) throws IOException {System.out.println(user_agent);}

2.2.2、@CookieValue

使用@CookieValue可以获得指定Cookie的值。@CookieValue注解的属性如下:

  • value:指定cookie的名称。
  • required:是否必须携带此cookie。
    @RequestMapping(value="/quick21")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public void save21(@CookieValue(value="JSESSIONID") String jsessionid) throws IOException {System.out.println(jsessionid);}

2.3、请求方式

2.3.1、Get / Post 请求传参 & Post请求中文乱码处理

@Configuration
@ComponentScan("com.clp.controller")
public class SpringMvcConfig {
}/*********************************************************************************/
/*** 定义一个servlet容器启动的配置类,在里面加载spring的配置*/
public class ServletContainerInitConfig extends AbstractDispatcherServletInitializer {// 加载SpringMVC容器配置@Overrideprotected WebApplicationContext createServletApplicationContext() {AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();ctx.register(SpringMvcConfig.class);return ctx;}// 设置哪些请求归属SpringMVC处理@Overrideprotected String[] getServletMappings() {return new String[]{"/"}; // 所有请求归SpringMVC进行处理}// 加载Spring容器的配置@Overrideprotected WebApplicationContext createRootApplicationContext() {AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();ctx.register(SpringConfig.class);return ctx;}@Overrideprotected Filter[] getServletFilters() {// Post请求中文乱码处理:配置字符过滤器,为web容器添加过滤器并指定字符集,spring-web包中提供了专用的字符过滤器CharacterEncodingFilter filter = new CharacterEncodingFilter();filter.setEncoding("UTF-8");return new Filter[]{filter};}
}/*********************************************************************************/
/*** 定义Controller*/
@Controller
@RequestMapping("/user")
public class UserController {@RequestMapping("/save")@ResponseBodypublic String save() {System.out.println("user save ...");return "{'module':'user save'}";}@RequestMapping("/delete")@ResponseBodypublic String dalete() {System.out.println("user delete ...");return "{'module':'user delete'}";}/*** Get请求:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数。*  例如:http://127.0.0.1/user/commonParam?name=zhangsan&age=18* @param name* @param age* @return*/@RequestMapping("/commonParam")@ResponseBodypublic String commonParam(String name, int age) {System.out.println("普通参数传递 name:" + name + ", age: " + age);return "{'module':'common param'}";}
}

注:通过配置方式获得过滤器:

​web.xml:...<!--配置全局过滤的filter--><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>...

2.4、请求参数

服务器端要获得请求的参数,有时还需要进行数据的封装,SpringMVC可以接收如下类型的参数:

参数种类:

  • 普通参数;
  • POJO类型参数;
  • 嵌套POJO类型参数;
  • 数组类型参数;
  • 集合类型参数。

2.4.1、获得普通类型参数

普通参数:url地址传参,地址参数名与形参变量名相同,定义形参即可接收参数。

Controller的业务方法的参数名称要与请求参数的name一致,参数值会自动映射匹配。当请求的参数名称与Controller的业务方法参数名称不一致时,就需要通过@RequestParam注解显式地绑定。注解@RequestParam有如下参数可以使用:

  • value:请求参数名称。
  • required:此在指定的请求参数是否必须包括,默认是true,提交时如果没有此参数则报错。
  • dafaultValue:当没有指定请求参数时,则使用指定的默认值赋值。
    /** 普通参数:请求参数名与形参名不同*  例如:http://127.0.0.1/user/commonParamDifferentName?name=zhangsan&age=18*/@RequestMapping("/commonParamDifferentName")@ResponseBody// @RequestParam的作用是绑定Get请求参数与形参的对应关系public String commonParamDifferentName(@RequestParam("name") String userName, int age) {System.out.println("【普通参数传递】 userName:" + userName + ", age:" + age);return "'module':'common param different name'";}

2.4.2、获得POJO类型参数(简单JavaBean)

Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配。

public class User {private String name;private int age;// getter()/setter()...
}/** POJO参数*   例如:http://127.0.0.1/user/commonParamDifferentName?name=zhangsan&age=18* */@RequestMapping("/pojoParam")@ResponseBodypublic String pojoParam(User user) {System.out.println("【pojo参数传递】 user:" + user);return "{'module':'pojo param'}";}

2.4.3、获得嵌套POJO类型参数

public class Address {private String city;private String province;// getter & setter
}public class User {private String name;private int age;private Address address;// getter & setter
}/** 嵌套POJO参数*   例如:http://127.0.0.1/user/commonParamDifferentName?name=zhangsan&age=18&address.city=beijing&address.province=beijing* */@RequestMapping("/pojoParam")@ResponseBodypublic String pojoParam(User user) {System.out.println("【pojo参数传递】 user:" + user);return "{'module':'pojo param'}";}

2.4.4、获得数组类型参数

Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配。

    /*** 数组参数*  例如:http://127.0.0.1/user/arrayParam?likes=game&likes=music&likes=travel*/@RequestMapping("/arrayParam")@ResponseBodypublic String arrayParam(String[] likes) {System.out.println("【数组参数传递】 likes:" + Arrays.toString(likes));return "{'module':'array param'}";}

2.4.5、获得集合类型参数

获得集合参数时,一定要在参数前添加@RequestParam注解。

    /*** 集合参数*  例如:http://127.0.0.1/user/listParam?likes=game&likes=music&likes=travel*/@RequestMapping("listParam")@ResponseBodypublic String listParam(@RequestParam List<String> likes) {System.out.println("【集合参数传递】 likes:" + likes);return "{'module':'list param'}";}

2.5、请求参数——传递json数据

解析json数据需要先导入相关坐标:

        <dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.0</version></dependency>

再在SpringMVC核心配置类中进行如下配置,开启自动转换json数据的支持(@EnableWebMVC注解功能强大,该注解整合了多个功能,此处仅使用其中一部分功能,即json数据进行自动类型转换):

@Configuration
@ComponentScan("com.clp.controller")
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig {
}

2.5.1、集合参数

    /*** 集合参数:json格式*  例如:http://127.0.0.1/user/listParamForJson,请求体:["game", "music", "travel"]*/@RequestMapping("/listParamForJson")@ResponseBody// @RequestBody的作用是将请求体的内容映射为对应的对象public String listParamForJson(@RequestBody List<String> likes) {System.out.println("【list param - json 参数传递】 likes:" + likes);return "{'module':'list param - json'}";}

2.5.2、POJO参数

    /*** 集合参数:json格式*  例如:http://127.0.0.1/user/pojoParamForJson,请求体:{"name":"zhangsan","age":"18","address":{"province":"beijing","city":"beijing"}}*/@RequestMapping("/pojoParamForJson")@ResponseBody// @RequestBody的作用是将请求体的内容映射为对应的对象public String pojoParamForJson(@RequestBody User user) {System.out.println("【pojo param - json 参数传递】 likes:" + user);return "{'module':'pojo param - json'}";}

2.5.3、集合+POJO参数

    /*** 集合参数:json格式*  例如:http://127.0.0.1/user/listPojoParamForJson,请求体:[{"name":"zhangsan","age":"18"},{"name":"lisi","age":"20"}]*/@RequestMapping("listPojoParamForJson")@ResponseBody// @RequestBody的作用是将请求体的内容映射为对应的对象public String listPojoParamForJson(@RequestBody List<User> userList) {System.out.println("【list pojo param - json 参数传递】 likes:" + userList);return "{'module':'list pojo param - json'}";}

2.6、请求参数——日期类型参数传递

日期类型数据基于系统不同格式也不尽相同。

    /*** 日期参数*  例如:http://127.0.0.1/user/dateParam?date1=2088/08/08&date2=2088-08-18&date3=2088/08/28 8:08:08*/@RequestMapping("/dateParam")@ResponseBody// 接收形参时,根据不同的日期格式设置不同的接收方式// @DateTimeFormat用于指定日期时间型数据格式。属性pattern:日期时间格式字符串public String dateParam(Date date1,@DateTimeFormat(pattern = "yyyy-MM-dd") Date date2,@DateTimeFormat(pattern = "yyyy/MM/dd HH:mm:ss") Date date3) {System.out.println("【参数传递】 date1:" + date1);System.out.println("【参数传递】 date2:" + date2);System.out.println("【参数传递】 date3:" + date3);return "{'module':'date param'}";}

2.6.1、类型转换器

Converter接口:

public interface Converter<S, T> {@NullableT convert(S var1);
}- 请求参数年龄数据(String -> Integer);
- 日期格式转换(String -> Date)。

2.7、@RequestBody、@RequestParam、@PathVariable的区别

区别:

  • @RequestParam用于接收url地址传参、表单传参【application/x-www-form-urlencoded】;
  • @RequestBody用于接收json数据【application/json】;
  • @PathVariable用于接收路径参数,使用{参数名称}描述路径参数。

应用:

  • 后期开发中,发送请求参数超过1个时,以发送json格式数据为主,@RequestBody应用较广;
  • 如果发送非json格式数据,选用@RequestParam接收请求参数;
  • 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值。

3、响应

3.1、SpringMVC的数据响应方式

1、页面跳转直接返回字符串通过ModelAndView对象返回2、回写数据直接返回字符串返回对象或集合

3.1.1、响应页面

1、返回字符串形式

直接返回字符串“此种方式会将返回的字符串与视图解析器的前后缀拼接后跳转。

    /*** 响应页面 / 跳转页面*/@RequestMapping("/toJumpPage")public String toJumpPage() {System.out.println("跳转页面");return "page.jsp";}

2、返回ModelAndView对象

package com.itheima.controller;import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;@Controller //将控制器加入到容器中
@RequestMapping("/user")
public class UserController {// 请求地址:  http://localhost:80/user/login ,访问时会执行该方法,要求使用GET方式请求@RequestMapping(value="/login",method = RequestMethod.GET,params = {"username"})public String save(){System.out.println("Controller save running....");return "/success"; //跳转到success.jsp( / 代表在当前web应用下寻找资源)}@RequestMapping(value="/quick2")public ModelAndView save2() {/*** Model:模型,作用封装数据* View:视图,作用展示数据*/ModelAndView modelAndView = new ModelAndView();//设置模型数据(放入request域)modelAndView.addObject("username","zhangsan");//设置视图名称modelAndView.setViewName("success");return modelAndView; //请求转发}@RequestMapping(value="/quick3")public ModelAndView save3(ModelAndView modelAndView) {//设置模型数据(放入request域)modelAndView.addObject("username","zhangsan");//设置视图名称modelAndView.setViewName("success");return modelAndView; //请求转发}@RequestMapping(value="/quick4")public String save4(Model model) {model.addAttribute("username","lisi");return "success"; //请求转发}@RequestMapping(value="/quick5")public String save5(HttpServletRequest request) {request.setAttribute("username","wangwu");return "success"; //请求转发}
}

3.1.2、响应文本数据(字符串)

Web基础阶段,客户端访问服务器端,如果想直接回写字符串作为响应体返回的话,只需要使用response.getWriter().print("hello world")即可,那么在Controller中想直接回写字符串该怎样呢?

  • 方法1:通过SpringMVC框架注入的response对象,使用response.getWriter().print("hello world")回写数据,此时不需要视图跳转,业务方法返回值为void。

        @RequestMapping(value="/quick6")public void save6(HttpServletResponse response) throws IOException {response.getWriter().print("hello world");}
  • 方法2:将需要回写的字符串直接返回,但此时需要通过@ResponseBody注解告知SpringMVC框架,方法返回的字符串不是跳转是直接在http响应体中返回。
        @RequestMapping(value="/quick7")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public String save7() throws IOException {return "hello world";}@RequestMapping(value="/quick8")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public String save8() throws IOException {return "{'username':'zhangsan','age':'18'}";}@RequestMapping(value="/quick9")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public String save9() throws IOException {User user = new User();user.setUsername("zhangsan");user.setAge(20);//使用json的转换工具,将对象转换成json格式字符串,再返回ObjectMapper objectMapper = new ObjectMapper();String json = objectMapper.writeValueAsString(user);return json;}
    /*** 响应文本数据*/@RequestMapping("/toText")@ResponseBodypublic String toText() {System.out.println("返回纯文本数据");return "response text";}

3.1.3、响应json数据(对象集合转json数组)

通过SpringMVC帮助我们对对象或集合进行json字符串的转换并回写,为处理器适配器配置消息转换参数,指定使用jackson进行对象或集合的转换,因此需要在spring-mvc.xml中进行如下配置:

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>...
    /*** 响应POJO对象*/@RequestMapping("/toJsonPojo")@ResponseBody // 加上该注解后返回的对象自动转为json格式字符串public User toJsonPojo() {System.out.println("返回json对象数据");User user = new User();user.setName("zhangsan");user.setAge(18);return user;}/*** 响应POJO集合对象*/@RequestMapping("/toJsonList")@ResponseBody // 加上该注解后返回的对象自动转为json格式字符串public List<User> toJsonList() {System.out.println("返回json集合数据");User user1 = new User();user1.setName("张三");user1.setAge(18);;User user2 = new User();user2.setName("李四");user2.setAge(20);;List<User> userList = Arrays.asList(user1, user2);return userList;}

在方法上添加@ResponseBody就可以返回json格式的字符串,但是这样配置比较麻烦,配置的代码比较多,因此,我们可以使用mvc的注解驱动代替上述配置。

spring-mvc.xml:<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"><!--Controller的组件扫描--><context:component-scan base-package="com.itheima"><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan><!--配置内部资源视图解析器--><bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver"><!--  /jsp/success.jsp  --><property name="prefix" value="/jsp/"></property><property name="suffix" value=".jsp"></property></bean><!--配置处理器映射器-->
<!--    <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的注解驱动--><mvc:annotation-driven />
</beans>

在SpringMVC的各个组件中,处理器映射器、处理器适配器、视图解析器称为SpringMVC的三大组件。使用<mvc:annotation-driven>自动加载RequestMappingHandlerMapping(处理映射器)和RequestMappingHandlerAdapter(处理适配器),可用在spring-mvc.xml配置文件中使用<mvc:annotation-driven>替代注解处理器和适配器的配置。同时使用<mvc:annotation-driven>默认底层就会集成jackson进行对象或集合的json格式字符串的转换。

3.2、类型转换器HttpMessageConverter

- HttpMessageConverter接口
public interface HttpMessageConverter<T> {boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);List<MediaType> getSupportedMediaTypes();T read(Class<? extends T> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException;void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotReadableException;
}

4、配置对静态资源的访问放行

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {/*** 放行 非SpringMvc的请求* @param registry*/@Overrideprotected void addResourceHandlers(ResourceHandlerRegistry registry) {// 当访问/pages/????的时候不要走mvc,走/pages目录下的内容registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");// 其他静态资源registry.addResourceHandler("/js/**").addResourceLocations("/js/");registry.addResourceHandler("/css/**").addResourceLocations("/css/");registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");}
}@Configuration
@ComponentScan({"com.clp.controller", "com.clp.config"}) // 添加扫描
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig {
}

5、自定义类型转换器

SpringMVC默认已经提供了一些常用的类型转换器,例如客户端提交的字符串转换成int型进行参数设置。但是不是所有的数据类型都提供了转换器,没有提供的就需要自定义转换器,例如:日期类型的数据就需要自定义转换器。

自定义类型转换器的开发步骤:

  1. 定义转换器实现Converter接口;
  2. 在配置文件中声明转换器;
  3. 在<annotation-driver>中引用转换器。
import org.springframework.core.convert.converter.Converter;import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;public class DateConverter implements Converter<String, Date> {public Date convert(String dateStr) {//将日期字符串转换成日期对象,然后返回SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");Date date = null;try {date = format.parse(dateStr);} catch (ParseException e) {e.printStackTrace();}return date;}
spring-mvc.xml:...<!--mvc的注解驱动--><mvc:annotation-driven conversion-service="conversionService"/><!--声明转换器--><bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean"><property name="converters"><list><bean class="com.converter.DateConverter"></bean></list></property></bean>...
    //localhost:80/user/quick18?date=2020-12-12@RequestMapping(value="/quick18")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public void save17(Date date) throws IOException {System.out.println(date);}

6、获得Servlet相关API

SpringMVC支持使用原始ServletAPI对象作为控制器方法的参数进行注入,常用的参数如下:

  • HttpServletRequest
  • HttpServletResponse
  • HttpSession

    @RequestMapping(value="/quick19")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public void save19(HttpServletRequest request, HttpServletResponse response, HttpSession session) throws IOException {System.out.println(request);System.out.println(response);System.out.println(session);}

7、REST风格

7.1、简介

REST(Representational State Transfer,表现形式状态转换)。根据REST风格对资源进行访问称为RESTful。Restful是一种软件架构风格、设计风格,而不是标准,知识提供了一组设计原则和约束条件。主要用于客户端和服务器交互的软件,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存机制等。

Restful风格的请求是使用"url+请求方式"表示一次请求的目的,HTTP协议里面四个表示操作方式的动词如下:

  • GET:用于获取资源。
  • POST:用于新建资源。
  • PUT:用于更新资源。
  • DELETE:用于删除资源。

上述行为是约定方式,约定不是规范,可以打破,所以称REST风格,而不是REST规范。描述模块的名称通常使用复数,也就是加s的格式描述,表示此类资源,而非单个资源,例如:users、books、accounts...

REST风格的优点:

  • 隐藏资源的访问行为,无法通过地址得知对资源是何种操作;
  • 书写简化。

7.2、入门案例

@Controller
public class UserController {/** @RequestMapping:设置当前控制器方法请求访问路径。*   属性:*       1、value(默认):请求访问路径;*       2、method:http请求动作,标准动作(GET、POST、PUT、DELETE)** @PathVariable:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应。* */@RequestMapping(value = "/users/{id}", method = RequestMethod.GET)@ResponseBodypublic String get(@PathVariable Integer id) {System.out.println("user get ..." + id);return "{'module':'user get'}";}@RequestMapping(value = "/users", method = RequestMethod.GET)@ResponseBodypublic String getAll() {System.out.println("user getAll...");return "{'module':'user getAll'}";}@RequestMapping(value = "/users", method = RequestMethod.POST)@ResponseBodypublic String post(@RequestBody User user) {System.out.println("user post ..." + user);return "{'module':'user post'}";}@RequestMapping(value = "/users/{id}", method = RequestMethod.DELETE)@ResponseBodypublic String delete(@PathVariable Integer id) {System.out.println("user delete ..." + id);return "{'module':'user delete'}";}@RequestMapping(value = "/users", method = RequestMethod.PUT)@ResponseBodypublic String put(@RequestBody User user) {System.out.println("user put ..." + user);return "{'module':'user put'}";}
}

7.3、RESTful快速开发

/*
* @RestController:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能。
* @GetMapping & @PostMapping & @PutMapping & @DeleteMapping:
*   设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作,例如@GetMapping对应GET请求。
* */
//@Controller
//@ResponseBody
@RestController // @RestController = @Controller + @ResponseBody
@RequestMapping("/books")
public class BookController {
//    @RequestMapping(method = RequestMethod.POST)@PostMapping // @PostMapping = @RequestMapping(method = RequestMethod.POST)public String post(@RequestBody Book book) {System.out.println("book post ..." + book);return "{'module':'book post'}";}//    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)@DeleteMapping("/{id}") // @DeleteMapping("/{id}") = @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)public String delete(@PathVariable Integer id) {System.out.println("book delete ..." + id);return "{'module':'book delete'}";}//    @RequestMapping(method = RequestMethod.PUT)@PutMapping // @PutMapping = @RequestMapping(method = RequestMethod.PUT)public String put(@RequestBody Book book) {System.out.println("book put ..." + book);return "{'module':'book put'}";}//    @RequestMapping(value = "/{id}}", method = RequestMethod.GET)@GetMapping("/{id}") // @GetMapping("/{id}") = @RequestMapping(value = "/{id}}", method = RequestMethod.GET)public String get(@PathVariable Integer id) {System.out.println("book get ..." + id);return "{'module':'book get'}";}//    @RequestMapping(method = RequestMethod.GET)@GetMapping // @GetMapping = @RequestMapping(method = RequestMethod.GET)public String getAll() {System.out.println("book getAll ...");return "{'module':'book getAll'}";}
}

8、SSM整合

8.1、SSM整合流程

1、创建工程
2、SSM整合- SpringSpringConfig- MyBatisMyBatisConfigJdbcConfigjdbc.properties- SpringMVCServletConfigSpringMvcConfig
3、功能模块- 表与实体类- dao(接口+自动代理)- service(接口+实现类)业务层接口测试(整合JUnit)- controller表现层接口测试(PostMan)

8.1.1、导入相关坐标

pom.xml:...<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.2.10.RELEASE</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.6</version></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis-spring</artifactId><version>1.3.0</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>5.1.47</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.16</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>com.fasterxml.jackson.core</groupId><artifactId>jackson-databind</artifactId><version>2.9.0</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.9.4</version></dependency></dependencies>...<plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port>80</port><path>/</path></configuration></plugin></plugins>...

8.1.2、SSM整合

jdbc.properties:jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssmdb
jdbc.username=root
jdbc.password=123456
public class JdbcConfig {/** 简单类型的依赖注入使用@Value* */@Value("${jdbc.driver}")private String driver;@Value("${jdbc.url}")private String url;@Value("${jdbc.username}")private String username;@Value("${jdbc.password}")private String password;// 1、定义一个方法获得要管理的对象// 2、添加@Bean表示当前方法的返回值是一个bean/** 引用类型的依赖注入:主需要为bean定义方法设置形参即可,容器会根据 类型 自动装配对象。* */@Beanpublic DataSource dataSource() {DruidDataSource dataSource = new DruidDataSource();dataSource.setDriverClassName(driver);dataSource.setUrl(url);dataSource.setUsername(username);dataSource.setPassword(password);return dataSource;}/*** 创建平台事务管理器* @param dataSource* @return*/@Beanpublic PlatformTransactionManager transactionManager(DataSource dataSource) {DataSourceTransactionManager dtm = new DataSourceTransactionManager();dtm.setDataSource(dataSource);return dtm;}
}
public class MyBatisConfig {/*** SqlSessionFactoryBean专门产生SqlSessionFactory* @return*/@Beanpublic SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();ssfb.setDataSource(dataSource); // 设置数据源ssfb.setTypeAliasesPackage("com.clp.domain"); // 设置实体类别名扫描包return ssfb;}/*** 扫描Dao层的映射信息* @return*/@Beanpublic MapperScannerConfigurer mapperScannerConfigurer() {MapperScannerConfigurer msc = new MapperScannerConfigurer();msc.setBasePackage("com.clp.dao");return msc;}
}
@Configuration
@ComponentScan({"com.clp.service"})
@PropertySource("jdbc.properties")
@Import({JdbcConfig.class, MyBatisConfig.class})
@EnableTransactionManagement // 开启事务管理器
public class SpringConfig {
}
@Configuration
@ComponentScan({"com.clp.controller"})
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig {
}
/*** 定义一个servlet容器启动的配置类,在里面加载spring的配置(需继承AbstractDispatcherServletInitializer,这样tomcat可以探测到这个类)*/
public class ServletConfig extends AbstractDispatcherServletInitializer {/** 注:Web容器(Tomcat)启动时,会加载SpringMvcConfig和SpringConfig配置类。*   这两个配置类产生的容器对象不是同一个对象。*   SpringMVC容器能访问Spring容器,而Spring容器不能访问SpringMVC容器。* */// 加载SpringMVC容器配置@Overrideprotected WebApplicationContext createServletApplicationContext() {AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();ctx.register(SpringMvcConfig.class);return ctx;}// 拦截请求:设置哪些请求归属SpringMVC处理@Overrideprotected String[] getServletMappings() {return new String[]{"/"}; // 所有请求归SpringMVC进行处理}// 加载Spring容器的配置@Overrideprotected WebApplicationContext createRootApplicationContext() {AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();ctx.register(SpringConfig.class);return ctx;}// 设置过滤器,处理中文乱码问题@Overrideprotected Filter[] getServletFilters() {// Post请求中文乱码处理:配置字符过滤器,为web容器添加过滤器并指定字符集,spring-web包中提供了专用的字符过滤器CharacterEncodingFilter filter = new CharacterEncodingFilter();filter.setEncoding("UTF-8");return new Filter[]{filter};}
}

8.1.3、功能模块

public interface BookDao {@Insert("insert into book values(null, #{type}, #{name}, #{description})")void save(Book book);@Update("update book set type=#{type}, name=#{name}, description=#{description} where id = #{id}")void update(Book book);@Delete("delete from book where id = #{id}")void delete(Integer id);@Select("select * from book where id = #{id}")Book getById(Integer id);@Select("select * from book")List<Book> getAll();
}
@Transactional
public interface BookService {boolean save(Book book);boolean update(Book book);boolean delete(Integer id);Book getById(Integer id);List<Book> getAll();
}@Service
public class BookServiceImpl implements BookService {@Autowiredprivate BookDao bookDao;@Overridepublic boolean save(Book book) {bookDao.save(book);return true;}@Overridepublic boolean update(Book book) {bookDao.update(book);return true;}@Overridepublic boolean delete(Integer id) {bookDao.delete(id);return true;}@Overridepublic Book getById(Integer id) {return bookDao.getById(id);}@Overridepublic List<Book> getAll() {return bookDao.getAll();}
}
@RestController // @RestController = @Controller + @ResponseBody
@RequestMapping("/books")
public class BookController {@Autowiredprivate BookService bookService;//    @RequestMapping(method = RequestMethod.POST)@PostMapping // @PostMapping = @RequestMapping(method = RequestMethod.POST)public boolean post(@RequestBody Book book) {return bookService.save(book);}//    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)@DeleteMapping("/{id}") // @DeleteMapping("/{id}") = @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)public boolean delete(@PathVariable Integer id) {return bookService.delete(id);}//    @RequestMapping(method = RequestMethod.PUT)@PutMapping // @PutMapping = @RequestMapping(method = RequestMethod.PUT)public boolean put(@RequestBody Book book) {return bookService.update(book);}//    @RequestMapping(value = "/{id}}", method = RequestMethod.GET)@GetMapping("/{id}") // @GetMapping("/{id}") = @RequestMapping(value = "/{id}}", method = RequestMethod.GET)public Book get(@PathVariable Integer id) {return bookService.getById(id);}//    @RequestMapping(method = RequestMethod.GET)@GetMapping // @GetMapping = @RequestMapping(method = RequestMethod.GET)public List<Book> getAll() {return bookService.getAll();}
}

8.1.4、测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {@Autowiredprivate BookService bookService;@Testpublic void testGetById() {Book book = bookService.getById(1);System.out.println(book);}@Testpublic void testGetAll() {List<Book> bookList = bookService.getAll();System.out.println(bookList);}
}

8.2、表现层数据封装

前端接收数据格式——封装特殊消息到message(msg)属性中。

- 设置统一数据返回结果类:public class Result {private Object data; // 数据private Integer code; // 编码private String msg; // 消息}注意:Result类中的字段并不是固定的,可以根据需要自行增减。提供若干个构造方法,方便操作。- 设置统一数据返回结果编码public class Code {public static final Integer SAVE_OK = 20011;public static final Integer DELETE_OK = 20021;public static final Integer UPDATE_OK = 20031;public static final Integer GET_OK = 20041;public static final Integer SAVE_ERR = 20010;public static final Integer DELETE_ERR = 20020;public static final Integer UPDATE_ERR = 20030;public static final Integer GET_ERR = 20040;}注意:Code类的常量设计也不是固定的,可以根据需要自行增减,例如将查询再进行细分为GET_OK,GET_ALL_OK,GET_PAGE_OK。- 根据情况设定合理的Result@RestController // @RestController = @Controller + @ResponseBody@RequestMapping("/books")public class BookController {@Autowiredprivate BookService bookService;//    @RequestMapping(method = RequestMethod.POST)@PostMapping // @PostMapping = @RequestMapping(method = RequestMethod.POST)public Result post(@RequestBody Book book) {boolean flag = bookService.save(book);return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);}//    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)@DeleteMapping("/{id}") // @DeleteMapping("/{id}") = @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)public Result delete(@PathVariable Integer id) {boolean flag = bookService.delete(id);return new Result(flag ? Code.DELETE_OK : Code.DELETE_ERR, flag);}//    @RequestMapping(method = RequestMethod.PUT)@PutMapping // @PutMapping = @RequestMapping(method = RequestMethod.PUT)public Result put(@RequestBody Book book) {boolean flag = bookService.update(book);return new Result(flag ? Code.UPDATE_OK : Code.UPDATE_ERR, flag);}//    @RequestMapping(value = "/{id}}", method = RequestMethod.GET)@GetMapping("/{id}") // @GetMapping("/{id}") = @RequestMapping(value = "/{id}}", method = RequestMethod.GET)public Result get(@PathVariable Integer id) {Book book = bookService.getById(id);return new Result(book != null ? Code.GET_OK : Code.GET_ERR,book,book != null ? "查询成功" : "查询失败");}//    @RequestMapping(method = RequestMethod.GET)@GetMapping // @GetMapping = @RequestMapping(method = RequestMethod.GET)public Result getAll() {List<Book> bookList = bookService.getAll();return new Result(bookList != null ? Code.GET_OK : Code.GET_ERR,bookList,bookList != null ? "查询成功" : "查询失败");}}

9、异常处理

9.1、异常处理的思路

系统中异常包括两类:预期异常和运行时异常RintimeException,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发、测试等手段减少运行时异常的发生。

系统的Dao、Service、Controller出现都通过throws Exception向上抛出,最后由SpringMVC前端控制器交由异常处理器进行异常处理。如下图:

9.2、异常处理的两种方式

9.2.1、简单异常处理器SimpleMappingExceptionResolver

使用SpringMVC提供的简单异常处理器SimpleMappingExceptionResolver。

SpringMVC已经定义好了该类型转换器,在使用时可以根据项目情况进行相应异常与视图的映射配置。

spring-mvc.xml:...<!--配置简单映射异常处理器--><bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver"><property name="defaultErrorView" value="error.jsp" /><property name="exceptionMappings"><map><entry key="java.lang.ClassCastException" value="error1.jsp"/><entry key="com.exception.MyException" value="error2.jsp"/></map></property></bean>...

9.2.2、自定义异常处理器

实现Spring的异常处理接口HandleExceptionResolver自定义自己的异常处理器。

自定义异常处理步骤:

  1. 创建异常处理器实现HandlerExceptionResolver;
  2. 配置异常处理器;
  3. 编写异常页面;
  4. 测试异常跳转。
package com.exception;public class MyException extends Exception{
}
/************************************************************************************/package com.exception;import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;public class MyExceptionResolver implements HandlerExceptionResolver {@Override/*** 参数Exception:异常对象。* 返回值ModelAndView:跳转的错误视图信息。*/public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {ModelAndView modelAndView = new ModelAndView();if(ex instanceof MyException) {modelAndView.addObject("info","自定义异常");}else if(ex instanceof ClassCastException) {modelAndView.addObject("info","类型转换异常");}modelAndView.setViewName("error.jsp");return null;}
}
spring-mvc.xml:...<!--自定义异常处理器--><bean class="com.exception.MyExceptionResolver" />...

9.3、Spring中的异常处理器

出现异常现象的常见位置与常见诱因如下:

  • 框架内部抛出的异常:因使用不合规导致;
  • 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时);
  • 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等);
  • 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致的异常);
  • 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)。

思考:

  • 各个层级均出现异常,异常代码书写在哪一层?——所有的异常均抛出到表现层进行处理。
  • 表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决?——AOP思想。

Spring中的异常处理器可以集中、统一地处理项目中出现的异常。

// 声明这个类是异常处理器,用于AOP处理异常,需要SpringMvcConfig配置扫描该类所在的包(@ComponentScan)
@RestControllerAdvice
public class ProjectExceptionAdvice {@ExceptionHandler(Exception.class) // 拦截异常// 方法的返回值将会作为响应结果public Result doException(Exception ex) {System.out.println("获取异常");return new Result(666, null);}
}BookController:@PostMapping // @PostMapping = @RequestMapping(method = RequestMethod.POST)public Result post(@RequestBody Book book) {boolean flag = bookService.save(book);int i = 1 / 0; // 手动产生异常return new Result(flag ? Code.SAVE_OK : Code.SAVE_ERR, flag);}

9.4、项目异常处理方案

- 项目异常分类- 业务异常(BusinessException)规范的用户行为产生的异常不规范的用户行为操作产生的异常- 系统异常(SystenException)项目运行过程中可预计且无法避免的异常- 其他异常(Exception)编程人员未预期到的异常- 项目异常处理方案:- 业务异常(BusinessException)发送对应消息传递给用户,提醒规范操作- 系统异常(SystemException)发送固定消息传递给用户,安抚用户发送特定消息给运维人员,提醒用户记录日志- 其他异常(Exception)发送固定消息传递给用户,安抚用户发送特定消息给编程人员,提醒维护(纳入预期范围内)记录日志

步骤:

  1. 自定义项目系统级异常、业务级异常:

    public class SystemException extends RuntimeException{private Integer code; // 异常编号public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public SystemException(Integer code, String message) {super(message);this.code = code;}public SystemException(Integer code, String message, Throwable cause) {super(message, cause);this.code = code;}
    }public class BusinessException extends RuntimeException{private Integer code; // 异常编号public Integer getCode() {return code;}public void setCode(Integer code) {this.code = code;}public BusinessException(Integer code, String message) {super(message);this.code = code;}public BusinessException(Integer code, String message, Throwable cause) {super(message, cause);this.code = code;}
    }
  2. 自定义异常编码(持续补充):
    public class Code {public static final Integer SYSTEM_ERR = 50001;public static final Integer BUSINESS_ERR = 60001;public static final Integer SYSTEM_UNKNOWN_ERR = 59999;
    }
  3. 触发(模拟)自定义异常:
    @Service
    public class BookServiceImpl implements BookService {@Autowiredprivate BookDao bookDao;@Overridepublic boolean save(Book book) {if (true) {throw new BusinessException(Code.BUSINESS_ERR, "请不要使用你的技术挑战我的耐心!");}// 将可能出现的异常进行包装,转换成自定义异常try {int i = 1 / 0;} catch (Exception e) {throw new SystemException(Code.SYSTEM_ERR, "服务器访问超时,请重试!", e);}bookDao.save(book);return true;}
    }
  4. 拦截并处理异常;
    // 声明这个类是异常处理器,用于AOP处理异常,需要SpringMvcConfig配置扫描该类所在的包(@ComponentScan)
    @RestControllerAdvice
    public class ProjectExceptionAdvice {// 处理系统异常@ExceptionHandler(SystemException.class)public Result doSystemException(SystemException ex) {// 记录日志// 发送消息给运维// 发送邮件给开发人员return new Result(ex.getCode(), null, ex.getMessage());}// 处理业务异常@ExceptionHandler(BusinessException.class)public Result doBusinessException(BusinessException ex) {return new Result(ex.getCode(), null, ex.getMessage());}// 处理其他异常@ExceptionHandler(Exception.class) // 拦截异常// 方法的返回值作为出现异常的方法的返回值public Result doException(Exception ex) {return new Result(Code.SYSTEM_UNKNOWN_ERR, null, "系统繁忙,请稍后再试!");}
    }

10、拦截器

10.1、拦截器的概念

拦截器(Interceptor)是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行。SpringMVC的拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。

拦截器的作用:

  • 在指定的方法调用前后执行预先设定的代码;
  • 阻止原始方法的执行。

将拦截器按一定的顺序联结成一条链,这条链称为拦截器链(Interceptor Chain)。在访问被拦截的方法或字段时,拦截器链中的拦截器就会按其之前定义的顺序被调用。拦截器也是AOP思想的具体体现。

10.2、拦截器和过滤器的区别

区别 过滤器(Filter) 拦截器(Interceptor)
使用范围 是servlet规范中的一部分,任何Java Web工程都可以使用。 是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能用。
拦截范围 在url-pattern中配置了/*之后,可以对所有要访问的资源拦截。 仅针对SpringMVC的访问进行增强。在<mvc:mapping path="" />中配置了/**之后,也可以对所有资源进行拦截,但是可以通过<mvc:exclude-mapping path="" />标签排除不需要拦截的资源。

10.3、拦截器入门案例

自定义拦截器很简单,只有如下三步:

  1. 创建拦截器类实现HandlerInterceptor接口;

    /*** 拦截器(interceptor)包在controller包下,因为已经配置了扫描controller包,所以不用再配置interceptor包了*/
    @Component
    public class ProjectInterceptor implements HandlerInterceptor{// 被拦截的原始操作之前运行的方法@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("preHandle");return true; // return false 会终止原始操作的执行。因此下面两个方法不会再执行}// 被拦截的原始操作之后运行的方法@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle");return;}// 被拦截的原始操作之后并且在postHandle()方法后执行的方法@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion");return;}
    }
  2. 配置拦截器;
    @Configuration
    public class SpringMvcSupport extends WebMvcConfigurationSupport {@Autowiredprivate ProjectInterceptor projectInterceptor;/*** 配置拦截器,并设定拦截的访问路径,路径可以通过可变参数设置多个*/@Overrideprotected void addInterceptors(InterceptorRegistry registry) {// addPathPatterns()方法的参数表示拦截的url字符串匹配则进行拦截registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");}
    }@Configuration
    @ComponentScan({"com.clp.controller", "com.clp.config"}) // 添加扫描
    @EnableWebMvc // 开启由json数据转换成对象的功能
    public class SpringMvcConfig {
    }/*
    - xml配置方式:
    spring-mvc.xml:...<!--配置拦截器--><mvc:interceptors><mvc:interceptor><!--对哪些资源执行拦截操作(此处代表对所有)--><mvc:mapping path="/**" /><!--配置哪些资源排除拦截操作--><mvc:exclude-mapping path="/user/login"/><bean class="com.interceptor.MyInterceptor1" /></mvc:interceptor></mvc:interceptors>...
    */- 上述配置的简化版(注意:侵入式较强):
    @Configuration
    @ComponentScan({"com.clp.controller"})
    @EnableWebMvc // 开启由json数据转换成对象的功能
    public class SpringMvcConfig implements WebMvcConfigurer {@Autowiredprivate ProjectInterceptor projectInterceptor;/*** 配置拦截器*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {// addPathPatterns()方法的参数表示拦截的url字符串匹配则进行拦截registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");}
    }
  3. 测试拦截器的拦截效果。

10.4、拦截器HandlerInterceptor接口方法说明

方法名 说明
preHandle() 方法将在请求处理之前进行调用,该方法的返回值是布尔值boolean类型的,当它返回为false时,表示请求结束,后续的Interceptor和Controller都不会再执行;当返回值为true时就会继续调用下一个Interceptor的preHandle方法。
postHandle() 该方法是在当前请求进行处理之后被调用,前提是preHandle方法的返回值为true时才能被调用,且它会在DispatcherServlet进行视图返回渲染之前被调用,所以我们可以在这个方法中对Controller处理之后的ModelAndView对象进行操作。
afterCompletion() 该方法将在整个请求结束之后,也就是在DispatcherServlet渲染了对应的视图之后执行,前提是preHandle方法的返回值为true时才能被调用。

10.5、拦截器执行流程

10.6、拦截器参数

- 前置处理
@Component
public class ProjectInterceptor implements HandlerInterceptor{// 被拦截的原始操作之前运行的方法@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {HandlerMethod hm = (HandlerMethod) handler;String contentType = request.getHeader("Content-Type");System.out.println("preHandle" + contentType);return true; // return false 会终止原始操作的执行。因此下面两个方法不会再执行}
}
- 参数request:请求对象response:响应对象handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
- 返回值若返回值为false:被拦截的处理器将不执行
- 后置处理// 被拦截的原始操作之后运行的方法@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {System.out.println("postHandle");return;}
- 参数:modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整。
- 完成后处理// 被拦截的原始操作之后并且在postHandle()方法后执行的方法@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {System.out.println("afterCompletion");return;}
- 参数:ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理。

10.7、多拦截器执行顺序

10.7.1、拦截器链配置方式

@Configuration
@ComponentScan({"com.clp.controller"})
@EnableWebMvc // 开启由json数据转换成对象的功能
public class SpringMvcConfig implements WebMvcConfigurer {@Autowiredprivate ProjectInterceptor projectInterceptor;@Autowiredprivate ProjectInterceptor2 projectInterceptor2;/*** 配置拦截器*/@Overridepublic void addInterceptors(InterceptorRegistry registry) {// addPathPatterns()方法的参数表示拦截的url字符串匹配则进行拦截// 拦截器链的执行顺序与添加顺序有关registry.addInterceptor(projectInterceptor).addPathPatterns("/books", "/books/*");registry.addInterceptor(projectInterceptor2).addPathPatterns("/books", "/books/*");}
}

10.7.2、拦截器链的运行顺序

  • 当配置多个拦截器时,形成拦截器链。
  • 拦截器链的运行顺序参照拦截器添加顺序为准。
  • 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行。
  • 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作。

11、文件上传

11.1、文件上传三要素

  • 表单项type="file"
  • 表单的提交方式是post
  • 表单的enctype属性是多部份表单形式,及enctype="multipart/form-data"
form.jsp:...<form action="${pageContext.request.contextPath}/quick22" method="post"enctype="multipart/form-data">名称:<input type="text" name="username"><br />文件:<input type="file" name="uploadFile"><br /><input type="submit" value="提交"><br /></form>...

11.2、文件上传原理

当form表单修改为多部分表单时,request.getParameter()将失效;

enctype="application/x-www-form-urlencoded"时,form表单的正文内容格式是:key=value&key=value...;

当form表单的enctype取值为multipart/form-data时,请求正文内容就变成多部分形式:

11.3、单文件上传步骤

  1. 导入fileupload和io坐标;
  2. 配置文件上传解析器;
  3. 编写文件上传代码。
pom.xml:<dependency><groupId>commons-fileupload</groupId><artifactId>commons-fileupload</artifactId><version>1.3.1</version></dependency><dependency><groupId>commons-io</groupId><artifactId>commons-io</artifactId><version>2.3</version></dependency>
spring-mvc.xml:...<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"><!--上传文件总大小--><property name="maxUploadSize" value="5242800" /><!--上传单个文件的大小--><property name="maxUploadSizePerFile" value="5242800" /><!--上传文件的编码类型--><property name="defaultEncoding" value="UTF-8" /></bean>...
    @RequestMapping(value="/quick22")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public void save22(String username, MultipartFile uploadFile) throws IOException {System.out.println(username);//获得文件名称String originalFilename = uploadFile.getOriginalFilename();//保存文件uploadFile.transferTo(new File("D:\\upload\\"+originalFilename));}

11.4、多文件上传实现

多文件上传,只需要将页面修改为多个文件上传项,将方法参数MultipartFile类型修改为MultipartFile[]即可。

    ...<form action="${pageContext.request.contextPath}/quick22" method="post"enctype="multipart/form-data">名称:<input type="text" name="username1"><br />文件:<input type="file" name="uploadFile1"><br />名称:<input type="text" name="username2"><br />文件:<input type="file" name="uploadFile2"><br /><input type="submit" value="提交"><br /></form>...
    @RequestMapping(value="/quick22")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public void save22(String username1,String username2, MultipartFile uploadFile1, MultipartFile uploadFile2) throws IOException {System.out.println(username);//获得文件名称String originalFilename1 = uploadFile1.getOriginalFilename();//保存文件uploadFile1.transferTo(new File("D:\\upload\\"+originalFilename1));//获得文件名称String originalFilename2 = uploadFile1.getOriginalFilename();//保存文件uploadFile2.transferTo(new File("D:\\upload\\"+originalFilename2));}
    <form action="${pageContext.request.contextPath}/quick23" method="post"enctype="multipart/form-data">名称:<input type="text" name="username"><br />文件1:<input type="file" name="uploadFiles"><br />文件2:<input type="file" name="uploadFiles"><br />文件3:<input type="file" name="uploadFiles"><br /><input type="submit" value="提交"><br /></form>
    @RequestMapping(value="/quick23")@ResponseBody //告诉SpringMVC框架,不是页面跳转,而是回写public void save23(String username, MultipartFile[] uploadFiles) throws IOException {System.out.println(username);for(MultipartFile multipartFile : uploadFiles) {//获得文件名称String originalFilename = multipartFile.getOriginalFilename();//保存文件multipartFile.transferTo(new File("D:\\upload\\"+originalFilename));}}

JavaWeb开发框架——SpringMVC相关推荐

  1. Java源码 JavaWeb开发框架 代码 SSH SSM OA ERP CRM Java项目[Java通用框架源码及开发视频教程]

    Java源码 JavaWeb开发框架 代码 SSH SSM OA ERP CRM Java项目 功能简介: A.代码生成器(开发利器) 生成Java各层次的类和JSP等文件,提高开发效率 B.阿里巴巴 ...

  2. 开发框架-SpringMVC

    SpringMVC 什么是SpringMVC? Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面. Spring 框架提供了构建 Web ...

  3. JavaWeb开发框架

    JavaWeb框架 重点摘要: 现在我们web的前端页面都是使用velocity这样的模板语言进行开发,抛弃了jsp,这样的选择又会给我们java的web开发带来什么样的好处. 框架有一个很笼统的好处 ...

  4. JAVAWEB 一一 SpringMVC(注解)

    web.xml <?xml version="1.0" encoding="UTF-8"?> <web-app version="2 ...

  5. JavaWeb开发框架——MyBatis

    目录 1.MyBatis的简介 1.1.原始jdbc操作(查询数据) 1.2.原始jdbc操作的分析 1.3.什么是MyBatis 2.MyBatis的快速入门 2.1.MyBatis开发步骤 3.M ...

  6. <a> 元素相关属性及方法

    <a> 元素 <a>元素用来设置链接.除了网页元素的通用接口(Node接口.Element接口.HTMLElement接口),它还继承了HTMLAnchorElement接口和 ...

  7. 2022编程语言排名, 后端开发语言选型

    文章目录 1 2022 编程语言排名 1.1 IEEE Spectrum 2021年度 1.2 TIOBE指数 2022.05 2 后端开发语言选型 2.1 特点概览 2.2 「PHP 是世界上最好的 ...

  8. 【摘】WebGIS开发从入门到......

    声明:本文不是原创,是转载或摘录,因涉及文章有多个出处,选择转载时地址无法填写多个,所以才选择为原创. 最近有打算想GIS开发方向转,但自己只是在平时的开发工作接触过,所以怎么样系统性的学习,或者如何 ...

  9. GIS开发学习推荐书目

    书名 作者 出版社 版次 GIS 基础 地理信息系统导论 [US] Kang-tsung Chang: 陈健飞 译 电子工业出版社 第七版 ArcGIS地理信息系统空间分析实验教程 汤国安 杨昕 科学 ...

最新文章

  1. R语言ggplot2可视化在可视化图像中添加上限线条、下限线条、添加上下限图例实战
  2. python安装教程win10-PyCharm 安装教程(Windows)
  3. joomla插件开发入门(六)
  4. 1.1 开始起步,配置ruby on rails运行环境
  5. MAT之SA:利用SA算法解决TSP(数据是14个虚拟城市的横纵坐标)问题
  6. 计算机网络划分子网_电网小课堂|通信类:子网划分技术
  7. 如何判断一条曲线是否自己相交?
  8. YV12 and NV12异同,
  9. 安装win8/win10提示无法在驱动器0分区上安装windows解决方法
  10. 浅谈Empty、Nothing
  11. ajax在success方法中打点不能调用
  12. atitit.按钮光标滑过高亮切换以及其他动态效果的实现css html js --attilax总结
  13. 台达PLC-EH3.铆压机,3轴,Z轴(SMC)电缸下降的距 离用的是位置加扭矩模式,台达PLC MODBUS通讯控制台达A2伺服扭矩
  14. ActiveMQ专题2 —— ActiveMQ下载和安装(Linux版)
  15. 计算机桌面显示器,电脑安装多显示器方法图文教程
  16. Java之Snake历程
  17. 用机械键盘敲代码会不会更带感 ?送 9 套雷柏机械键盘
  18. 标准方程法(正规方程法)
  19. danpianji与ros通信_ROS和Arduino通讯新模式——StandardFirmata
  20. QT的Listwidget控件

热门文章

  1. linux 日志压缩及清理,linux下的日志压缩脚本
  2. java 姓 名字_使用Java正则表达式验证名字和姓氏
  3. pc微信逆向最新3.9.2.23版本
  4. 人脸识别——PCA降维
  5. 逻辑越权总结(超详细总结涉及各类越权)
  6. 美食及其简易制作步骤记录[学一个,加一个,学习之余不能忘了吃呀]【持续更新】
  7. [转]缓解压力的5项训练
  8. scrapy由浅入深(三) selenium模拟爬取ajax动态页面(智联招聘)
  9. keynote教程-第一代iPhone的诞生
  10. 用大白话解析函数调用,系统调用和API之间的关系