4.SSM-SpringMVC之REST风格、SSM整合、拦截器

3.REST风格

REST(Representational State Transfer),表现形式状态转换
传统风格资源描述形式
http://localhost:8080/user/getById?id=1
http://localhost:8080/user/saveUser

REST风格描述形式
http://localhost:8080/user/1
http://localhost:8080/user

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

按照REST风格访问资源时使用行为动作区分对资源进行了何种操作
http://localhost/users 查询全部用户信息 GET(查询)
http://localhost/users/1 查询指定用户信息 GET(查询)
http://localhost/users 添加用户信息 POST(新增/保存)
http://localhost/users 修改用户信息 PUT(修改/更新)
http://localhost/users/1 删除用户信息 DELETE(删除)

3.1 RESTful入门案例

步骤:
1.设定http请求动作(动词)
@RequestMapping(value = “/users”,method = RequestMethod.POST)
2.设定请求参数(路径变量)
@RequestMapping(value = “/users/{id}”,method = RequestMethod.DELETE)
public String delete(@PathVariable Integer id)

使用的注解:
@RequestMapping:设置当前控制器方法请求访问路径
属性:value(默认):请求访问路径
method:http请求动作,标准动作(GET/POST/PUT/DELETE)

@PathVariable:绑定路径参数与处理器方法形参间的关系,要求路径参数名与形参名一一对应

参数接受注解对比
@RequestBody、@RequestParam、@PathVariable

  • 区别
    • @RequestParam用于接收url地址传参或表单传参
    • @RequestBody用于接收json数据
    • @PathVariable用于接收路径参数,使用{参数名称}描述路径参数
  • 应用
    • 后期开发中,发送请求参数超过1个时,以json格式为主,@RequestBody应用较广
    • 如果发送非json格式数据,选用@RequestParam接收请求参数
    • 采用RESTful进行开发,当参数数量较少时,例如1个,可以采用@PathVariable接收请求路径变量,通常用于传递id值

UserController类(目前的开发方式)

package com.example.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class UserController {
    @RequestMapping("/save")
    @ResponseBody
    public String save(){
        System.out.println("user save...");
        return "{'module':'user save'}";
    }

    @RequestMapping("/delete")
    @ResponseBody
    public String delete(){
        System.out.println("user delete...");
        return "{'module':'user delete'}";
    }

    @RequestMapping("/update")
    @ResponseBody
    public String update(){
        System.out.println("user update...");
        return "{'module':'user update'}";
    }

    @RequestMapping("/getById")
    @ResponseBody
    public String getById(){
        System.out.println("user getById...");
        return "{'module':'user getById'}";
    }

    @RequestMapping("/getAll")
    @ResponseBody
    public String getAll(){
        System.out.println("user getAll...");
        return "{'module':'user getAll'}";
    }
}

REST风格

package com.example.controller;

import com.example.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

@Controller
public class UserController {
    @RequestMapping(value = "/users",method = RequestMethod.POST)
    @ResponseBody
    public String save(){
        System.out.println("user save...");
        return "{'module':'user save'}";
    }

    @RequestMapping(value = "/users/{id}",method = RequestMethod.DELETE)
    @ResponseBody
    public String delete(@PathVariable Integer id){
        System.out.println("user delete..."+id);
        return "{'module':'user delete'}";
    }

    @RequestMapping(value = "/users",method = RequestMethod.PUT)
    @ResponseBody
    public String update(@RequestBody User user){
        System.out.println("user update..." + user);
        return "{'module':'user update'}";
    }

    @RequestMapping(value = "/users/{id}",method = RequestMethod.GET)
    @ResponseBody
    public String getById(@PathVariable Integer id){
        System.out.println("user getById..." + id);
        return "{'module':'user getById'}";
    }

    @RequestMapping(value = "/users",method = RequestMethod.GET)
    @ResponseBody
    public String getAll(){
        System.out.println("user getAll...");
        return "{'module':'user getAll'}";
    }
}

3.2 TESTful快速开发

@RestController:设置当前控制器类为RESTful风格,等同于@Controller与@ResponseBody两个注解组合功能
@PostMapping、@DeleteMapping、@PutMapping、@GetMapping:设置当前控制器方法请求访问路径与请求动作,每种对应一个请求动作

快速开发

package com.example.controller;

import com.example.domain.User;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/users")
public class UserController {
    @PostMapping
    public String save(){
        System.out.println("user save...");
        return "{'module':'user save'}";
    }

    @DeleteMapping("/{id}")
    public String delete(@PathVariable Integer id){
        System.out.println("user delete..."+id);
        return "{'module':'user delete'}";
    }

    @PutMapping
    public String update(@RequestBody User user){
        System.out.println("user update..." + user);
        return "{'module':'user update'}";
    }

    @GetMapping ("/{id}")
    public String getById(@PathVariable Integer id){
        System.out.println("user getById..." + id);
        return "{'module':'user getById'}";
    }

    @GetMapping
    public String getAll(){
        System.out.println("user getAll...");
        return "{'module':'user getAll'}";
    }
}

3.3 案例:基于RESTful页面数据交互

3.3.1 后台接口开发

配置SpringMvcConfig和ServletContainersInitConfig

① 添加Book类

package com.example.domain;

public class Book {
    private Integer id;
    private String type;
    private String name;
    private String description;

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", type='" + type + '\'' +
                ", name='" + name + '\'' +
                ", description='" + description + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

② BookController类

package com.example.controller;

import com.example.domain.Book;
import org.springframework.web.bind.annotation.*;

import java.util.ArrayList;
import java.util.List;

@RestController
@RequestMapping("/books")
public class BookController {
    @PostMapping
    public String save(@RequestBody Book book){
        System.out.println("book save ==> " + book);
        return "{'module':'book save success'}";
    }

    @GetMapping
    public List<Book> getAll(){
        List<Book> bookList = new ArrayList<>();
        Book book1 = new Book();
        book1.setType("计算机");
        book1.setName("SpringMVC入门教程");
        book1.setDescription("小试牛刀");

        Book book2 = new Book();
        book2.setType("计算机");
        book2.setName("SpringMVC实战教程");
        book2.setDescription("一代宗师");

        bookList.add(book1);
        bookList.add(book2);
        return bookList;
    }

}

③ 测试
发送Post请求用于测试保存图书接口
http://localhost:8080/books
发送的json数据

{
    "type":"计算机",
    "name":"SPringMVC终极开发",
    "description":"这是一本好书"
}

发送Get请求用于测试查询所有图书接口
http://localhost:8080/books

3.3.2 页面访问处理

将REST功能页面导入webapp中(链接:https://pan.baidu.com/s/1Eb0Dv1Z3Iaxa5zxAQkZUGg 提取码:nxve)

① 创建SpringMvcSupport(放行非SpringMvc的请求)

package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        //当访问/page/???时候,走/pages目录下的内容
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }
}

② SpringMvcConfig中添加扫描路径

package com.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan({"com.example.controller","com.example.config"})
@EnableWebMvc
public class SpringMvcConfig {
}

访问:http://localhost:8080/pages/books.html

③ 前端页面通过异步提交访问后台控制器
books.html中

//添加
saveBook () {
    axios.post("/books",this.formData).then((res)=>{

    });
},

//主页列表查询
getAll() {
    axios.get("/books").then((res)=>{
        this.dataList = res.data;
    });
},

③ 重启容器,访问:http://localhost:8080/pages/books.html
点击新建按钮,然后输入一些输入,在确认,查看idea是否接收到保存数据

4. SSM整合

SSM整合流程

  1. 创建工程
  2. SSM整合
    • Spring
      • SpringConfig
    • MyBatis
      • MybatisConfig
      • JdbcConfig
      • jdbc.properties
    • SpringMVC
      • ServletCofig
      • SpringMvcConfig
  3. 功能模块
    • 表与实体类
    • dao(接口+自动代理)
    • service(接口+实现类)
      • 业务层接口测试(整合JUnit)
    • controller
      • 表现层接口测试(PostMan)、

1.新建工程
2.添加依赖

<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>

3.创建目录,SSM整合配置
com.example下创建config、controller、dao、domain、service.impl目录
环境准备:

SpringConfig核心配置

package com.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

@Configuration
@ComponentScan({"com.example.service"})
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}

resources下创建jdbc.properties(根据自己的mysql数据库更改)

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.1.224:3306/ssm_db?useSSL=false
jdbc.username=root
jdbc.password=123456

JdbcConfig配置类

package com.example.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

MybatisConfig配置类

package com.example.config;

import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;

import javax.sql.DataSource;

public class MybatisConfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        factoryBean.setDataSource(dataSource);
        factoryBean.setTypeAliasesPackage("com.example.domain");
        return factoryBean;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.example.dao");
        return msc;
    }
}

SpringMvcConfig配置类

package com.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan({"com.example.controller"})
@EnableWebMvc
public class SpringMvcConfig {
}

ServletConfig

package com.example.config;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import javax.servlet.Filter;

public class ServletConfig extends AbstractAnnotationConfigDispatcherServletInitializer {
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[]{SpringConfig.class};
    }

    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }

    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    //乱码处理
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter filter = new CharacterEncodingFilter();
        filter.setEncoding("utf-8");
        return new Filter[]{filter};
    }
}

3.功能模块
mysql建表

CREATE DATABASE ssm_db;
USE ssm_db;

-- ----------------------------
-- Table structure for tbl_book
-- ----------------------------
DROP TABLE IF EXISTS `tbl_book`;
CREATE TABLE `tbl_book`  (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `type` VARCHAR(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `name` VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `description` VARCHAR(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = INNODB AUTO_INCREMENT = 13 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = DYNAMIC;

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

BookDao

package com.example.dao;

import com.example.domain.Book;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

public interface BookDao {
    @Insert("insert into tbl_book values(null,#{type},#{name},#{description})")
    public void save(Book book);

    @Update("update tbl_book set type=#{type},name=#{name},description=#{description} where id = #{id}")
    public void update(Book book);

    @Delete("delete from tbl_book where id = #{id}")
    public void delete(Integer id);

    @Select("select * from tbl_book where id = #{id}")
    public Book getById(Integer id);

    @Select("select * from tbl_book")
    public List<Book> getAll();
}

Book

package com.example.domain;

public class Book {
    private Integer id;
    private String type;
    private String name;
    private String description;

    @Override
    public String toString() {
        return "Book{" +
                "id=" + id +
                ", type='" + type + '\'' +
                ", name='" + name + '\'' +
                ", description='" + description + '\'' +
                '}';
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

BookService

package com.example.service;

import com.example.domain.Book;

import java.util.List;

public interface BookService {
    /*
    * 保存
    * */
    public boolean save(Book book);

    /*
     * 修改
     * */
    public boolean update(Book book);

    /*
     * 根据id删除
     * */
    public boolean delete(Integer id);

    /*
     * 根据id查询
     * */
    public Book getById(Integer id);

    /*
     * 查询全部
     * */
    public List<Book> getAll();
}

BookServiceImpl

package com.example.service.impl;

import com.example.dao.BookDao;
import com.example.domain.Book;
import com.example.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;

    @Override
    public boolean save(Book book) {
        bookDao.save(book);
        return true;
    }

    @Override
    public boolean update(Book book) {
        bookDao.update(book);
        return true;
    }

    @Override
    public boolean delete(Integer id) {
        bookDao.delete(id);
        return true;
    }

    @Override
    public Book getById(Integer id) {
        return bookDao.getById(id);
    }

    @Override
    public List<Book> getAll() {
        return bookDao.getAll();
    }
}

BookController

package com.example.controller;

import com.example.domain.Book;
import com.example.service.BookService;
import org.apache.ibatis.annotations.Delete;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/books")
public class BookController {
    @Autowired
    private BookService bookService;

    @PostMapping
    public boolean save(@RequestBody Book book) {
        return bookService.save(book);
    }

    @PutMapping
    public boolean update(@RequestBody Book book) {
        return bookService.update(book);
    }

    @DeleteMapping("/{id}")
    public boolean delete(@PathVariable Integer id) {
        return bookService.delete(id);
    }

    @GetMapping("/{id}")
    public Book getById(@PathVariable Integer id) {
        return bookService.getById(id);
    }

    @GetMapping
    public List<Book> getAll() {
        return bookService.getAll();
    }
}

4.接口测试

package com.example.service;


import com.example.config.SpringConfig;
import com.example.domain.Book;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class BookServiceTest {
    @Autowired
    private BookService bookService;

    @Test
    public void testGetById(){
        Book book = bookService.getById(1);
        System.out.println(book);
    }

    @Test
    public void testGetAll(){
        List<Book> list = bookService.getAll();
        System.out.println(list);
    }
}

采用Postman发送请求:
① 保存图书,发送post请求
http://localhost:8080/books

{
    "type":"计算机",
    "name":"SPringMVC终极开发",
    "description":"这是一本好书"
}

在mysql中查看

② 修改图书,发送put请求
http://localhost:8080/books

{
    "id":13,
    "type":"计算机",
    "name":"SPringMVC终极开发666",
    "description":"这是一本好书"
}

在mysql中查看

③ 删除图书,发送delete请求
http://localhost:8080/books/13

查看mysql

④ 查询单个图书,发送get请求
http://localhost:8080/books/1

查看页面(Postman中的Pretty)

⑤ 查看所有图书,发送get请求
http://localhost:8080/books

查看页面(Postman中的Pretty)

5.添加事务管理
① 在SpringConfig中添加注解@EnableTransactionManagement
② JdbcConfig中添加事务管理

    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager ds = new DataSourceTransactionManager();
        ds.setDataSource(dataSource);
        return ds;
    }

③ BookService接口中添加注解@Transactional

4.1 表现层数据封装

设置统一数据返回结果类

public class Result{
    private Object data;
    private Integer code;
    private String msg;
}

注意事项:
Result类中的字段并不是固定的,可以根据需要自行增减
提供若干构造方法,方便操作

实现
设置统一数据返回类Result

package com.example.controller;

public class Result{
    private Object data;
    private Integer code;
    private String msg;

    public Result(Integer code,Object data, String msg) {
        this.data = data;
        this.code = code;
        this.msg = msg;
    }

    public Result(Integer code,Object data) {
        this.data = data;
        this.code = code;
    }

    public Result() {
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }
}

设置统一数据返回结果码

package com.example.controller;

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;
}

根据情况设计合理的Result
修改BookController

package com.example.controller;

import com.example.domain.Book;
import com.example.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RestController
@RequestMapping("/books")
public class BookController {
    @Autowired
    private BookService bookService;

    @PostMapping
    public Result save(@RequestBody Book book) {
        Boolean flag = bookService.save(book);
        return new Result(flag ? Code.SAVE_OK:Code.SAVE_ERR,flag);
    }

    @PutMapping
    public Result update(@RequestBody Book book) {
        Boolean flag = bookService.update(book);
        return new Result(flag ? Code.UPDATE_OK:Code.UPDATE_ERR,flag);
    }

    @DeleteMapping("/{id}")
    public Result delete(@PathVariable Integer id) {
        Boolean flag = bookService.delete(id);
        return new Result(flag ? Code.DELETE_OK:Code.DELETE_ERR,flag);
    }

    @GetMapping("/{id}")
    public Result getById(@PathVariable Integer id) {
        Book book = bookService.getById(id);
        Integer code = book != null ? Code.GET_OK: Code.GET_ERR;
        String msg = book != null ? "":"数据查询失败,请重试!";
        return new Result(code,book,msg);
    }

    @GetMapping
    public Result getAll() {
        List<Book> bookList = bookService.getAll();
        Integer code = bookList != null ? Code.GET_OK: Code.GET_ERR;
        String msg = bookList != null ? "":"数据查询失败,请重试!";
        return new Result(code,bookList,msg);
    }
}

用Postman工具测试各接口

4.2 异常处理器

程序开发过程中不可避免的会遇到异常现象

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

各个层级均出现异常,异常处理代码书写在哪一层——所有异常均抛出到表现层进行处理

表现层处理异常,每个方法中单独书写,代码书写量巨大且意义不强,如何解决——AOP思想

用到的注解
@RestControllerAdvice:为Rest风格开发的控制器类做增强
@ExceptionHandler:设置指定异常的处理方案,功能等同于控制器方法,出现异常后终止原始控制器执行,并转入当前方法执行

异常处理器:集中的、统一的处理项目中出现的异常

创建异常处理器ProjectExceptionAdvice

package com.example.controller;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class ProjectExceptionAdvice {
    @ExceptionHandler(Exception.class)
    public Result doException(Exception ex){
        return new Result(666,null,"出现异常");
    }
}

4.3 项目异常处理

  • 项目异常分类

    • 业务异常(BusinessException)
      • 规范的用户行为产生的异常
      • 不规范的用户行为操作产生的异常
    • 系统异常(SystemException)
      • 项目运行过程中预计且无法避免的异常
    • 其他异常(Exception)
      • 编程人员未预期到的异常
  • 项目异常处理方案

    • 业务异常(BusinessException)
      • 发送对应信息传递给用户,提醒规范操作
    • 系统异常(SystemException)
      • 发送固定信息传递给用户,安抚用户
      • 发送特定消息给运维人员,提醒维护
      • 记录日志
    • 其他异常(Exception)
      • 发送固定信息传递给用户,安抚用户
      • 发送特定消息给运维人员,提醒维护
      • 记录日志

实验
① 自定义项目系统级异常

package com.example.exception;

public class SystemException extends RuntimeException{
    private Integer 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 Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

② 自定义项目业务级异常

package com.example.exception;

public class BusinessException extends RuntimeException{
    private Integer 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;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }
}

③ 自动以哦异常编码(持续补充)

    public static final Integer SYSTEM_ERR = 50001;
    public static final Integer SYSTEM_TIMEOUT_ERR = 50002;
    public static final Integer BUSSINESS_ERR = 60002;
    public static final Integer SYSTEM_UNKNOW_ERR = 5999;

④ 模拟异常
修改BookServiceImpl

@Override
public Book getById(Integer id) {
    if(id == 1){
        throw new BusinessException(Code.BUSSINESS_ERR,"请不要使用你的技术,挑战我的耐性!");
    }
    //将可能出现的异常进行包装,转换成自定义异常
    try{
        int i = 1 / 0;
    }catch(ArithmeticException e){
        throw new SystemException(Code.SYSTEM_TIMEOUT_ERR,"服务器请求超时,请重试",e);
    }
    return bookDao.getById(id);
}

⑤ 拦截并处理异常

package com.example.controller;

import com.example.exception.BusinessException;
import com.example.exception.SystemException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

@RestControllerAdvice
public class ProjectExceptionAdvice {
    @ExceptionHandler(SystemException.class)
    public Result doSystemException(SystemException ex){
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,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){
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,ex对象发送给开发人员
        return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试");
    }
}

⑥ 测试异常
发送get请求:http://localhost:8080/books/1
发送get请求:http://localhost:8080/books/2

4.4 案例:SSM整合标准开发

在webapp包下导入SSM功能页面(链接:https://pan.baidu.com/s/15RVS1UO-l3BYcBPMOoGdaQ 提取码:4tn9)

添加配置(放行非SpringMvc的请求)
SpringMvcSupport

package com.example.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }
}

SpringMvcConfig

package com.example.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan({"com.example.controller","com.example.config"})
@EnableWebMvc
public class SpringMvcConfig {
}

1.列表功能
在books.html中个修改

//列表
getAll() {
    //发送ajax请求
    axios.get("/books").then((res)=>{
       this.dataList = res.data.data;
    });
},

2.添加功能
BookDao返回影响行数

package com.example.dao;

import com.example.domain.Book;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface BookDao {
    @Insert("insert into tbl_book values(null,#{type},#{name},#{description})")
    public int save(Book book);

    @Update("update tbl_book set type=#{type},name=#{name},description=#{description} where id = #{id}")
    public int update(Book book);

    @Delete("delete from tbl_book where id = #{id}")
    public int delete(Integer id);

    @Select("select * from tbl_book where id = #{id}")
    public Book getById(Integer id);

    @Select("select * from tbl_book")
    public List<Book> getAll();
}

BookServiceImpl返回是否正确添加删除更新

package com.example.service.impl;

import com.example.controller.Code;
import com.example.dao.BookDao;
import com.example.domain.Book;
import com.example.exception.BusinessException;
import com.example.exception.SystemException;
import com.example.service.BookService;
import org.omg.CORBA.CODESET_INCOMPATIBLE;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    private BookDao bookDao;

    @Override
    public boolean save(Book book) {
        return bookDao.save(book) > 0;
    }

    @Override
    public boolean update(Book book) {
        return bookDao.update(book) > 0;
    }

    @Override
    public boolean delete(Integer id) {
        return bookDao.delete(id) > 0;
    }

    @Override
    public Book getById(Integer id) {
        return bookDao.getById(id);
    }

    @Override
    public List<Book> getAll() {
        return bookDao.getAll();
    }
}

在books.html中增添添加功能

//弹出添加窗口
handleCreate() {
    this.dialogFormVisible = true;
    this.resetForm();
},

//重置表单
resetForm() {
    this.formData = {}
},

//添加
handleAdd () {
    //发送ajax请求
    axios.post("/books",this.formData).then((res)=>{
        //如果操作成功,关闭弹层,显示数据
        if(res.data.code == 20011){
            this.dialogFormVisible = false;
            this.$message.success("添加成功");

        }else if(res.data.code == 20010){
            this.$message.error("添加失败");
        }else{
            this.$message.error(res.data.msg);
        }
    }).finally(()=>{
        this.getAll();
    });
},

3.修改功能
在books.html中增添修改功能

//弹出编辑窗口
handleUpdate(row) {
    // console.log(row); //row.id 查询条件
    axios.get("/books/"+row.id).then((res)=>{
        if(res.data.code == 20041){
            //查询数据,根据id查询
            this.formData = res.data.data;
            //展示弹层,加载数据
            this.dialogFormVisible4Edit = true;

        }else{
            this.$message.error(res.data.msg);
        }
    });
},

//编辑
handleEdit() {
    //发送ajax请求
    axios.put("/books",this.formData).then((res)=>{
        //如果操作成功,关闭弹层,显示数据
        if(res.data.code == 20031){
            this.dialogFormVisible4Edit = false;
            this.$message.success("修改成功");

        }else if(res.data.code == 20030){
            this.$message.error("修改失败");
        }else{
            this.$message.error(res.data.msg);
        }
    }).finally(()=>{
        this.getAll();
    });
},

4.删除功能
在books.html中增添删除功能

// 删除
handleDelete(row) {
    //1.弹出提示框
    this.$confirm("是否确认删除?","提示",{
        type:'info'
    })
    .then(()=>{
        //2.做删除业务
        axios.delete("/books/"+row.id).then((res)=>{
            if(res.data.code == 20021){
                this.$message.success("删除成功");
            }else{
                this.$message.error("删除失败");
            }
        }).finally(()=>{
            this.getAll();
        });
    })
    .catch(()=>{
        //3.取消删除
        this.$message.info("取消删除操作");
    })
}

5.拦截器

5.1 拦截器概念

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

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

拦截器与过滤器区别
归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术
拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强

5.2 入门案例

1.制作拦截器功能类
2.配置拦截器的执行位置

① 声明拦截器的bean,并实现HandlerInterceptor接口(注意:扫描加载bean)

package com.example.controller.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}

② 定义配置类,继承WebMvcConfigurationSupport,实现addInterceptors方法(注意:扫描加载配置)

package com.example.config;

import com.example.controller.interceptor.ProjectInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Autowired
    private ProjectInterceptor projectInterceptor;
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    }
}

也可以使用简化配置
使用标准接口WebMvcConfigurer简化开发(注意:侵入式较强)

package com.example.config;

import com.example.controller.interceptor.ProjectInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@ComponentScan({"com.example.controller","com.example.config"})
@EnableWebMvc
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private ProjectInterceptor projectInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
    }
}标准接口

5.3 拦截器参数

  • 前置处理(preHandle)

  • 参数

    • request:请求对象
    • response:响应对象
    • handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装
  • 返回值

    • 返回值为false,被拦截的处理器将不执行
  • 后置处理(postHandle)

  • 参数

    • modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行调整
  • 完成后处理(afterCompletion)

  • 参数

    • ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理
5.4 拦截器链配置

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

拦截器链运行顺序
preHandle:与配置顺序想用,必定运行
postHandle:与配置顺序相反,可能不运行
afterCompletion:与配置顺序相反,可能不运行

实验
在创建一个拦截器

package com.example.controller.interceptor;

import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
public class ProjectInterceptor2 implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle222...");
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle222...");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion222...");
    }
}

添加多个拦截器

package com.example.config;

import com.example.controller.interceptor.ProjectInterceptor;
import com.example.controller.interceptor.ProjectInterceptor2;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Autowired
    private ProjectInterceptor projectInterceptor;
    @Autowired
    private ProjectInterceptor2 projectInterceptor2;
    @Override
    protected void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/pages/**").addResourceLocations("/pages/");
        registry.addResourceHandler("/css/**").addResourceLocations("/css/");
        registry.addResourceHandler("/js/**").addResourceLocations("/js/");
        registry.addResourceHandler("/plugins/**").addResourceLocations("/plugins/");
    }

    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(projectInterceptor).addPathPatterns("/books","/books/*");
        registry.addInterceptor(projectInterceptor2).addPathPatterns("/books","/books/*");
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值