首先說明一下,這里使用的是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控制台會重新部署,如果是修改前端界面,是不會重新部署的,但是也已經刷新了。大大節省了時間和精力。