SpringMVC整合SSM、RestFull、异常处理器和拦截器讲解



一、RestFul


REST这个词,是Roy Thomas Fielding在他2000年的博士论文中提出的。Fielding是一个非常重要的人,他是HTTP协议(1.0版和1.1版)的主要设计者、Apache服务器软件的作者之一、Apache基金会的第一任主席。所以,他的这篇论文一经发表,就引起了关注,并且立即对互联网开发产生了深远的影响。


他这样介绍论文的写作目的:

“本文研究计算机科学两大前沿----软件和网络----的交叉点。长期以来,软件研究主要关注软件设计的分类、设计方法的演化,很少客观地评估不同的设计选择对系统行为的影响。而相反地,网络研究主要关注系统之间通信行为的细节、如何改进特定通信机制的表现,常常忽视了一个事实,那就是改变应用程序的互动风格比改变互动协议,对整体表现有更大的影响。我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。”

RESTful 是一种设计风格。它不是一种标准,也不是一种软件,而是一种思想。


特点:


每一个URI(请求地址)代表1种资源;

  • 以前我们认为url地址它是一个动作: 增删改查的动作

    • localhost:8080/项目名/findAllUser

    • lolcahost:8080/项目名/deleteUser?id=3

  • rest设计风格认为地址是一种资源,体现的只有名词,而没有动词。

    • localhost:8080/项目名/user/3 : 删除id为3

    • localhost:8080/项目名/user/3 : 查询id为3

客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;、

  • 由于地址不能体现出来动作,包含的都是名词,没有动词,那么服务端如何区分出来客户端想要执行的是什么操作呢?

  • 采用请求方式来区分 localhost:8080/项目名/user/3

    • 新增 ----- post请求

    • 查询 ----- get请求

    • 删除 ----- delete请求

    • 更新 ----- put请求

客户端与服务端之间的交互在请求之间是无状态的,从客户端到服务端的每个请求都必须包含理解请求所必需的信息


接口结构


如何设计接口


域名

应该尽量将API部署在专用域名之下。 第三方SDK来说,比较有用。

http://api.example.com 或者 http://www.example.com/api/


版本

将API的版本号放在url中:http://www.example.com/api/v1.0


路径

在RESTful架构中,每个地址代表一种资源(resource),所以地址中不能有动词,只能有名词,而且所用的名词往往与数据库的表名对应。


具体操作

对于资源的具体操作类型,由HTTP动词表示。常用的HTTP动词有下面四个(括号里是对应的SQL命令)


GET(SELECT):从服务器取出资源(一项或多项)。
POST(CREATE):在服务器新建一个资源。
PUT(UPDATE):在服务器更新资源(客户端提供改变后的完整资源)。
DELETE(DELETE):从服务器删除资源。

还有三个不常用的HTTP动词。
 
PATCH(UPDATE):在服务器更新资源(客户端提供改变的属性)。
HEAD:获取资源的元数据。
OPTIONS:获取信息,关于资源的哪些属性是客户端可以改变的

示例说明


GET /user:列出所有用户

POST /user:新建一个用户

GET /user/{id}:获取某个指定用户的信息

PUT /user:更新某个指定用户的信息(提供该用户的全部信息)

DELETE /user/{id}:删除某个用户


  • 原来的方式

    http://127.0.0.1/user/find?id=3 GET方法,根据用户id获取数据
    http://127.0.0.1/user/update POST方法,用户修改
    http://127.0.0.1/user/add POST方法,用户新增
    http://127.0.0.1/user/deleteUser?id=3 GET/POST方法,用户根据id删除

  • RestFul方式

    http://127.0.0.1/user/{id} GET方法,根据用户id获取数据
    http://127.0.0.1/user/{id} DELETE方法,用户根据id删除

    http://127.0.0.1/user/ GET 方法 查询所有的用户

    http://127.0.0.1/user/ PUT方法,用户修改
    http://127.0.0.1/user/ POST方法,用户新增


小结

  1. RESTful 是一种设计风格 , 可以用,也可以不用!

  2. 每一个URI代表1种资源,地址里面只能出现名词,不能出现动词。

  3. 客户端使用GET、POST、PUT、DELETE4个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;


基本实现


在postman工具里面测试


User

package com.execise.bean;

import lombok.Data;

@Data
public class User {
    private String username;
    private String password;
}

UserController

package com.execise.controller;

import com.execise.bean.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

/*
    这是用来处理一切与用户有关请求的controller
 */
@Controller
public class UserController {

    /*
        新增:
            以前:  localhost:82/addUser        GET|POST
            restFul :  localhost:82/user       POST
     */
//    @RequestMapping(value = "/user", method = RequestMethod.POST)

    @ResponseBody
    @PostMapping("/user")
    public String add(User user ){
        System.out.println("add..." + user);
        return "add success...";
    }


    /*
        删除:
            以前: localhost:82/deleteUser?id=1         GET
            restFul: localhost:82/user/1               DELETE
     */
    @ResponseBody
    @DeleteMapping("/user/{id}")
    public String delete(@PathVariable int id){
        System.out.println("delete...id=" + id);
        return "delete success...";
    }


    /*
        修改:
             以前: localhost:82/updateUser            GET|POST
            restFul: localhost:82/user                 PUT

        更新的操作比较特殊一些:
            springmvc不会从put请求里面拿请求体的数据出来,赋值给方法参数。
            所以看到的都是null!
     */
    @ResponseBody
    @PutMapping("/user")
    public String update(User user){
        System.out.println("update..." + user);
        return "update success...";
    }


    /*
        根据id来查询用户:
              以前: localhost:82/findUserById?id=1            GET
            restFul: localhost:82/user/1                       GET
     */
    @ResponseBody
    @GetMapping("/user/{id}")
    public String findById(@PathVariable int id){
        System.out.println("findById..." + id);
        return "findById success...";
    }


    /*
        查询所有:
             以前: localhost:82/findAll                        GET
            restFul: localhost:82/user                         GET
     */
    @ResponseBody
    @GetMapping("/user")
    public String findAll(){
        System.out.println("findAll..." );
        return "findAll success...";
    }
}


小结

  1. 创建Maven web工程

  2. 创建Pojo

  3. 创建Controller ,定义增删改查方法

  4. 分别使用 get | post | put | delete 来指定方法请求方式

二、表现层数据封装


1 表现层响应数据的问题


问题:我们表现层增删改方法返回true或者false表示是否成功,getById()方法返回一个json对象,getAll()方法返回一个json对象数组,这里就出现了三种格式的响应结果,极其不利于前端解析。


在这里插入图片描述


解决:我们需要统一响应结果的格式


2 定义Result类封装响应结果


Result类封装响应结果

public class Result {
    //描述统一格式中的数据
    private Object data;
    //描述统一格式中的编码,用于区分操作,可以简化配置0或1表示成功失败
    private Integer code;
    //描述统一格式中的消息,可选属性
    private String msg;

    public Result() {
    }
    public Result(Integer code,Object data) {
        this.data = data;
        this.code = code;
    }
    public Result(Integer code, Object data, String msg) {
        this.data = data;
        this.code = code;
        this.msg = msg;
    }
     //同学们自己添加getter、setter、toString()方法
}

注意事项:

Result类中的字段并不是固定的,可以根据需要自行增减


Code类封装响应码

//状态码
public class Code {
    public static final Integer SAVE_OK = 20011;
    public static final Integer DELETE_OK = 20021;
    public static final Integer UPDATE_OK = 20031;
    public static final Integer GET_OK = 20041;

    public static final Integer SAVE_ERR = 20010;
    public static final Integer DELETE_ERR = 20020;
    public static final Integer UPDATE_ERR = 20030;
    public static final Integer GET_ERR = 20040;
}

注意事项:

Code类的常量设计也不是固定的,可以根据需要自行增减,例如将查询再进行细分为GET_OK,GET_ALL_OK,GET_PAGE_OK


3 表现层数据封装返回Result对象


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

三、异常处理器


程序开发过程中不可避免的会遇到异常现象,我们不能让用户看到这样的页面数据


在这里插入图片描述


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

  • 框架内部抛出的异常:因使用不合规导致

  • 数据层抛出的异常:因外部服务器故障导致(例如:服务器访问超时)

  • 业务层抛出的异常:因业务逻辑书写错误导致(例如:遍历业务书写操作,导致索引异常等)

  • 表现层抛出的异常:因数据收集、校验等规则导致(例如:不匹配的数据类型间导致异常)

  • 工具类抛出的异常:因工具类书写不严谨不够健壮导致(例如:必要释放的连接长期未释放等)


编写异常处理器

@RestControllerAdvice  //用于标识当前类为REST风格对应的异常处理器
public class ProjectExceptionAdvice {

    //统一处理所有的Exception异常
    @ExceptionHandler(Exception.class)
    public Result doOtherException(Exception ex){
        return new Result(666,null);
    }
}

使用异常处理器之后的效果


在这里插入图片描述


@RestControllerAdvice注解介绍

  • 名称:@RestControllerAdvice

  • 类型:类注解

  • 位置:Rest风格开发的控制器增强类定义上方

  • 作用:为Rest风格开发的控制器类做增强

  • 说明:此注解自带@ResponseBody注解与@Component注解,具备对应的功能


@ExceptionHandler注解介绍

  • 名称:@ExceptionHandler

  • 类型:方法注解

  • 位置:专用于异常处理的控制器方法上方

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

  • 说明:此类方法可以根据处理的异常不同,制作多个方法分别处理对应的异常


项目异常处理方案


项目当前异常的分类以及对应类型异常该如何处理?


1 项目异常分类


业务异常(BusinessException)

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

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

系统异常(SystemException)

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

其他异常(Exception)

  • 编程人员未预期到的异常

2 项目异常处理方案


业务异常(BusinessException)

  • 发送对应消息传递给用户,提醒规范操作

系统异常(SystemException)

  • 发送固定消息传递给用户,安抚用户

发送特定消息给运维人员,提醒维护

  • 记录日志

其他异常(Exception)

  • 发送固定消息传递给用户,安抚用户

  • 发送特定消息给编程人员,提醒维护(纳入预期范围内)

  • 记录日志


3 项目异常处理代码实现


根据异常分类自定义异常类


自定义项目系统级异常

//自定义异常处理器,用于封装异常信息,对异常进行分类
public class SystemException extends RuntimeException{
    private Integer code;

    public Integer getCode() {
        return code;
    }

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

    public SystemException(Integer code, String message) {
        super(message);
        this.code = code;
    }

    public SystemException(Integer code, String message, Throwable cause) {
        super(message, cause);
        this.code = code;
    }
}

自定义项目业务级异常

//自定义异常处理器,用于封装异常信息,对异常进行分类
public class BusinessException extends RuntimeException{
    private Integer code;

    public Integer getCode() {
        return code;
    }

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

    public BusinessException(Integer code, String message) {
        super(message);
        this.code = code;
    }

    public BusinessException(Integer code,String message,Throwable cause) {
        super(message, cause);
        this.code = code;
    }
}

自定义异常编码(持续补充)

public class Code {

	//之前其他状态码省略没写,以下是新补充的状态码,可以根据需要自己补充
    
    public static final Integer SYSTEM_ERR = 50001;
    public static final Integer SYSTEM_TIMEOUT_ERR = 50002;
    public static final Integer SYSTEM_UNKNOW_ERR = 59999;
    public static final Integer BUSINESS_ERR = 60002;
    
}

触发自定义异常

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

	//在getById演示触发异常,其他方法省略没有写进来
    public Book getById(Integer id) {
        //模拟业务异常,包装成自定义异常
        if(id <0){
            throw new BusinessException(Code.BUSINESS_ERR,"请不要使用你的技术挑战我的耐性!");
        }
    }
}

在异常通知类中拦截并处理异常

@RestControllerAdvice //用于标识当前类为REST风格对应的异常处理器
public class ProjectExceptionAdvice {
    //@ExceptionHandler用于设置当前处理器类对应的异常类型
    @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());
    }

    //除了自定义的异常处理器,保留对Exception类型的异常处理,用于处理非预期的异常
    @ExceptionHandler(Exception.class)
    public Result doOtherException(Exception ex){
        //记录日志
        //发送消息给运维
        //发送邮件给开发人员,ex对象发送给开发人员
        return new Result(Code.SYSTEM_UNKNOW_ERR,null,"系统繁忙,请稍后再试!");
    }
}

测试:在postman中发送请求访问getById方法,传递参数-1,得到以下结果:


在这里插入图片描述


四、拦截器


问题1:拦截器拦截的对象是谁?

问题2:拦截器和过滤器有什么区别?


拦截器概念和作用


在这里插入图片描述


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

作用:

  1. 在指定的方法调用前后执行预先设定的代码

  2. 阻止原始方法的执行

  3. 总结:增强

核心原理:AOP思想


拦截器和过滤器的区别

归属不同:Filter属于Servlet技术,Interceptor属于SpringMVC技术

拦截内容不同:Filter对所有访问进行增强,Interceptor仅针对SpringMVC的访问进行增强


在这里插入图片描述


拦截器代码实现


【第一步】定义拦截器

做法:定义一个类,实现HandlerInterceptor接口即可


@Component //注意当前类必须受Spring容器控制
//定义拦截器类,实现HandlerInterceptor接口
public class ProjectInterceptor implements HandlerInterceptor {
    @Override
    //原始方法调用前执行的内容
    //返回值类型可以拦截控制的执行,true放行,false终止
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle..."+contentType);
        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...");
    }
}

【第二步】配置加载拦截器

@Configuration
public class SpringMvcSupport extends WebMvcConfigurationSupport {
    @Autowired
    private ProjectInterceptor projectInterceptor;

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

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

@Configuration
@ComponentScan({"com.execise.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private ProjectInterceptor projectInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置多拦截器
        registry.addInterceptor(projectInterceptor)
            .addPathPatterns("/books","/books/*");
    }
}

拦截器流程分析


在这里插入图片描述


拦截器参数


postHandle()和afterCompletion()方法都是处理器方法执行之后执行,有什么区别?


前置处理

//原始方法调用前执行的内容
//返回值类型可以拦截控制的执行,true放行,false终止
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("preHandle..."+contentType);
    return true;
}

参数

  1. request:请求对象

  2. response:响应对象

  3. handler:被调用的处理器对象,本质上是一个方法对象,对反射技术中的Method对象进行了再包装

返回值为false,被拦截的处理器将不执行。


后置处理

//原始方法调用后执行的内容
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    System.out.println("postHandle...");
}

参数

modelAndView:如果处理器执行完成具有返回结果,可以读取到对应数据与页面信息,并进行跳转


注意:如果处理器方法出现异常了,该方法不会执行


完成后处理

//原始方法调用完成后执行的内容
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
    System.out.println("afterCompletion...");
}

参数

ex:如果处理器执行过程中出现异常对象,可以针对异常情况进行单独处理


注意:无论处理器方法内部是否出现异常,该方法都会执行。


拦截器链配置


什么是拦截器链?


多个拦截器配置


定义第二个拦截器

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

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

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

配置第二个拦截器

@Configuration
@ComponentScan({"com.execise.controller"})
@EnableWebMvc
//实现WebMvcConfigurer接口可以简化开发,但具有一定的侵入性
public class SpringMvcConfig implements WebMvcConfigurer {
    @Autowired
    private ProjectInterceptor projectInterceptor;
    @Autowired
    private ProjectInterceptor2 projectInterceptor2;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        //配置多拦截器
        registry.addInterceptor(projectInterceptor)
            .addPathPatterns("/books","/books/*");
        registry.addInterceptor(projectInterceptor2)
            .addPathPatterns("/books","/books/*");
    }
}

多个连接器工作流程分析

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

  • 拦截器链的运行顺序参照拦截器添加顺序为准

  • 当拦截器中出现对原始处理器的拦截,后面的拦截器均终止运行

  • 当拦截器运行中断,仅运行配置在前面的拦截器的afterCompletion操作


在这里插入图片描述


五、整合SSM


SSM整合流程

  1. 创建数据库,创建Maven工程【javaweb】,添加依赖

  2. 添加页面

  3. SSM整合

    • Spring

      • applicationContext.xml
    • MyBatis

      • db.properties
    • SpringMVC

      • springmvc.xml

      • web.xml

  4. 功能模块

    • 表与实体类

    • 工具类(Result、Code)

    • dao(接口+自动代理)

    • service(接口+实现类)

      • 业务层接口测试(整合JUnit)
    • controller

      • 表现层接口测试(PostMan)

SSM整合配置


创建工程,添加依赖和插件


  <!-- 设置JDK编译版本1.8 -->
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
  </properties>

  <!--依赖管理-->
  <dependencies>
    <!--junit单元测试-->
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    <!--Spring+SpringMVC核心依赖-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.1.2.RELEASE</version>
    </dependency>
    <!--AOP-->
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.4</version>
    </dependency>

    <!--整合Mybatis-->
    <!--1. 数据库依赖-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
    </dependency>
    <!--2. 连接池依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.23</version>
    </dependency>
    <!--3. mybatis本身的依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.6</version>
    </dependency>
    <!--4. 整合mybatis和spring的依赖-->
    <!--MyBatis提供的和Spring进行整合的jar包-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>2.0.6</version>
    </dependency>
    <!--spring对jdbc封装的jar包也要导入进来,否则mybatis无法整合-->
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-jdbc</artifactId>
      <version>5.1.2.RELEASE</version>
    </dependency>

    <!--5. 日志依赖-->
    <!-- 添加slf4j日志api -->
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>1.7.20</version>
    </dependency>
    <!-- 添加logback-classic依赖 -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-classic</artifactId>
      <version>1.2.3</version>
    </dependency>
    <!-- 添加logback-core依赖 -->
    <dependency>
      <groupId>ch.qos.logback</groupId>
      <artifactId>logback-core</artifactId>
      <version>1.2.3</version>
    </dependency>
    <!--jackson-->
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.6</version>
    </dependency>
    <!--Lombok-->
    <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <version>1.18.18</version>
      <scope>provided</scope>
    </dependency>
    <!--Servlet-->
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
  </dependencies>

  <build>
      <plugins>
        <plugin>
          <groupId>org.apache.tomcat.maven</groupId>
          <artifactId>tomcat7-maven-plugin</artifactId>
          <version>2.2</version>
          <configuration>
            <!-- 指定端口 -->
            <port>80</port>
            <!-- 请求路径 -->
            <path>/</path>
          </configuration>
        </plugin>
      </plugins>
  </build>

Spring整合Mybatis


创建数据库和表

-- 创建ssm_db数据库
CREATE DATABASE IF NOT EXISTS day35_ssm CHARACTER SET utf8;

-- 使用ssm_db数据库
USE day35_ssm;

-- 创建tbl_book表
CREATE TABLE tbl_book(
    id INT PRIMARY KEY AUTO_INCREMENT, -- 图书编号
    `type` VARCHAR(100), -- 图书类型
    `name` VARCHAR(100), -- 图书名称
    description VARCHAR(100) -- 图书描述
);
-- 添加初始化数据
INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring实战 第5版','Spring入门经典教材,深入理解Spring原理技术内幕');
INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring 5核心原理与30个类手写实战','十年沉淀之作,手写Spring精华思想');
INSERT INTO tbl_book VALUES(NULL,'计算机理论','Spring 5设计模式','深入Spring源码剖析,Spring源码蕴含的10大设计模式');
INSERT INTO tbl_book VALUES(NULL,'市场营销','直播就该这么做:主播高效沟通实战指南','李子柒、李佳琦、薇娅成长为网红的秘密都在书中');
INSERT INTO tbl_book VALUES(NULL,'市场营销','直播销讲实战一本通','和秋叶一起学系列网络营销书籍');
INSERT INTO tbl_book VALUES(NULL,'市场营销','直播带货:淘宝、天猫直播从新手到高手','一本教你如何玩转直播的书,10堂课轻松实现带货月入3W+');

db.properties属性文件


db.driverClass=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/day35_ssm
db.username=root
db.password=root

Spring整合MyBatis

定义 spring-mybatis.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--0. 扫描包-->
   <!-- <context:component-scan base-package="com.execise"/>-->

    <!--整合Spring和mybatis-->
    <!--1. 数据库连接池-->
    <context:property-placeholder location="classpath:db.properties"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${db.driverClass}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
    </bean>

    <!--2. SqlSessionFactoryBean-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>

        <property name="typeAliasesPackage" value="com.execise.bean"/>
    </bean>

    <!--3. MapperScannerConfigurer-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.execise.dao"/>
    </bean>

    <!--4. 事务的配置-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>

    <!--5. 打开事务的注解开关 :: @Transactional-->
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

Spring整合SpringMVC

定义springmvc.xml


<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--1. 扫描包-->
    <context:component-scan base-package="com.execise"/>

    <!--2. 注解驱动-->
    <mvc:annotation-driven/>

    <!--3. 视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!--4. 静态资源处理-->
    <mvc:default-servlet-handler/>

    <!--5. 导入spring和mybatis整合的配置文件-->
    <import resource="spring-mybatis.xml"/>
</beans>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	version="2.5">

	<!--配置前端控制器 DispatcherServlet-->
		<servlet>
			<servlet-name>dispatcher</servlet-name>
			<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

			<init-param>
				<param-name>contextConfigLocation</param-name>
				<param-value>classpath:springmvc.xml</param-value>
			</init-param>

			<load-on-startup>1</load-on-startup>
		</servlet>

		<servlet-mapping>
			<servlet-name>dispatcher</servlet-name>
			<url-pattern>/</url-pattern>
		</servlet-mapping>
</web-app>

功能模块开发


查询所有


页面

//添加
handleAdd() {

    //1. 提交请求
    axios.post("/books", this.formData).then(response=>{
        console.log("添加的结果:");
        console.log(response);

        //2. 判定
        this.dialogFormVisible = false;
        if(response.data.code  === 20001){
            //提示
            this.$message.success(response.data.msg);

            //添加成功了,需要重新载入数据
            this.getAll();
        }else{
            this.$message.error(response.data.msg);
        }
    })
},

controller

package com.execise.controller;

/*
    所有关于书籍的增删改查的操作,都交给这个类处理
 */

import com.execise.bean.Book;
import com.execise.bean.Result;
import com.execise.constant.Code;
import com.execise.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RequestMapping("/books")
@RestController
public class BookController {

    @Autowired
    private BookService bs;

    /*
        查询所有
     */
    @GetMapping
    public Result findAll(){
        Result result = null;

        try {
            //1. 调用service
            List<Book> list = bs.findAll();

            //成功
            result = new Result(Code.GET_ALL_BOOK_SUCCESS, list , "查询所有书籍成功");
        } catch (Exception e) {
            e.printStackTrace();

            //失败
            result = new Result(Code.GET_ALL_BOOK_FAILED,  "查询所有书籍失败");
        }

        return result;
    }
}

service

package com.execise.service;

import com.execise.bean.Book;

import java.util.List;

public interface BookService {

    List<Book> findAll();
}
package com.execise.service.impl;

import com.execise.bean.Book;
import com.execise.dao.BookDao;
import com.execise.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao dao;

    @Override
    public List<Book> findAll() {
        return dao.findAll();
    }
}

dao

package com.execise.dao;

import com.execise.bean.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 {

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

添加图书


页面

 //添加
handleAdd() {

    //1. 提交请求
    axios.post("/books", this.formData).then(response=>{
        console.log("添加的结果:");
        console.log(response);

        //2. 判定
        this.dialogFormVisible = false;
        if(response.data.code  === 20001){
            //提示
            this.$message.success(response.data.msg);

            //添加成功了,需要重新载入数据
            this.getAll();
        }else{
            this.$message.error(response.data.msg);
        }
    })
},	

controller

@RequestMapping("/books")
@RestController
public class BookController {

    @PostMapping
    public Result add(@RequestBody Book book){

        //1. 调用service干活
        int row = bs.add(book);

        //2. 响应
        Result result = null;
        if(row > 0 ){
            result = new Result(Code.ADD_SUCCESS , "添加书籍成功!");
        }else{
            result = new Result(Code.ADD_FAILED , "添加书籍失败!");
        }
        return result;

    }
}

service

package com.execise.service;

import com.execise.bean.Book;

import java.util.List;

public interface BookService {

    int add(Book book);

}
package com.execise.service.impl;

import com.execise.bean.Book;
import com.execise.dao.BookDao;
import com.execise.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
public class BookServiceImpl implements BookService {

    @Autowired
    private BookDao dao;
    
    @Override
    public int add(Book book) {
        return dao.add(book);
    }


dao

package com.execise.dao;

import com.execise.bean.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} )")
    int add(Book book);
}

更新图书


页面

//弹出编辑窗口
handleUpdate(row) {

    //1. 弹出编辑的对话框
    this.dialogFormVisible4Edit = true;

    //2. 回显数据
    //this.formData = row;
    this.formData = JSON.parse(JSON.stringify(row));

},

//编辑
handleEdit() {
    //1. 发起请求
    axios.put("/books" , this.formData).then(response=>{
        console.log("更新结果:");
        console.log(response);

        //不管成功或者失败,都要让对话框消失
        this.dialogFormVisible4Edit = false;

        if(response.data.code === 20002){
            this.$message.success(response.data.msg);
            //重新载入数据
            this.getAll();
        }else{
            this.$message.error(response.data.msg);
        }
    });

},

controller

package com.execise.controller;

/*
    所有关于书籍的增删改查的操作,都交给这个类处理
 */

import com.execise.bean.Book;
import com.execise.bean.Result;
import com.execise.constant.Code;
import com.execise.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RequestMapping("/books")
@RestController
public class BookController {
    @PutMapping
    public Result update(@RequestBody Book book){
        System.out.println("book = " + book);

        //1. 交代service干活
        int row = bs.update(book);

        //2. 响应
        Result result = null;
        if(row > 0){
            result = new Result(Code.UPDATE_SUCCESS , "更新图书成功" );
        }else{
            result = new Result(Code.UPDATE_FAILED , "更新图书失败" );
        }
        return result;
    }
}


service

package com.execise.service;

import com.execise.bean.Book;

import java.util.List;

public interface BookService {

    int update(Book book);
}
@Service
@Transactional
public class BookServiceImpl implements BookService {  
	@Override
    public int update(Book book) {
        return dao.update(book);
    }
}


dao

package com.execise.dao;

import com.execise.bean.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 {

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


删除图书


页面

// 删除
handleDelete(row) {

    this.$confirm('确定删除该图书吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
    }).then(() => {

        //点击确定就来到这里了。
        //axios.get("请求地址?id="+row.id)
        axios.delete("/books/"+row.id).then(response=>{
            console.log("删除结果:");
            console.log(response);

            if(response.data.code === 20003){
                this.$message.success(response.data.msg);
                this.getAll();
            }else{
                this.$message.error(response.data.msg);
            }
        });

    }).catch(() => {
        this.$message({
            type: 'info',
            message: '已取消删除'
        });
    });
}

controller

package com.execise.controller;

/*
    所有关于书籍的增删改查的操作,都交给这个类处理
 */

import com.execise.bean.Book;
import com.execise.bean.Result;
import com.execise.constant.Code;
import com.execise.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;

import java.util.List;

@RequestMapping("/books")
@RestController
public class BookController {
    @DeleteMapping("/{id}")
    public Result delete(@PathVariable int id){

        //1. 调用service
        int row = bs.delete(id);

        //2. 响应
        Result result = null;
        if(row > 0){
            result = new Result(Code.DELETE_SUCCESS , "删除图书成功" );
        }else{
            result = new Result(Code.DELETE_FAILED , "删除图书失败" );
        }
        return result;
    }
}


service

package com.execise.service;

import com.execise.bean.Book;

import java.util.List;

public interface BookService {
    int delete(int id );
}
@Service
@Transactional
public class BookServiceImpl implements BookService {
   	@Override
    public int delete(int id) {
        return dao.delete(id);
    }
}


dao

 package com.execise.dao;

import com.execise.bean.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 {

   @Delete("delete from tbl_book where id = #{id}")
    int delete(int id);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

请叫我阿杰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值