zoukankan      html  css  js  c++  java
  • SpringBoot异常处理五种方式、Junit单元测试、热部署

    首先说明一下,这里使用的是Springboot2.2.6.RELEASE版本,由于Springboot迭代很快,所以要注意版本问题。

    1、SpringBoot中异常处理方式。SpringBoot中对于异常处理提供了五种处理方式。

    1.1、第一种方式,自定义错误页面。

      SpringBoot默认的处理异常的机制:SpringBoot 默认的已经提供了一套处理异常的机制。一旦程序中出现了异常 SpringBoot 会像/error 的 url 发送请求。在springBoot 中提供了一个叫BasicExceptionController来处理/error请求,然后跳转到默认显示异常的页面来展示异常信息。

    上面的界面是Springboot提供的默认错误界面,我们可以自己修改这恶鬼错误的界面。如果我们需要将所有的异常同一跳转到自定义的错误页面,需要再src/main/resources/templates目录下创建error.html页面。注意:错误界面的名称必须叫error。

    缺点:自定义错误界面处理异常,异常信息颗粒度比较粗,不符合异常处理的原则。异常处理原则,对关心异常在一个界面进行展示,对不关心的异常可以统一跳转到一个界面进行展示。

     1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
     2         "http://www.w3.org/TR/html4/loose.dtd">
     3 <html xmlns:th="http://www.w3.org/1999/xhtml">
     4 <head>
     5     <title>错误提示页面</title>
     6 </head>
     7 <body>
     8 
     9 出错了,请与管理员联系......
    10 <!--<span th:text="${exception}"></span>-->
    11 
    12 </body>
    13 </html>

    1.2、第二种方式,@ExceptionHandle 注解处理异常。

     1 package com.bie.springboothello.controller;
     2 
     3 import org.springframework.stereotype.Controller;
     4 import org.springframework.ui.Model;
     5 import org.springframework.web.bind.annotation.ExceptionHandler;
     6 import org.springframework.web.bind.annotation.RequestMapping;
     7 import org.springframework.web.bind.annotation.ResponseBody;
     8 import org.springframework.web.servlet.ModelAndView;
     9 
    10 /**
    11  *
    12  */
    13 @Controller
    14 public class DemoController {
    15 
    16     @RequestMapping(value = "/hello")
    17     @ResponseBody
    18     public String showUser(Model model) {
    19         String str = null;
    20         str.length();
    21         return "hello";
    22     }
    23 
    24     /**
    25      * 该方法需要返回一个ModelAndView,目的是可以让我们封装异常信息以及试图的指定参数Exception e,
    26      * 会将产生异常对象注入到方法中。
    27      * <p>
    28      * <p>
    29      * 该方法可以处理ArithmeticException算术异常,程序中抛出算术异常,该方法就可以进行捕获。
    30      * 根据方法中的定义做什么操作,会将异常对象Exception注入进来,所以需要Exception参数。
    31      *
    32      * @param e
    33      * @return
    34      * @ExceptionHandler该注解的value值是可以处理那个异常的异常类型。
    35      */
    36     @ExceptionHandler(value = {java.lang.ArithmeticException.class})
    37     public ModelAndView arithmeticExceptionHandler(Exception e) {
    38         ModelAndView modelAndView = new ModelAndView();
    39         modelAndView.addObject("error", e.toString());
    40         modelAndView.setViewName("error1");
    41         return modelAndView;
    42     }
    43 
    44 
    45     /**
    46      * java.lang.NullPointerException
    47      * <p>
    48      * <p>
    49      * 该方法需要返回一个 ModelAndView,目的是可以让我们封装异常信息以及视图的指定参数Exception e:
    50      * 会将产生异常对象注入到方法中
    51      *
    52      * @param e
    53      * @return
    54      */
    55     @ExceptionHandler(value = {java.lang.NullPointerException.class})
    56     public ModelAndView nullPointerExceptionHandler(Exception e) {
    57         ModelAndView mv = new ModelAndView();
    58         mv.addObject("error", e.toString());
    59         mv.setViewName("error2");
    60         return mv;
    61     }
    62 
    63 }

    可以根据不同的错误来定义不同的错误提示界面,如下创建了error1.html、error2.html。

     1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
     2         "http://www.w3.org/TR/html4/loose.dtd">
     3 <html xmlns:th="http://www.w3.org/1999/xhtml">
     4 <head>
     5     <title>错误提示页面</title>
     6 </head>
     7 <body>
     8 
     9 java.lang.ArithmeticException出错了,请与管理员联系......
    10 <span th:text="${error}"></span>
    11 
    12 </body>
    13 </html>
     1 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
     2         "http://www.w3.org/TR/html4/loose.dtd">
     3 <html xmlns:th="http://www.w3.org/1999/xhtml">
     4 <head>
     5     <title>错误提示页面</title>
     6 </head>
     7 <body>
     8 
     9 java.lang.NullPointerException出错了,请与管理员联系......
    10 <span th:text="${error}"></span>
    11 
    12 </body>
    13 </html>

    缺点,如果该Controller需要处理的异常比较多,就会给代码造成了冗余现象。该方法只能对该Controller的异常进行处理,不可以跨Controller进行异常处理。

    1.3、第三种方式,@ControllerAdvice+@ExceptionHandler 注解处理异常。

     1 package com.bie.springboothello.controller;
     2 
     3 import org.springframework.web.bind.annotation.ControllerAdvice;
     4 import org.springframework.web.bind.annotation.ExceptionHandler;
     5 import org.springframework.web.servlet.ModelAndView;
     6 
     7 /**
     8  * 全局异常处理类。
     9  * 需要创建一个能够处理异常的全局异常类。在该类上需要添加@ControllerAdvice注解
    10  */
    11 @ControllerAdvice // 该注解可以实现全局异常处理
    12 public class GlobalException {
    13 
    14 
    15     /**
    16      * java.lang.ArithmeticException。
    17      * <p>
    18      * 该方法需要返回一个 ModelAndView,目的是可以让我们封装异常信息以及视图的指定参数
    19      * Exception e:会将产生异常对象注入到方法中。
    20      *
    21      * @param e
    22      * @return
    23      */
    24     @ExceptionHandler(value = {java.lang.ArithmeticException.class})
    25     public ModelAndView arithmeticExceptionHandler(Exception e) {
    26         ModelAndView mv = new ModelAndView();
    27         mv.addObject("error", e.toString());
    28         mv.setViewName("error1");
    29         return mv;
    30     }
    31 
    32 
    33     /**
    34      * java.lang.NullPointerException。
    35      * <p>
    36      * 该方法需要返回一个 ModelAndView:目的是可以让我们封装异常信息以及视图的指定参数
    37      * Exception e:会将产生异常对象注入到方法中。
    38      *
    39      * @param e
    40      * @return
    41      */
    42     @ExceptionHandler(value = {java.lang.NullPointerException.class})
    43     public ModelAndView nullPointerExceptionHandler(Exception e) {
    44         ModelAndView mv = new ModelAndView();
    45         mv.addObject("error", e.toString());
    46         mv.setViewName("error2");
    47         return mv;
    48     }
    49 
    50 }

    可以根据不同的错误来定义不同的错误提示界面,如下创建了error1.html、error2.html。这里直接使用了上面创建的错误界面error1.htm、error2.html。

    1.4、第四种方式,配置 SimpleMappingExceptionResolver 处理异常。该处理方式是对第三种处理异常的简化。

     1 package com.bie.springboothello.controller;
     2 
     3 
     4 import org.springframework.context.annotation.Bean;
     5 import org.springframework.context.annotation.Configuration;
     6 import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
     7 
     8 import java.util.Properties;
     9 
    10 /**
    11  * 通过SimpleMappingExceptionResolver做全局异常处理
    12  */
    13 @Configuration
    14 public class GlobalException {
    15 
    16     @Bean
    17     public SimpleMappingExceptionResolver getSimpleMappingExceptionResolver() {
    18         SimpleMappingExceptionResolver resolver = new SimpleMappingExceptionResolver();
    19         Properties mappings = new Properties();
    20         // 参数一:异常的类型,注意必须是异常类型的全名
    21         // 参数二:视图名称
    22         mappings.put("java.lang.ArithmeticException", "error1");
    23         mappings.put("java.lang.NullPointerException", "error2");
    24         // 设置异常与视图映射信息的
    25         resolver.setExceptionMappings(mappings);
    26         return resolver;
    27     }
    28 
    29 
    30 }

    可以根据不同的错误来定义不同的错误提示界面,如下创建了error1.html、error2.html。这里直接使用了上面创建的错误界面error1.htm、error2.html。

    缺点,和第三种方式对比,无法传递异常对象信息,只是跳转到指定的异常错误界面了。

    1.5、第五种方式,自定义 HandlerExceptionResolver 类处理异常。需要在全局异常处理类中实现HandlerExceptionResolver接口。

     1 package com.bie.springboothello.controller;
     2 
     3 
     4 import org.springframework.context.annotation.Configuration;
     5 import org.springframework.web.servlet.HandlerExceptionResolver;
     6 import org.springframework.web.servlet.ModelAndView;
     7 
     8 import javax.servlet.http.HttpServletRequest;
     9 import javax.servlet.http.HttpServletResponse;
    10 
    11 /**
    12  * 通过实现 HandlerExceptionResolver接口做全局异常处理
    13  */
    14 @Configuration
    15 public class GlobalException implements HandlerExceptionResolver {
    16 
    17     @Override
    18     public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
    19         ModelAndView mv = new ModelAndView();
    20         // 判断不同异常类型,做不同视图跳转
    21         if (ex instanceof ArithmeticException) {
    22             mv.setViewName("error1");
    23         }
    24         if (ex instanceof NullPointerException) {
    25             mv.setViewName("error2");
    26         }
    27         mv.addObject("error", ex.toString());
    28         return mv;
    29     }
    30 
    31 
    32 }

    可以根据不同的错误来定义不同的错误提示界面,如下创建了error1.html、error2.html。这里直接使用了上面创建的错误界面error1.htm、error2.html。

    2、Spring Boot整合Junit 单元测试。在pom.xml配置文件中加入junit的启动类依赖包。

     1 <!-- 添加junit环境的jar包 -->
     2 <dependency>
     3     <groupId>org.springframework.boot</groupId>
     4     <artifactId>spring-boot-starter-test</artifactId>
     5     <scope>test</scope>
     6     <!--<exclusions>
     7         <exclusion>
     8             <groupId>org.junit.vintage</groupId>
     9             <artifactId>junit-vintage-engine</artifactId>
    10         </exclusion>
    11     </exclusions>-->
    12 </dependency>

    Spring Boot整合Junit 单元测试,代码如下所示:

     1 package com.bie.springboothello;
     2 
     3 import com.bie.springboothello.po.Users;
     4 import com.bie.springboothello.service.UsersService;
     5 import org.junit.jupiter.api.Test;
     6 import org.junit.runner.RunWith;
     7 import org.springframework.beans.factory.annotation.Autowired;
     8 import org.springframework.boot.test.context.SpringBootTest;
     9 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    10 
    11 /**
    12  * Springboot测试类。
    13  *
    14  * @RunWith:启动器。 SpringJUnit4ClassRunner.class:让junit与spring环境进行整合。
    15  * @SpringBootTest(classes={SpringbootHelloApplication.class}) 第一层含义, 当前类为springBoot的测试类。
    16  * @SpringBootTest(classes={SpringbootHelloApplication.class}) 第二层含义, 加载SpringBoot启动类。启动springBoot。
    17  * <p>
    18  * junit 与 spring 整合 @Contextconfiguartion("classpath:applicationContext.xml")
    19  */
    20 @RunWith(value = SpringJUnit4ClassRunner.class)
    21 @SpringBootTest(classes = {SpringbootHelloApplication.class})
    22 class SpringbootHelloApplicationTests {
    23 
    24     @Autowired
    25     private UsersService usersService;
    26 
    27     @Test
    28     public void testAddUser() {
    29         System.out.println("开始---------------------------------------------");
    30         this.usersService.addUser(new Users("别先生", 25));
    31         System.out.println("结束---------------------------------------------");
    32     }
    33 
    34 }

    3、Spring Boot热部署。Springboot的热部署,热部署就是在服务不停止的情况下,完成项目的部署处理。意思就是修改完系统就可以立刻看到效果,不用重启项目。

      SprigBoot的热部署方式分为两种,第一种是SpringLoader插件、第二种DevTools工具。

      在这里吐槽一下eclipse和idea吧,我以eclipse为主,也比较喜欢eclipse吧,因为eclipse免费,哈哈哈,idea收费的,虽然可以买或者找到破解的方法,但是个人还是比较喜欢eclipse的,虽说idea收费版创建springboot是真的爽,eclipse也集成了springboot创建的插件,但是创建成功还需要进行简单的配置,不然pom.xml老是报错,也是十分不爽的。还有一个就是就比如此案例,idea想以maven方式运行项目,可能自己对idea不熟悉吧,嗯,此案例又换成了eclipse创建,练习热部署。

    3.1、方式一:以 maven 插件方式使用 SpringLoader。在pom文件中添加插件配置。springloader插件添加到pom.xml配置文件中,将插件的依赖包导入到maven中。

     1 <!-- springloader插件 -->
     2 <build>
     3     <plugins>
     4         <plugin>
     5             <groupId>org.springframework.boot</groupId>
     6             <artifactId>spring-boot-maven-plugin</artifactId>
     7             <dependencies>
     8                 <dependency>
     9                     <groupId>org.springframework</groupId>
    10                     <artifactId>springloaded</artifactId>
    11                     <version>1.2.5.RELEASE</version>
    12                 </dependency>
    13             </dependencies>
    14         </plugin>
    15     </plugins>
    16 </build>

    完整的pom.xml配置文件,如下所示:

    注意:开始使用的是Springboot2.2.7.RELEASE版本,死活实现不了修改后台,热部署启动,改成了1.5.10.RELEASE版本才实现了效果,我想我大概是不会使用这种方法来实现热部署吧。

     1 <project xmlns="http://maven.apache.org/POM/4.0.0"
     2     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     3     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
     4     http://maven.apache.org/xsd/maven-4.0.0.xsd">
     5     <modelVersion>4.0.0</modelVersion>
     6     <parent>
     7         <groupId>org.springframework.boot</groupId>
     8         <artifactId>spring-boot-starter-parent</artifactId>
     9         <!-- 由于学习热部署的缘故,换成了eclipse创建springboot项目,此案例使用了springboot1.5.10.RELEASE版本 -->
    10         <version>1.5.10.RELEASE</version>
    11     </parent>
    12     <groupId>com.bie.springboot</groupId>
    13     <artifactId>springboot-world</artifactId>
    14     <version>0.0.1-SNAPSHOT</version>
    15 
    16 
    17     <properties>
    18         <java.version>1.8</java.version>
    19         <!-- 解决eclipse使用springboot插件创建springboot项目pom.xml配置文件报错的问题 -->
    20         <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
    21         <thymeleaf.version>3.0.2.RELEASE</thymeleaf.version>
    22         <thymeleaf-layout-dialect.version>2.0.4</thymeleaf-layout-dialect.version>
    23     </properties>
    24 
    25     <dependencies>
    26         <dependency>
    27             <groupId>org.springframework.boot</groupId>
    28             <artifactId>spring-boot-starter-web</artifactId>
    29         </dependency>
    30 
    31         <!-- thymeleaf 的启动器 -->
    32         <dependency>
    33             <groupId>org.springframework.boot</groupId>
    34             <artifactId>spring-boot-starter-thymeleaf</artifactId>
    35         </dependency>
    36 
    37     </dependencies>
    38 
    39     <!-- springloader 插件 -->
    40     <build>
    41         <plugins>
    42             <plugin>
    43                 <groupId>org.springframework.boot</groupId>
    44                 <artifactId>spring-boot-maven-plugin</artifactId>
    45                 <dependencies>
    46                     <dependency>
    47                         <groupId>org.springframework</groupId>
    48                         <artifactId>springloaded</artifactId>
    49                         <version>1.2.5.RELEASE</version>
    50                     </dependency>
    51                 </dependencies>
    52             </plugin>
    53         </plugins>
    54     </build>
    55 
    56 </project>

    注意:使用 maven 的命令起来启动,如果还是以直接运行main方法启动的话,是没有使用到这个插件的。所以要使用maven的命令运行,才可以做到热部署效果,但是此插件只能做到修改后台不用启动,前端html修改了是无法进行热部署的。

    使用 maven 的命令spring-boot:run来启动项目。

    SpringLoader插件的缺陷:就是 Java 代码做部署处理,但是对页面无能为力。 

    效果,如下所示:

    注意:这种方式的缺点是 Springloader 热部署程序是在 系统后台以进程的形式来运行,需要手动关闭该进程。嗯,我更不要选择这种方式来做热部署了。

    Windows下查看进程及结束进程命令。查看占用8080端口的进程号。

    netstat -aon | findstr "8080"

    可知,进程号为10968的进程占用了8080端口,可以使用命令 tasklist | findstr “10968”进一步查看10968进程的具体信息。可知10968进程为javaw.exe。 

    tasklist | findstr "10968"

    杀掉进程,tskill 10968。

    如果显示'tskill' 不是内部或外部命令,也不是可运行的程序或批处理文件。那么用任务管理器吧,找到pid结束进程。

    或者在项目中直接使用jar包的方式,添加springloader的jar包,在项目的lib目录下面添加springloader的jar包。然后在启动的时候使用maven的命令来启动。

    启动命令: -javaagent:.libspringloaded-1.2.5.RELEASE.jar -noverify

    3.1、方式二:使用DevTools 工具,修改项目的 pom.xml配置文件添加 devtools 的依赖。

    注意:SpringLoader与DevTools 的区别:

    1)、SpringLoader:SpringLoader 在部署项目时使用的是热部署的方式。

    2)、DevTools:DevTools 在部署项目时使用的是重新部署的方式。

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     3          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     4     <modelVersion>4.0.0</modelVersion>
     5     <parent>
     6         <groupId>org.springframework.boot</groupId>
     7         <artifactId>spring-boot-starter-parent</artifactId>
     8         <version>2.2.6.RELEASE</version>
     9         <relativePath/> <!-- lookup parent from repository -->
    10     </parent>
    11     <groupId>com.bie</groupId>
    12     <artifactId>springboot-hello</artifactId>
    13     <version>0.0.1-SNAPSHOT</version>
    14     <name>springboot-hello</name>
    15     <description>Demo project for Spring Boot</description>
    16 
    17     <properties>
    18         <java.version>1.8</java.version>
    19     </properties>
    20 
    21     <dependencies>
    22         <dependency>
    23             <groupId>org.springframework.boot</groupId>
    24             <artifactId>spring-boot-starter-web</artifactId>
    25         </dependency>
    26         <!-- 添加junit环境的jar包 -->
    27         <dependency>
    28             <groupId>org.springframework.boot</groupId>
    29             <artifactId>spring-boot-starter-test</artifactId>
    30             <scope>test</scope>
    31             <!--<exclusions>
    32                 <exclusion>
    33                     <groupId>org.junit.vintage</groupId>
    34                     <artifactId>junit-vintage-engine</artifactId>
    35                 </exclusion>
    36             </exclusions>-->
    37         </dependency>
    38         <!-- thymeleaf的启动器 -->
    39         <dependency>
    40             <groupId>org.springframework.boot</groupId>
    41             <artifactId>spring-boot-starter-thymeleaf</artifactId>
    42         </dependency>
    43         <!-- mybatis的启动器 -->
    44         <dependency>
    45             <groupId>org.mybatis.spring.boot</groupId>
    46             <artifactId>mybatis-spring-boot-starter</artifactId>
    47             <version>2.1.1</version>
    48         </dependency>
    49         <!-- mysql数据库驱动的依赖包 -->
    50         <dependency>
    51             <groupId>mysql</groupId>
    52             <artifactId>mysql-connector-java</artifactId>
    53         </dependency>
    54         <!-- druid数据库连接池 -->
    55         <dependency>
    56             <groupId>com.alibaba</groupId>
    57             <artifactId>druid</artifactId>
    58             <version>1.1.10</version>
    59         </dependency>
    60         <dependency>
    61             <groupId>junit</groupId>
    62             <artifactId>junit</artifactId>
    63             <scope>test</scope>
    64         </dependency>
    65         <!-- DevTools 的坐标 -->
    66         <dependency>
    67             <groupId>org.springframework.boot</groupId>
    68             <artifactId>spring-boot-devtools</artifactId>
    69             <!-- 表示当前依赖不向下传递 -->
    70             <optional>true</optional>
    71         </dependency>
    72     </dependencies>
    73 
    74     <build>
    75         <plugins>
    76             <plugin>
    77                 <groupId>org.springframework.boot</groupId>
    78                 <artifactId>spring-boot-maven-plugin</artifactId>
    79             </plugin>
    80         </plugins>
    81         <!-- 此配置可以解决,如果将*.mapper.xml配置文件放入到src/main/java,无法加载mybatis映射文件的问题 -->
    82         <!--<resources>
    83             <resource>
    84                 <directory>src/main/java</directory>
    85                 <includes>
    86                     <include>**/*.xml</include>
    87                 </includes>
    88             </resource>
    89         </resources>-->
    90     </build>
    91 
    92 
    93 </project>

    如果Idea如法实现热部署,那么可能是idea和DevTools和Loader的配置问题,Intellij IEDA和Eclipse不同,Eclipse一般设置了自动编译,而IDEA需要自己打开。

    在setting->Build,Execution,Deployment->Compiler找到 Build Project Automatically。 这个选项再Eclipse是默认打开的,再IDEA要手动打开。

    然后找个地方ctrl+shift+alt+/ 调出Maintenance(维护)控制台,选择Registry(登记)。

    勾选运行时自动编译(auto-making when app running)。重启Idea就可以了。

    重启之后,此时修改前端还是后台,都可以实现热部署的,如果修改后端的话,Idea控制台会重新部署,如果是修改前端界面,是不会重新部署的,但是也已经刷新了。大大节省了时间和精力。

  • 相关阅读:
    shell中括号的特殊用法 linux if多条件判断
    uboot kernel 博客
    meson 中调用shell script
    200. 岛屿数量
    9. 回文数
    53. 最大子序和
    394. 字符串解码
    32. 最长有效括号
    leetcode排序的题 912. 排序数组 215. 数组中的第K个最大元素
    c++引用和运算符重载思考
  • 原文地址:https://www.cnblogs.com/biehongli/p/12888136.html
Copyright © 2011-2022 走看看