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整合流程
- 创建工程
- SSM整合
- Spring
- SpringConfig
- MyBatis
- MybatisConfig
- JdbcConfig
- jdbc.properties
- SpringMVC
- ServletCofig
- SpringMvcConfig
- Spring
- 功能模块
- 表与实体类
- 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)
-
项目异常处理方案
- 业务异常(BusinessException)
- 发送对应信息传递给用户,提醒规范操作
- 系统异常(SystemException)
- 发送固定信息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 其他异常(Exception)
- 发送固定信息传递给用户,安抚用户
- 发送特定消息给运维人员,提醒维护
- 记录日志
- 业务异常(BusinessException)
实验
① 自定义项目系统级异常
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/*");
}
}