当前位置:   article > 正文

03—Spring MVC

03—Spring MVC

概述

SpringMVC是一种基于Java实现MVC模型的轻量级web框架

入门案例

创建一个Maven项目,在pom.xml中导入依赖

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <project xmlns="http://maven.apache.org/POM/4.0.0"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6. <groupId>com.spark</groupId>
  7. <artifactId>springmvc-start</artifactId>
  8. <version>1.0-SNAPSHOT</version>
  9. <properties>
  10. <maven.compiler.source>8</maven.compiler.source>
  11. <maven.compiler.target>8</maven.compiler.target>
  12. </properties>
  13. <dependencies>
  14. <dependency>
  15. <groupId>org.springframework</groupId>
  16. <artifactId>spring-webmvc</artifactId>
  17. <version>5.2.10.RELEASE</version>
  18. </dependency>
  19. <dependency>
  20. <groupId>javax.servlet</groupId>
  21. <artifactId>javax.servlet-api</artifactId>
  22. <version>3.1.0</version>
  23. <scope>provided</scope>
  24. </dependency>
  25. </dependencies>
  26. </project>

编写表现层Controller

  1. package com.spark.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. /**
  6. * UserController class
  7. * description: UserController
  8. *
  9. * @author Administrator
  10. * @date 2023/7/26
  11. */
  12. @Controller
  13. public class UserController {
  14. // 提供方法的请求路径
  15. @RequestMapping("/save")
  16. @ResponseBody
  17. public String saveUser(){
  18. System.out.println("user save ...");
  19. return "{module:save}";
  20. }
  21. }

编写SpringMVC配置类加载Controller层注册的Bean

  1. package com.spark.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. /**
  5. * SpringmvcConfig class
  6. * description: SpringMvcConfig
  7. *
  8. * @author Administrator
  9. * @date 2023/7/26
  10. */
  11. @Configuration
  12. @ComponentScan("com.spark.controller")
  13. public class SpringMvcConfig {
  14. }

编写Servlet容器配置类加载SpringMVC配置类

  1. package com.spark.config;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.context.WebApplicationContext;
  4. import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
  5. import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
  6. /**
  7. * ServletInitConfig class
  8. * description: ServletInitConfig
  9. *
  10. * @author Administrator
  11. * @date 2023/7/26
  12. */
  13. @Configuration
  14. public class ServletInitConfig extends AbstractDispatcherServletInitializer {
  15. @Override
  16. protected WebApplicationContext createServletApplicationContext() {
  17. AnnotationConfigWebApplicationContext atx = new AnnotationConfigWebApplicationContext();
  18. atx.register(SpringMvcConfig.class);
  19. return atx;
  20. }
  21. @Override
  22. protected String[] getServletMappings() {
  23. // 所有请求
  24. return new String[] {"/"};
  25. }
  26. @Override
  27. protected WebApplicationContext createRootApplicationContext() {
  28. return null;
  29. }
  30. }

配置Tomcat

打开浏览器访问 URL + Controller定义的方法请求路径查看结果

http://localhost:8082/springmvc_start/save

入门案例的工作流程

① 服务器启动初始化流程

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

② 发送save请求过程

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

Bean加载过程 

一个完整的SpringMVC请求涉及到有表现层(Controller)、数据层(Dao)、业务层(Service)

Controller层由SpringMVC加载,业务层和数据层由Spring加载

Web容器启动后,Spring会加载到SpringMVC的bean,如何进行避免? 

加载Spring控制的bean时排除掉SpringMVC控制的bean

方式一:Spring加载的bean设定扫描范围,并排除掉Controller包内的bean

  1. package com.spark.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.FilterType;
  5. /**
  6. * SpringConfig class
  7. * description: SpringConfig
  8. *
  9. * @author Administrator
  10. * @date 2023/7/26
  11. */
  12. @Configuration
  13. // 方式一:排除掉Controller层,排除规则为注解方式
  14. @ComponentScan(value = "com.spark",
  15. excludeFilters = @ComponentScan.Filter(
  16. type = FilterType.ANNOTATION
  17. )
  18. )
  19. public class SpringConfig {
  20. }

方式二:Spring加载的bean设定扫描范围为精确范围

  1. package com.spark.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.FilterType;
  5. /**
  6. * SpringConfig class
  7. * description: SpringConfig
  8. *
  9. * @author Administrator
  10. * @date 2023/7/26
  11. */
  12. @Configuration
  13. // 方式二:设定精确的扫描范围
  14. @ComponentScan({"com.spark.dao","com.spark.service"})
  15. public class SpringConfig {
  16. }

在Servlet容器配置类中注册Spring配置类

  1. package com.spark.config;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.context.WebApplicationContext;
  4. import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
  5. import org.springframework.web.servlet.support.AbstractDispatcherServletInitializer;
  6. /**
  7. * ServletInitConfig class
  8. * description: ServletInitConfig
  9. *
  10. * @author Administrator
  11. * @date 2023/7/26
  12. */
  13. @Configuration
  14. public class ServletInitConfig extends AbstractDispatcherServletInitializer {
  15. @Override
  16. protected WebApplicationContext createServletApplicationContext() {
  17. AnnotationConfigWebApplicationContext atx = new AnnotationConfigWebApplicationContext();
  18. atx.register(SpringMvcConfig.class);
  19. return atx;
  20. }
  21. @Override
  22. protected String[] getServletMappings() {
  23. // 所有请求
  24. return new String[] {"/"};
  25. }
  26. @Override
  27. protected WebApplicationContext createRootApplicationContext() {
  28. // 注册Spring配置类
  29. AnnotationConfigWebApplicationContext atx = new AnnotationConfigWebApplicationContext();
  30. atx.register(SpringConfig.class);
  31. return atx;
  32. }
  33. }

拓展:简化开发Servlet容器配置类

  1. package com.spark.config;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
  4. /**
  5. * ServletInitConfig class
  6. * description: ServletInitConfig
  7. *
  8. * @author Administrator
  9. * @date 2023/7/26
  10. */
  11. @Configuration
  12. public class ServletInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
  13. @Override
  14. protected Class<?>[] getRootConfigClasses() {
  15. // 注册Spring配置类
  16. return new Class[]{SpringConfig.class};
  17. }
  18. @Override
  19. protected Class<?>[] getServletConfigClasses() {
  20. // 注册SpringMVC配置类
  21. return new Class[]{SpringMvcConfig.class};
  22. }
  23. @Override
  24. protected String[] getServletMappings() {
  25. // 所有请求
  26. return new String[]{"/"};
  27. }
  28. }

Postman 

Postman是一款功能强大的网页调试与发送网页Http请求的Chrome插件,常用于进行接口测试

下载地址:https://www.postman.com/downloads/ 

安装完成后,打开让选择注册用户,可以不进行注册,关闭重新打开就可以进入

启动入门案例IDEA中配置的Tomcat,输入Controller层方法配置的接口地址,查看响应结果

请求与响应 

设置请求路径 

团队多人开发,每个人设置不同的请求路径,有可能会出现冲突,当多人的请求路径相同时,项目启动会出现错误。

通常会在请求路径前设置模块名作为请求路径前缀来解决问题

设置请求路径使用@RequestMapping注解,该注解可以用在类或方法上,前缀一般设置在Controller类上

  1. package com.spark.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. /**
  6. * UserController class
  7. * description: UserController
  8. *
  9. * @author Administrator
  10. * @date 2023/7/26
  11. */
  12. @Controller
  13. // 设置前缀,一般使用模块名
  14. @RequestMapping("/user")
  15. public class UserController {
  16. // 提供方法的请求路径
  17. @RequestMapping("/save")
  18. @ResponseBody
  19. public String saveUser(){
  20. System.out.println("user save ...");
  21. return "{module:save}";
  22. }
  23. }

请求方式GET和POST 

创建一个新的Maven项目,在Controller层添加一个方法用于展示请求传递的参数

  1. package com.spark.controller;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.ResponseBody;
  5. /**
  6. * UserController class
  7. * description: UserController
  8. *
  9. * @author Administrator
  10. * @date 2023/7/26
  11. */
  12. @Controller
  13. // 设置前缀,一般使用模块名
  14. @RequestMapping("/user")
  15. public class UserController {
  16. // 接收请求参数
  17. @RequestMapping("/commonParams")
  18. @ResponseBody
  19. public String commonParams(String name,int age){
  20. System.out.println("普通参数name===>"+name);
  21. System.out.println("普通参数age===>"+age);
  22. return "{module:commonParams}";
  23. }
  24. }

① GET请求传参

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

② POST请求传参

普通参数:使用form表单进行传参,表单参数名与形参变量名相同,定义形参即可接收参数

传递中文会出现乱码问题,需要在Servlet容器配置类中添加过滤器配置,处理中文乱码

  1. package com.spark.config;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.filter.CharacterEncodingFilter;
  4. import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
  5. import javax.servlet.Filter;
  6. /**
  7. * ServletInitConfig class
  8. * description: ServletInitConfig
  9. *
  10. * @author Administrator
  11. * @date 2023/7/26
  12. */
  13. @Configuration
  14. public class ServletInitConfig extends AbstractAnnotationConfigDispatcherServletInitializer{
  15. @Override
  16. protected Class<?>[] getRootConfigClasses() {
  17. // 注册Spring配置类
  18. return new Class[]{SpringConfig.class};
  19. }
  20. @Override
  21. protected Class<?>[] getServletConfigClasses() {
  22. // 注册SpringMVC配置类
  23. return new Class[]{SpringMvcConfig.class};
  24. }
  25. @Override
  26. protected String[] getServletMappings() {
  27. // 所有请求
  28. return new String[]{"/"};
  29. }
  30. // 添加过滤器配置
  31. @Override
  32. protected Filter[] getServletFilters() {
  33. CharacterEncodingFilter filter = new CharacterEncodingFilter();
  34. // 设置字符编码
  35. filter.setEncoding("UTF-8");
  36. return new Filter[]{filter};
  37. }
  38. }

五种请求参数  

① 普通参数传递

普通参数传递要求形参与传参的参数名一致,如果不一致,需要使用@RequestParam注解指定传参的参数名

举例:形参列表的参数名为userName,传参的参数名为name

  1. // 接收请求参数
  2. @RequestMapping("/commonParams")
  3. @ResponseBody
  4. // 将传参的name属性值赋值给形参的userName
  5. public String commonParams(@RequestParam("name") String userName){
  6. System.out.println("普通参数userName===>"+userName);
  7. return "{module:commonParams}";
  8. }

② POJO类型参数传递

POJO参数传递要求请求参数名与形参对象属性名相同,定义POJO类型参数即可接收参数,将请求参数封装为实体类

  1. package com.spark.entity;
  2. /**
  3. * UserEntity class
  4. * description: UserEntity
  5. *
  6. * @author Administrator
  7. * @date 2023/7/30
  8. */
  9. public class UserEntity {
  10. private String userName;
  11. private int age;
  12. public String getUserName() {
  13. return userName;
  14. }
  15. public void setUserName(String userName) {
  16. this.userName = userName;
  17. }
  18. public int getAge() {
  19. return age;
  20. }
  21. public void setAge(int age) {
  22. this.age = age;
  23. }
  24. @Override
  25. public String toString() {
  26. return "UserEntity{" +
  27. "userName='" + userName + '\'' +
  28. ", age=" + age +
  29. '}';
  30. }
  31. }

Controller层定义方法

  1. // 接收POJO参数
  2. @RequestMapping("/pojoParams")
  3. @ResponseBody
  4. public String pojoParams(UserEntity user){
  5. System.out.println("user:::"+user);
  6. return "{module:pojoParams}";
  7. }

③ 嵌套POJO类型参数传递

当实体类中包含其他实体类情况下,请求参数名与形参对象属性名相同,按照对象层次结构关系即可接收嵌套POJO属性参数

添加Address实体类

  1. package com.spark.entity;
  2. /**
  3. * AddressEntity class
  4. * description: AddressEntity
  5. *
  6. * @author Administrator
  7. * @date 2023/7/30
  8. */
  9. public class AddressEntity {
  10. private String city;
  11. public String getCity() {
  12. return city;
  13. }
  14. public void setCity(String city) {
  15. this.city = city;
  16. }
  17. @Override
  18. public String toString() {
  19. return "AddressEntity{" +
  20. "city='" + city + '\'' +
  21. '}';
  22. }
  23. }

UserEntity实体类中添加AddressEntity实体类

  1. package com.spark.entity;
  2. /**
  3. * UserEntity class
  4. * description: UserEntity
  5. *
  6. * @author Administrator
  7. * @date 2023/7/30
  8. */
  9. public class UserEntity {
  10. private String userName;
  11. private int age;
  12. private AddressEntity address;
  13. public String getUserName() {
  14. return userName;
  15. }
  16. public void setUserName(String userName) {
  17. this.userName = userName;
  18. }
  19. public int getAge() {
  20. return age;
  21. }
  22. public void setAge(int age) {
  23. this.age = age;
  24. }
  25. public AddressEntity getAddress() {
  26. return address;
  27. }
  28. public void setAddress(AddressEntity address) {
  29. this.address = address;
  30. }
  31. @Override
  32. public String toString() {
  33. return "UserEntity{" +
  34. "userName='" + userName + '\'' +
  35. ", age=" + age +
  36. ", address=" + address +
  37. '}';
  38. }
  39. }

Controller层定义方法

  1. // 接收嵌套POJO参数
  2. @RequestMapping("/pojoNestParams")
  3. @ResponseBody
  4. public String pojoNestParams(UserEntity user){
  5. System.out.println("user:::"+user);
  6. return "{module:pojoParams}";
  7. }

④ 数组类型参数传递

请求参数名与形参对象属性名相同且请求参数为多个,定义数组类型形参即可接收参数

Controller层定义方法

  1. // 接收数组类型参数
  2. @RequestMapping("/arrayParams")
  3. @ResponseBody
  4. public String arrayParams(String [] likes){
  5. System.out.println(Arrays.toString(likes));
  6. return "{module:arrayParams}";
  7. }

⑤ 集合参数传递

请求参数名与形参集合对象名相同且请求参数为多个,@RequestParam绑定参数关系

Controller层定义方法

  1. // 接收集合类型参数
  2. @RequestMapping("/listParams")
  3. @ResponseBody
  4. public String listParams(@RequestParam List<String>likes){
  5. System.out.println(likes);
  6. return "{module:listParams}";
  7. }

集合类型参数加@RequestParam原因是因为系统会默认将List看做为引用数据类型,会去调用其构造方法去初始化,List类型为接口没有构造方法

json数据传递 

在项目中数据传递一般是使用的是json对象和json数组。

① 实现json对象传递

pom.xml中引入依赖

  1. <dependency>
  2. <groupId>com.fasterxml.jackson.core</groupId>
  3. <artifactId>jackson-databind</artifactId>
  4. <version>2.9.0</version>
  5. </dependency>

在SpringMVC配置类中开启json转换为实体

  1. package com.spark.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.servlet.config.annotation.EnableWebMvc;
  5. /**
  6. * SpringmvcConfig class
  7. * description: SpringMvcConfig
  8. *
  9. * @author Administrator
  10. * @date 2023/7/26
  11. */
  12. @Configuration
  13. @ComponentScan("com.spark.controller")
  14. @EnableWebMvc
  15. public class SpringMvcConfig {
  16. }

Controller层定义方法

  1. // 接收json类型参数
  2. @RequestMapping("/jsonParams")
  3. @ResponseBody
  4. public String jsonParams(@RequestBody UserEntity user){
  5. System.out.println(user);
  6. return "{module:jsonParams}";
  7. }

@RequestBody将请求中请求体包含的数据传递给请求参数,此注解一个处理器方法只能用一次

Postman发送请求

② json数组参数传递 

Controller层定义方法

  1. // 接收json类型参数
  2. @RequestMapping("/jsonArrayParams")
  3. @ResponseBody
  4. public String jsonArrayParams(@RequestBody List<UserEntity> users){
  5. System.out.println(users);
  6. return "{module:jsonArrayParams}";
  7. }

Postman发送请求

@RequestParam和@RequestBody

区别

  • @RequestParam用于接收url地址传参,表单传参
  • @RequestBody用于接收json数据  

应用

  • 后期开发中,发送json格式数据为主,@RequestBody应用较广
  • 如果发送非json数据,选用@RequestParam接收请求参数 

日期型参数传递 

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

接收形参时,根据不同的日期格式设置不同的接收方式

  1. // 接收日期类型参数
  2. @RequestMapping("/dateParams")
  3. @ResponseBody
  4. public String dateParams(Date date,
  5. @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date date2){
  6. System.out.println("Date:"+date);
  7. System.out.println("Date2:"+date2);
  8. return "{module:dateParams}";
  9. }

@DateTimeFormat设置日期类型数据格式 

Rest风格 

概述 

REST表现形式状态转换

传统风格资源描述形式

  • http://localhost/user/getById?id=1
  • http://localhost/user/saveUser

REST风格描述形式

  • http://localhost/user/1
  • http://localhost/user 

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

按照REST风格访问资源时使用行为动作区分对资源进行了何种操作,常用的有四种

  • GET(查询)
  • POST(新增/保存)
  • PUT(修改/更新)
  • DELETE(删除) 

根据REST风格对资源进行访问称为RestFul

入门案例 

使用REST风格约定请求动作,编写表现层Controller

  1. package com.spark.controller;
  2. import com.spark.entity.UserEntity;
  3. import org.springframework.stereotype.Controller;
  4. import org.springframework.web.bind.annotation.RequestBody;
  5. import org.springframework.web.bind.annotation.RequestMapping;
  6. import org.springframework.web.bind.annotation.ResponseBody;
  7. /**
  8. * RestController class
  9. * description: RestController
  10. *
  11. * @author Administrator
  12. * @date 2023/7/31
  13. */
  14. @Controller
  15. @RequestMapping("/users")
  16. public class RestController {
  17. // 约定save方法的请求动作为POST
  18. @RequestMapping(value = "/save",method = RequestMethod.POST)
  19. @ResponseBody
  20. public String save(@RequestBody UserEntity user){
  21. System.out.println(user);
  22. return "{module:save}";
  23. }
  24. // 约定delete方法的请求动作为DELETE
  25. @RequestMapping(value = "/delete",method = RequestMethod.DELETE)
  26. @ResponseBody
  27. public String delete(Integer id){
  28. System.out.println("user id===>"+id);
  29. return "{module:delete}";
  30. }
  31. }

当规定了行为动作后,使用其他方式访问接口会报请求方式不支持异常 

地址传参,使用DELETE方式访问却报404

使用路径传参时,方法的形参列表需要使用@PathVariable表明值从url获取

改写delete方法

  1. // 约定delete方法的请求动作为DELETE
  2. // url指定参数名
  3. @RequestMapping(value = "/delete/{id}",method = RequestMethod.DELETE)
  4. @ResponseBody
  5. // 使用@PathVariable注解表明参数从url获取
  6. public String delete(@PathVariable Integer id){
  7. System.out.println("user id===>"+id);
  8. return "{module:delete}";
  9. }

简化开发 

@RestController注解是@Controller和@ResponseBody的复合注解,以后在表现层只需要使用@RestController注解就可以管理该类并且方法结果都可以原样返回

@RequestMapping中的method属性指明行为动作,也可以使用具体的注解进行简化开发

  • @GetMapping
  • @PostMapping
  • @PutMapping
  • @DeleteMapping 
  1. package com.spark.controller;
  2. import org.springframework.web.bind.annotation.*;
  3. /**
  4. * RestController class
  5. * description: RestController
  6. *
  7. * @author Administrator
  8. * @date 2023/7/31
  9. */
  10. @RestController
  11. @RequestMapping("/books")
  12. public class RestfulController {
  13. @GetMapping("/allBooks")
  14. public String getAllBooks() {
  15. return "{module:getAllBooks}";
  16. }
  17. @PostMapping("/saveBooks")
  18. public String saveBooks(){
  19. return "{module:saveBooks}";
  20. }
  21. @PutMapping("/updateBook")
  22. public String updateBook(){
  23. return "{module:updateBook}";
  24. }
  25. @DeleteMapping("/deleteBook")
  26. public String deleteBook(){
  27. return "{module:deleteBook}";
  28. }
  29. }

SSM整合 

整合配置 

使用web模版创建一个Maven项目,在pom.xml中引入依赖

  1. <dependencies>
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-webmvc</artifactId>
  5. <version>5.2.10.RELEASE</version>
  6. </dependency>
  7. <dependency>
  8. <groupId>org.springframework</groupId>
  9. <artifactId>spring-test</artifactId>
  10. <version>5.2.10.RELEASE</version>
  11. </dependency>
  12. <dependency>
  13. <groupId>org.springframework</groupId>
  14. <artifactId>spring-jdbc</artifactId>
  15. <version>5.2.10.RELEASE</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.mybatis</groupId>
  19. <artifactId>mybatis-spring</artifactId>
  20. <version>2.0.6</version>
  21. </dependency>
  22. <dependency>
  23. <groupId>org.mybatis</groupId>
  24. <artifactId>mybatis</artifactId>
  25. <version>3.5.6</version>
  26. </dependency>
  27. <dependency>
  28. <groupId>com.alibaba</groupId>
  29. <artifactId>druid</artifactId>
  30. <version>1.2.8</version>
  31. </dependency>
  32. <dependency>
  33. <groupId>mysql</groupId>
  34. <artifactId>mysql-connector-java</artifactId>
  35. <version>5.1.49</version>
  36. </dependency>
  37. <dependency>
  38. <groupId>com.fasterxml.jackson.core</groupId>
  39. <artifactId>jackson-core</artifactId>
  40. <version>2.13.1</version>
  41. </dependency>
  42. <dependency>
  43. <groupId>com.fasterxml.jackson.core</groupId>
  44. <artifactId>jackson-databind</artifactId>
  45. <version>2.13.1</version>
  46. </dependency>
  47. <dependency>
  48. <groupId>com.fasterxml.jackson.core</groupId>
  49. <artifactId>jackson-annotations</artifactId>
  50. <version>2.13.1</version>
  51. </dependency>
  52. <dependency>
  53. <groupId>javax.servlet</groupId>
  54. <artifactId>javax.servlet-api</artifactId>
  55. <version>3.1.0</version>
  56. <scope>provided</scope>
  57. </dependency>
  58. <dependency>
  59. <groupId>junit</groupId>
  60. <artifactId>junit</artifactId>
  61. <version>4.12</version>
  62. </dependency>
  63. </dependencies>

resources目录下新建jdbc.properties文件,写入MySQL连接信息

  1. jdbc.driver = com.mysql.jdbc.Driver
  2. jdbc.url = jdbc:mysql://localhost:3306/ssm_db
  3. jdbc.username = root
  4. jdbc.password = root

编写JDBC配置类

  1. package com.spark.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import org.springframework.beans.factory.annotation.Value;
  4. import org.springframework.context.annotation.Bean;
  5. import javax.sql.DataSource;
  6. /**
  7. * JdbcConfig class
  8. * description: JdbcConfig
  9. *
  10. * @author Administrator
  11. * @date 2023/8/3
  12. */
  13. public class JdbcConfig {
  14. @Value("${jdbc.driver}")
  15. private String driver;
  16. @Value("${jdbc.url}")
  17. private String url;
  18. @Value("${jdbc.username}")
  19. private String username;
  20. @Value("${jdbc.password}")
  21. private String password;
  22. // 注册数据源
  23. @Bean
  24. public DataSource getDataSource(){
  25. DruidDataSource dataSource = new DruidDataSource();
  26. dataSource.setDriverClassName(driver);
  27. dataSource.setUrl(url);
  28. dataSource.setUsername(username);
  29. dataSource.setPassword(password);
  30. return dataSource;
  31. }
  32. }

编写Mybatis配置类

  1. package com.spark.config;
  2. import org.mybatis.spring.SqlSessionFactoryBean;
  3. import org.mybatis.spring.mapper.MapperScannerConfigurer;
  4. import org.springframework.context.annotation.Bean;
  5. import javax.sql.DataSource;
  6. /**
  7. * MybatisConfig class
  8. * description: MybatisConfig
  9. *
  10. * @author Administrator
  11. * @date 2023/8/3
  12. */
  13. public class MybatisConfig {
  14. // 获取SqlSessionFactory
  15. @Bean
  16. public SqlSessionFactoryBean getSqlSessionFactoryBean(DataSource dataSource) {
  17. SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
  18. sqlSessionFactoryBean.setDataSource(dataSource);
  19. // 扫描entity包
  20. sqlSessionFactoryBean.setTypeAliasesPackage("com.spark.entity");
  21. return sqlSessionFactoryBean;
  22. }
  23. // 映射dao
  24. @Bean
  25. public MapperScannerConfigurer mapperScannerConfigurer(){
  26. MapperScannerConfigurer configurer = new MapperScannerConfigurer();
  27. configurer.setBasePackage("com.spark.dao");
  28. return configurer;
  29. }
  30. }

编写Spring和SpringMVC配置类

  1. package com.spark.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Import;
  5. import org.springframework.context.annotation.PropertySource;
  6. /**
  7. * SpringConfig class
  8. * description: SpringConfig
  9. *
  10. * @author Administrator
  11. * @date 2023/8/3
  12. */
  13. @Configuration
  14. @PropertySource({"classpath:jdbc.properties"})
  15. @Import({JdbcConfig.class,MybatisConfig.class})
  16. @ComponentScan({"com.spark.service","com.spark.dao"})
  17. public class SpringConfig {
  18. }

  1. package com.spark.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.servlet.config.annotation.EnableWebMvc;
  5. /**
  6. * SpringMvcConfig class
  7. * description: SpringMvcConfig
  8. *
  9. * @author Administrator
  10. * @date 2023/8/3
  11. */
  12. @Configuration
  13. @ComponentScan("com.spark.controller")
  14. @EnableWebMvc
  15. public class SpringMvcConfig {
  16. }

编写Servlet容器配置

  1. package com.spark.config;
  2. import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
  3. /**
  4. * ServletConfig class
  5. * description: ServletConfig
  6. *
  7. * @author Administrator
  8. * @date 2023/8/3
  9. */
  10. public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
  11. @Override
  12. protected Class<?>[] getRootConfigClasses() {
  13. return new Class[]{SpringConfig.class};
  14. }
  15. @Override
  16. protected Class<?>[] getServletConfigClasses() {
  17. return new Class[]{SpringMvcConfig.class};
  18. }
  19. @Override
  20. protected String[] getServletMappings() {
  21. return new String[]{"/"};
  22. }
  23. }

功能模块开发 

数据库创建数据表,并写入数据

  1. DROP TABLE IF EXISTS `books`;
  2. CREATE TABLE `books` (
  3. `id` int(11) NOT NULL AUTO_INCREMENT,
  4. `type` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  5. `name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  6. `description` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  7. PRIMARY KEY (`id`) USING BTREE
  8. ) ENGINE = InnoDB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
  9. INSERT INTO `books` VALUES (1, '计算机理论', 'Spring实战 第5版', 'Spring入门经典教程,深入理解Spring原理技术内幕');
  10. INSERT INTO `books` VALUES (2, '计算机理论', 'Spring 5核心原理与30个类手写实战', '十年沉淀之作,手写Spring精华思想');
  11. INSERT INTO `books` VALUES (3, '计算机理论', 'Spring 5 设计模式', '深入Spring源码剖析Spring源码中蕴含的10大设计模式');
  12. INSERT INTO `books` VALUES (4, '计算机理论', 'Spring MVC+MyBatis开发从入门到项目实战', '全方位解析面向Web应用的轻量级框架,带你成为Spring MVC开发高手');
  13. INSERT INTO `books` VALUES (5, '计算机理论', '轻量级Java Web企业应用实战', '源码级剖析Spring框架,适合已掌握Java基础的读者');
  14. INSERT INTO `books` VALUES (6, '计算机理论', 'Java核心技术 卷I 基础知识(原书第11版)', 'Core Java 第11版,Jolt大奖获奖作品,针对Java SE9、10、11全面更新');
  15. INSERT INTO `books` VALUES (7, '计算机理论', '深入理解Java虚拟机', '5个维度全面剖析JVM,大厂面试知识点全覆盖');
  16. INSERT INTO `books` VALUES (8, '计算机理论', 'Java编程思想(第4版)', 'Java学习必读经典,殿堂级著作!赢得了全球程序员的广泛赞誉');
  17. INSERT INTO `books` VALUES (9, '计算机理论', '零基础学Java(全彩版)', '零基础自学编程的入门图书,由浅入深,详解Java语言的编程思想和核心技术');
  18. INSERT INTO `books` VALUES (10, '市场营销', '直播就该这么做:主播高效沟通实战指南', '李子柒、李佳琦、薇娅成长为网红的秘密都在书中');
  19. INSERT INTO `books` VALUES (11, '市场营销', '直播销讲实战一本通', '和秋叶一起学系列网络营销书籍');
  20. INSERT INTO `books` VALUES (12, '市场营销', '直播带货:淘宝、天猫直播从新手到高手', '一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');

创建实体类,并编写Dao 

  1. package com.spark.entity;
  2. /**
  3. * Book class
  4. * description: Book
  5. *
  6. * @author Administrator
  7. * @date 2023/8/3
  8. */
  9. public class Book {
  10. private Integer id;
  11. private String type;
  12. private String name;
  13. private String description;
  14. public Integer getId() {
  15. return id;
  16. }
  17. public void setId(Integer id) {
  18. this.id = id;
  19. }
  20. public String getType() {
  21. return type;
  22. }
  23. public void setType(String type) {
  24. this.type = type;
  25. }
  26. public String getName() {
  27. return name;
  28. }
  29. public void setName(String name) {
  30. this.name = name;
  31. }
  32. public String getDescription() {
  33. return description;
  34. }
  35. public void setDescription(String description) {
  36. this.description = description;
  37. }
  38. @Override
  39. public String toString() {
  40. return "Book{" +
  41. "id=" + id +
  42. ", type='" + type + '\'' +
  43. ", name='" + name + '\'' +
  44. ", description='" + description + '\'' +
  45. '}';
  46. }
  47. }
  1. package com.spark.dao;
  2. import com.spark.entity.Book;
  3. import org.apache.ibatis.annotations.Delete;
  4. import org.apache.ibatis.annotations.Insert;
  5. import org.apache.ibatis.annotations.Select;
  6. import org.apache.ibatis.annotations.Update;
  7. import java.util.List;
  8. /**
  9. * BookDao class
  10. * description: BookDao
  11. *
  12. * @author Administrator
  13. * @date 2023/8/3
  14. */
  15. public interface BookDao {
  16. @Insert("insert into books(`type`,`name`,`description`) values(#{type},#{name},#{description})")
  17. int save(Book book);
  18. @Update("update books set type=#{type}, name=#{name}, description=#{description} where id = #{id}")
  19. int update(Book book);
  20. @Delete("delete from books where id = #{id}")
  21. int delete(Integer id);
  22. @Select("select * from books where id = #{id}")
  23. Book getById(Integer id);
  24. @Select("select * from books")
  25. List<Book> getAll();
  26. }

定义业务层接口方法并编写实现类

  1. package com.spark.service;
  2. import com.spark.entity.Book;
  3. import java.util.List;
  4. /**
  5. * BookService class
  6. * description: BookService
  7. *
  8. * @author Administrator
  9. * @date 2023/8/3
  10. */
  11. public interface BookService {
  12. boolean addBook(Book book);
  13. boolean updateBook(Book book);
  14. boolean deleteBook(Integer id);
  15. Book selectById(Integer id);
  16. List<Book> selectAll();
  17. }
  1. package com.spark.service.impl;
  2. import com.spark.dao.BookDao;
  3. import com.spark.entity.Book;
  4. import com.spark.service.BookService;
  5. import org.springframework.beans.factory.annotation.Autowired;
  6. import org.springframework.stereotype.Service;
  7. import java.util.List;
  8. /**
  9. * BookServiceImpl class
  10. * description: BookServiceImpl
  11. *
  12. * @author Administrator
  13. * @date 2023/8/3
  14. */
  15. @Service
  16. public class BookServiceImpl implements BookService {
  17. @Autowired
  18. BookDao bookDao;
  19. @Override
  20. public boolean addBook(Book book) {
  21. return bookDao.save(book) > 0;
  22. }
  23. @Override
  24. public boolean updateBook(Book book) {
  25. return bookDao.update(book) > 0;
  26. }
  27. @Override
  28. public boolean deleteBook(Integer id) {
  29. return bookDao.delete(id) > 0;
  30. }
  31. @Override
  32. public Book selectById(Integer id) {
  33. return bookDao.getById(id);
  34. }
  35. @Override
  36. public List<Book> selectAll() {
  37. return bookDao.getAll();
  38. }
  39. }

编写表现层Controller

  1. package com.spark.controller;
  2. import com.spark.entity.Book;
  3. import com.spark.service.BookService;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.web.bind.annotation.*;
  6. import java.util.List;
  7. /**
  8. * BookController class
  9. * description: BookController
  10. *
  11. * @author Administrator
  12. * @date 2023/8/3
  13. */
  14. @RestController
  15. @RequestMapping("/books")
  16. public class BookController {
  17. @Autowired
  18. private BookService bookService;
  19. @PostMapping("/addBook")
  20. public String addBook(@RequestBody Book book){
  21. if(bookService.addBook(book)){
  22. return "添加成功";
  23. }else{
  24. return "添加失败";
  25. }
  26. }
  27. @PutMapping("/updateBook")
  28. public String updateBook(@RequestBody Book book){
  29. if(bookService.updateBook(book)){
  30. return "更新成功";
  31. }else{
  32. return "更新失败";
  33. }
  34. }
  35. @DeleteMapping("/deleteBook")
  36. public String deleteBook(Integer id){
  37. if(bookService.deleteBook(id)){
  38. return "删除成功";
  39. }else{
  40. return "删除失败";
  41. }
  42. }
  43. @GetMapping("/getById")
  44. public Book selectById(Integer id){
  45. return bookService.selectById(id);
  46. }
  47. @GetMapping("/getAll")
  48. public List<Book> selectAll(){
  49. return bookService.selectAll();
  50. }
  51. }

接口测试

编写测试类

  1. package com.spark;
  2. import com.spark.config.SpringConfig;
  3. import com.spark.entity.Book;
  4. import com.spark.service.BookService;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.test.context.ContextConfiguration;
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  10. /**
  11. * BookServiceTest class
  12. * description: BookServiceTest
  13. *
  14. * @author Administrator
  15. * @date 2023/8/5
  16. */
  17. @RunWith(SpringJUnit4ClassRunner.class)
  18. @ContextConfiguration(classes = SpringConfig.class)
  19. public class BookServiceTest {
  20. @Autowired
  21. BookService bookService;
  22. @Test
  23. public void testBookInsert(){
  24. Book book = new Book();
  25. book.setName("Java从入门到放弃");
  26. book.setType("计算机");
  27. book.setDescription("Java从入门到放弃");
  28. bookService.addBook(book);
  29. }
  30. @Test
  31. public void testBookUpdate(){
  32. Book book = new Book();
  33. book.setName("Java从入门到放弃 第二版");
  34. book.setType("计算机");
  35. book.setDescription("Java从入门到放弃第二版");
  36. book.setId(13);
  37. bookService.updateBook(book);
  38. }
  39. @Test
  40. public void testBookDelete(){
  41. bookService.deleteBook(13);
  42. }
  43. @Test
  44. public void testBookSelectById(){
  45. System.out.println(bookService.selectById(1));
  46. }
  47. @Test
  48. public void testBookSelectAll(){
  49. System.out.println(bookService.selectAll());
  50. }
  51. }

拓展:开启事务

Spring配置类中使用注解开启事务管理

  1. package com.spark.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.context.annotation.Import;
  5. import org.springframework.context.annotation.PropertySource;
  6. import org.springframework.transaction.annotation.EnableTransactionManagement;
  7. /**
  8. * SpringConfig class
  9. * description: SpringConfig
  10. *
  11. * @author Administrator
  12. * @date 2023/8/3
  13. */
  14. @Configuration
  15. @PropertySource({"classpath:jdbc.properties"})
  16. @Import({JdbcConfig.class,MybatisConfig.class})
  17. @ComponentScan({"com.spark.service","com.spark.dao"})
  18. // 开启事务
  19. @EnableTransactionManagement
  20. public class SpringConfig {
  21. }

在Jdbc配置类中获取事务管理对象

  1. package com.spark.config;
  2. import com.alibaba.druid.pool.DruidDataSource;
  3. import org.springframework.beans.factory.annotation.Value;
  4. import org.springframework.context.annotation.Bean;
  5. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  6. import javax.sql.DataSource;
  7. /**
  8. * JdbcConfig class
  9. * description: JdbcConfig
  10. *
  11. * @author Administrator
  12. * @date 2023/8/3
  13. */
  14. public class JdbcConfig {
  15. @Value("${jdbc.driver}")
  16. private String driver;
  17. @Value("${jdbc.url}")
  18. private String url;
  19. @Value("${jdbc.username}")
  20. private String username;
  21. @Value("${jdbc.password}")
  22. private String password;
  23. // 注册数据源
  24. @Bean
  25. public DataSource getDataSource(){
  26. DruidDataSource dataSource = new DruidDataSource();
  27. dataSource.setDriverClassName(driver);
  28. dataSource.setUrl(url);
  29. dataSource.setUsername(username);
  30. dataSource.setPassword(password);
  31. return dataSource;
  32. }
  33. // 数据源事务管理对象
  34. @Bean
  35. public DataSourceTransactionManager transactionManager(DataSource dataSource){
  36. DataSourceTransactionManager ds = new DataSourceTransactionManager();
  37. ds.setDataSource(dataSource);
  38. return ds;
  39. }
  40. }

在业务层使用事务管理注解

  1. package com.spark.service;
  2. import com.spark.entity.Book;
  3. import org.springframework.transaction.annotation.Transactional;
  4. import java.util.List;
  5. /**
  6. * BookService class
  7. * description: BookService
  8. *
  9. * @author Administrator
  10. * @date 2023/8/3
  11. */
  12. @Transactional
  13. public interface BookService {
  14. boolean addBook(Book book);
  15. boolean updateBook(Book book);
  16. boolean deleteBook(Integer id);
  17. Book selectById(Integer id);
  18. List<Book> selectAll();
  19. }

表现层与前端数据传输

由于数据传输可能会有多种格式,前端需要分别去解析数据,因此与前端交互时一般需要统一格式。创建结果模型类,封装数据到data属性中,封装操作结果到code属性中,封装特殊消息到message(msg)属性中

  1. public class Result{
  2. private String code;
  3. private String msg;
  4. private Object data;
  5. }

Result类字段并不固定,可以根据需要自行增减,提供若干个构造方法,方便操作 

定义统一结果类

  1. package com.spark.util;
  2. /**
  3. * Result class
  4. * description: Result
  5. *
  6. * @author Administrator
  7. * @date 2023/8/5
  8. */
  9. public class Result {
  10. private String code;
  11. private String msg;
  12. private Object data;
  13. public Result(String code, String msg, Object data) {
  14. this.code = code;
  15. this.msg = msg;
  16. this.data = data;
  17. }
  18. public String getCode() {
  19. return code;
  20. }
  21. public void setCode(String code) {
  22. this.code = code;
  23. }
  24. public String getMsg() {
  25. return msg;
  26. }
  27. public void setMsg(String msg) {
  28. this.msg = msg;
  29. }
  30. public Object getData() {
  31. return data;
  32. }
  33. public void setData(Object data) {
  34. this.data = data;
  35. }
  36. }

定义结果类所需的常量

  1. package com.spark.common;
  2. /**
  3. * RestCommonContent class
  4. * description: RestCommonContent
  5. *
  6. * @author Administrator
  7. * @date 2023/8/5
  8. */
  9. public class RestCommonContent {
  10. // 定义响应编码和响应信息
  11. public static final String RESPONSE_SUCCESS = "操作成功!";
  12. public static final String RESPONSE_ERROR = "操作失败!";
  13. public static final String RESPONSE_SUCCESS_CODE = "200";
  14. public static final String RESPONSE_ERROR_CODE = "9999";
  15. public static final String SELECT_SUCCESS = "查询成功!";
  16. public static final String SELECT_ERROR = "数据不存在,查询失败!";
  17. // 定义数据常量
  18. public static final String CONTENT_EMPTY = "";
  19. public static final String CONTENT_NULL = null;
  20. }

 改写表现层Controller

  1. package com.spark.controller;
  2. import com.spark.common.RestCommonContent;
  3. import com.spark.entity.Book;
  4. import com.spark.service.BookService;
  5. import com.spark.util.Result;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.util.ObjectUtils;
  8. import org.springframework.web.bind.annotation.*;
  9. import java.util.List;
  10. /**
  11. * BookController class
  12. * description: BookController
  13. *
  14. * @author Administrator
  15. * @date 2023/8/3
  16. */
  17. @RestController
  18. @RequestMapping("/books")
  19. public class BookController {
  20. @Autowired
  21. private BookService bookService;
  22. @PostMapping("/addBook")
  23. public Result addBook(@RequestBody Book book){
  24. if(bookService.addBook(book)){
  25. return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
  26. RestCommonContent.RESPONSE_SUCCESS,
  27. true);
  28. }else{
  29. return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
  30. RestCommonContent.RESPONSE_ERROR,
  31. false);
  32. }
  33. }
  34. @PutMapping("/updateBook")
  35. public Result updateBook(@RequestBody Book book){
  36. if(bookService.updateBook(book)){
  37. return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
  38. RestCommonContent.RESPONSE_SUCCESS,
  39. true);
  40. }else{
  41. return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
  42. RestCommonContent.RESPONSE_ERROR,
  43. false);
  44. }
  45. }
  46. @DeleteMapping("/deleteBook")
  47. public Result deleteBook(Integer id){
  48. if(bookService.deleteBook(id)){
  49. return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
  50. RestCommonContent.RESPONSE_SUCCESS,
  51. true);
  52. }else{
  53. return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
  54. RestCommonContent.RESPONSE_ERROR,
  55. false);
  56. }
  57. }
  58. @GetMapping("/getById")
  59. public Result selectById(Integer id){
  60. Book book = bookService.selectById(id);
  61. if(ObjectUtils.isEmpty(book)){
  62. return new Result(RestCommonContent.RESPONSE_ERROR_CODE,
  63. RestCommonContent.SELECT_ERROR,
  64. RestCommonContent.CONTENT_EMPTY);
  65. }
  66. return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
  67. RestCommonContent.SELECT_SUCCESS,
  68. book);
  69. }
  70. @GetMapping("/getAll")
  71. public Result selectAll(){
  72. List<Book> books = bookService.selectAll();
  73. if(ObjectUtils.isEmpty(books)){
  74. return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
  75. RestCommonContent.SELECT_SUCCESS,
  76. RestCommonContent.CONTENT_EMPTY);
  77. }
  78. return new Result(RestCommonContent.RESPONSE_SUCCESS_CODE,
  79. RestCommonContent.SELECT_SUCCESS,
  80. books);
  81. }
  82. }

异常处理器

开发中避免不了会遇到异常现象,如果不做处理,前端页面获取错误信息时拿到的是错误页面,无法进行解析,此时需要对异常进行统一处理

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

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

Spring提供了异常处理器,集中的、统一的处理项目中出现的异常

  1. @RestControllerAdvice
  2. public class ProjectExceptionAdvice{
  3. @ExceptionHandler(Exception.class)
  4. public Result doException(Exception ex){
  5. // 处理异常返回结果
  6. }
  7. }

@ExceptionHandler专用于异常处理的控制器方法上方,设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行

项目异常分类

业务异常(BusinessException)

  • 不规范的用户行为产生的异常 

系统异常(SystemException)

  • 项目运行过程中可预计且无法避免的异常 

其他异常(Exception)

  • 编程人员未预期的异常 

项目异常处理

编写业务异常类和系统异常类

  1. package com.spark.exception;
  2. /**
  3. * BusinessException class
  4. * description: BusinessException
  5. *
  6. * @author Administrator
  7. * @date 2023/8/5
  8. */
  9. public class BusinessException extends RuntimeException{
  10. private String code; // 错误编码
  11. private String tranCode; // 交易码(接口号)
  12. public BusinessException(String code, String message, String tranCode) {
  13. super(message);
  14. this.code = code;
  15. this.tranCode = tranCode;
  16. }
  17. public BusinessException(String code,String message, String tranCode, Throwable cause) {
  18. super(message, cause);
  19. this.code = code;
  20. this.tranCode = tranCode;
  21. }
  22. public String getCode() {
  23. return code;
  24. }
  25. public void setCode(String code) {
  26. this.code = code;
  27. }
  28. public String getTranCode() {
  29. return tranCode;
  30. }
  31. public void setTranCode(String tranCode) {
  32. this.tranCode = tranCode;
  33. }
  34. }
  1. package com.spark.exception;
  2. /**
  3. * SystemException class
  4. * description: SystemException
  5. *
  6. * @author Administrator
  7. * @date 2023/8/5
  8. */
  9. public class SystemException extends RuntimeException {
  10. private String code; // 错误编码
  11. private String tranCode; // 交易码(接口号)
  12. public SystemException(String code, String message, String tranCode) {
  13. super(message);
  14. this.code = code;
  15. this.tranCode = tranCode;
  16. }
  17. public SystemException(String code,String message, String tranCode, Throwable cause) {
  18. super(message, cause);
  19. this.code = code;
  20. this.tranCode = tranCode;
  21. }
  22. public String getCode() {
  23. return code;
  24. }
  25. public void setCode(String code) {
  26. this.code = code;
  27. }
  28. public String getTranCode() {
  29. return tranCode;
  30. }
  31. public void setTranCode(String tranCode) {
  32. this.tranCode = tranCode;
  33. }
  34. }

自定义异常错误编码和错误信息 

  1. package com.spark.common;
  2. /**
  3. * ExceptionContent class
  4. * description: ExceptionContent
  5. *
  6. * @author Administrator
  7. * @date 2023/8/5
  8. */
  9. public class ExceptionContent {
  10. public static final String BUSINESS_ERROR_CODE = "50001";
  11. public static final String BUSINESS_DATA_ERROR = "数据有误,请检查您的数据格式是否正确!";
  12. public static final String BUSINESS_ERROR_MSG = "操作有误,请联系管理员!";
  13. public static final String SYSTEM_ERROR_CODE = "50002";
  14. public static final String SYSTEM_ERROR_MSG = "服务器异常,请您稍后重试!";
  15. public static final String SYSTEM_UNKNOWN_ERROR_CODE = "50003";
  16. public static final String SYSTEM_UNKNOWN_MSG = "系统繁忙,请联系管理员!";
  17. }

改写业务层根据id查询Book方法,处理异常

  1. @Override
  2. public Book selectById(Integer id) {
  3. if(id<=0){
  4. throw new BusinessException(ExceptionContent.BUSINESS_ERROR_CODE,
  5. ExceptionContent.BUSINESS_DATA_ERROR,
  6. "BK1004",
  7. new Throwable(ExceptionContent.BUSINESS_DATA_ERROR));
  8. }
  9. return bookDao.getById(id);
  10. }

编写异常处理器统一处理异常

  1. package com.spark.exception;
  2. import com.spark.common.ExceptionContent;
  3. import com.spark.util.Result;
  4. import org.springframework.web.bind.annotation.ExceptionHandler;
  5. import org.springframework.web.bind.annotation.RestControllerAdvice;
  6. /**
  7. * ProjectExceptionAdvice class
  8. * description: ProjectExceptionAdvice
  9. *
  10. * @author Administrator
  11. * @date 2023/8/5
  12. */
  13. @RestControllerAdvice
  14. public class ProjectExceptionAdvice {
  15. // 处理业务异常
  16. @ExceptionHandler
  17. public Result doBusinessException(BusinessException ex){
  18. // 记录日志
  19. System.out.println(ex.getTranCode() + "出现异常");
  20. return new Result(ex.getCode(),ex.getMessage(),null);
  21. }
  22. // 处理系统异常
  23. @ExceptionHandler
  24. public Result doSystemException(SystemException sy){
  25. // 记录日志
  26. System.out.println(sy.getTranCode() + "出现异常");
  27. return new Result(sy.getCode(),sy.getMessage(),null);
  28. }
  29. // 处理其他未知异常
  30. @ExceptionHandler
  31. public Result doException(Exception ex){
  32. // 记录日志
  33. System.out.println("出现未知异常");
  34. System.out.println("异常信息如下:");
  35. System.out.println(ex.getMessage());
  36. return new Result(ExceptionContent.SYSTEM_UNKNOWN_ERROR_CODE,
  37. ExceptionContent.SYSTEM_UNKNOWN_MSG,
  38. null);
  39. }
  40. }

前后台联调 

由于后台Servlet容器配置里面是拦截一切请求,前端代码在项目的webapp目录下,Tomcat启动后,去访问前端页面会被拦截,因此需要对前端资源进行放行

编写SpringMVC资源放行配置类

  1. package com.spark.config;
  2. import org.springframework.context.annotation.Configuration;
  3. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  4. import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
  5. /**
  6. * SpringMvcSupportConfig class
  7. * description: SpringMvcSupportConfig
  8. *
  9. * @author Administrator
  10. * @date 2023/8/5
  11. */
  12. @Configuration
  13. public class SpringMvcSupportConfig extends WebMvcConfigurationSupport {
  14. @Override
  15. protected void addResourceHandlers(ResourceHandlerRegistry registry) {
  16. registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
  17. registry.addResourceHandler("/css/**").addResourceLocations("/css/");
  18. registry.addResourceHandler("/js/**").addResourceLocations("/js/");
  19. registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
  20. }
  21. }

在SpringMVC配置类中扫描config包

  1. package com.spark.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.servlet.config.annotation.EnableWebMvc;
  5. /**
  6. * SpringMvcConfig class
  7. * description: SpringMvcConfig
  8. *
  9. * @author Administrator
  10. * @date 2023/8/3
  11. */
  12. @Configuration
  13. @ComponentScan({"com.spark.controller","com.spark.config"})
  14. @EnableWebMvc
  15. public class SpringMvcConfig {
  16. }

 拦截器

概述 

拦截器是一种动态拦截方法调用的机制,在SpringMVC中动态拦截控制器方法的执行

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

拦截器和过滤器的区别

  • 归属不同。Filter属于Servlet技术,Inteceptor属于SpringMVC技术
  • 拦截内容不同。Filter对所有访问进行增强,Inteceptor仅对SpringMVC的访问进行增强 

入门案例 

编写拦截器

  1. package com.spark.interceptor;
  2. import org.springframework.stereotype.Component;
  3. import org.springframework.web.servlet.HandlerInterceptor;
  4. import org.springframework.web.servlet.ModelAndView;
  5. import javax.servlet.http.HttpServletRequest;
  6. import javax.servlet.http.HttpServletResponse;
  7. /**
  8. * ProjectInteceptor class
  9. * description: ProjectInteceptor
  10. *
  11. * @author Administrator
  12. * @date 2023/8/7
  13. */
  14. @Component
  15. public class ProjectInteceptor implements HandlerInterceptor {
  16. @Override
  17. public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
  18. System.out.println("原始方法执行前执行...");
  19. return true;
  20. }
  21. @Override
  22. public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
  23. System.out.println("原始方法执行后执行...");
  24. }
  25. @Override
  26. public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  27. System.out.println("postHandle执行后执行...");
  28. }
  29. }

SpringMVC配置类中添加拦截器并扫描

  1. package com.spark.config;
  2. import com.spark.interceptor.ProjectInteceptor;
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.context.annotation.Configuration;
  5. import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
  6. import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
  7. import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
  8. /**
  9. * SpringMvcSupportConfig class
  10. * description: SpringMvcSupportConfig
  11. *
  12. * @author Administrator
  13. * @date 2023/8/5
  14. */
  15. @Configuration
  16. public class SpringMvcSupportConfig extends WebMvcConfigurationSupport {
  17. @Autowired
  18. ProjectInteceptor projectInteceptor;
  19. @Override
  20. protected void addResourceHandlers(ResourceHandlerRegistry registry) {
  21. registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
  22. registry.addResourceHandler("/css/**").addResourceLocations("/css/");
  23. registry.addResourceHandler("/js/**").addResourceLocations("/js/");
  24. registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
  25. }
  26. // 添加拦截器
  27. @Override
  28. public void addInterceptors(InterceptorRegistry registry) {
  29. registry.addInterceptor(projectInteceptor).addPathPatterns("/books/*","/books");
  30. }
  31. }
  1. package com.spark.config;
  2. import org.springframework.context.annotation.ComponentScan;
  3. import org.springframework.context.annotation.Configuration;
  4. import org.springframework.web.servlet.config.annotation.EnableWebMvc;
  5. /**
  6. * SpringMvcConfig class
  7. * description: SpringMvcConfig
  8. *
  9. * @author Administrator
  10. * @date 2023/8/3
  11. */
  12. @Configuration
  13. @ComponentScan({"com.spark.controller","com.spark.config","com.spark.interceptor"})
  14. @EnableWebMvc
  15. public class SpringMvcConfig{
  16. }

拦截器链 

当配置多个拦截器时,形成拦截器链

多拦截器执行顺序

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

preHandle方法按照拦截器配置顺序执行,postHandle和afterCompletion方法按照配置顺序倒序执行 

工作流程

  1. 用户发送请求至前端控制器DispatcherServlet
  2. DispatcherServlet收到请求后,调用HandlerMapping处理器映射器,请求获取Handle
  3. 处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
  4. DispatcherServlet调用HandlerAdapter处理器适配器
  5. HandlerAdapter经过适配调用具体处理器(Handler,也叫后端控制器)
  6. Handler执行完毕后返回ModelAndView
  7. HandlerAdapter将Handler执行结果ModelAndView返回给DispatcherServlet
  8. DispatcherServlet将ModelAndView传给ViewResolver视图解析器进行解析
  9. ViewResolver解析后返回具体View
  10. DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
  11. DispatcherServlet响应用户 
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/919916
推荐阅读
相关标签
  

闽ICP备14008679号