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:.\lib\springloaded-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控制台會重新部署,如果是修改前端界面,是不會重新部署的,但是也已經刷新了。大大節省了時間和精力。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM