SpringBoot進階之Web進階
一.表單驗證
1.為要校驗的物件新增對應的校驗註解
-
使用
@Min(value=18,message="未成年少女金之入內")
標記對應類中的屬性。註解表示age最小值為18,錯誤提示為:未成年少女禁止入內 -
Girl類例項程式碼
package com.study.demo.domain; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; import javax.validation.constraints.Min; @Entity
2.在對應方法的入參中使用@Vaild註解標記該對應需要被驗證,並通過BindingResult型別引數接受驗證結果
-
結果物件的hasErrors()方法用於判斷是否發生錯誤
-
結果物件的getFieldError().getDefaultMessage()方法獲得對應的錯誤資訊
-
對應方法的例項程式碼
//新增女生 @PostMapping(value="/girls") public Girl girlAdd(@Valid Girl girl, BindingResult bindingResult){//@Valid註解表示要驗證的物件為girl,驗證結果會返回到引數bindingResult物件中 //通過物件的hasErrors()方法獲得是否發生錯誤 if(bindingResult.hasErrors()){ //並通過getFieldError().getDefaultMessage()方法獲得對應的錯誤資訊 System.out.println(bindingResult.getFieldError().getDefaultMessage()); return null; } girl.setCupSize(girl.getCupSize()); girl.setAge(girl.getAge()); return girlRepository.save(girl);//方法返回的就是新增的物件 }
-
當傳送post請求時,若cupSize屬性超過18則會在控制檯輸出“未成年少女禁止入內”,並返回null值
二.AOP統一處理請求日誌
1.什麼是AOP?
- AOP是一種程式設計正規化,與語言無關,是一種程式設計思想
- 面向切面(AOP)Aspect Oriented Programming:將通用邏輯從業務邏輯中分離出來
- 面向物件(OOP)Object Oriented Programming
- 面向過程(POP)Procedure Oriented Programming
2.網路請求的生命週期
收到HttpRequest請求
—> 記錄請求
—> 處理網路請求
—> 生成HttpResponse
—> 記錄回覆
3.操作資料庫的生命週期
收到資料庫操作請求
—> 記錄請求
—> 增刪改查
—> 生成處理結果
—> 記錄回覆
4.記錄每一個http請求
-
新增依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
-
建立處理檔案:aspect/HttpAspect.java
import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Aspect//標記類為Aspect類 @Component//引入到Spring容器中 public class HttpAspect { //org.slf4j.Logger是Spring自帶的日誌記錄框架 private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class); @Pointcut("execution(public * com.study.demo.controller.HelloController.girlList(..))")//.girlList(..)表示girlList方法不管是任何引數都會被攔截;.*(..)則表示該類的所有方法都會被攔截 public void log(){ } //定義在http請求到方法之前將內容記錄下來 @Before("log()") public void doBefore(){ logger.info("記錄日誌");//使用Logger的方法記錄日誌 } //定義在執行完方法後呼叫 @After("log()") public void doAfter(){ logger.info("執行方法之後的呼叫..."); } }
-
訪問介面時的顯示內容(可以看出使用
logger.info()
方法列印的內容帶有時間等相關資訊)2018-09-19 12:58:55.250 INFO 44464 --- [nio-8082-exec-2] com.study.demo.aspect.HttpAspect : 記錄日誌 2018-09-19 12:58:55.253 INFO 44464 --- [nio-8082-exec-2] c.study.demo.controller.HelloController : 執行到GirlList 2018-09-19 12:58:55.280 INFO 44464 --- [nio-8082-exec-2] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory Hibernate: select girl0_.id as id1_0_, girl0_.age as age2_0_, girl0_.cup_size as cup_size3_0_ from girl girl0_ 2018-09-19 12:58:55.364 INFO 44464 --- [nio-8082-exec-2] com.study.demo.aspect.HttpAspect : 執行方法之後的呼叫...
-
調整@Before註解的方法執行指定內容
@Before("log()") public void doBefore(JoinPoint joinPoint){ ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); //url logger.info("url={}",request.getRequestURL()); //method logger.info("method={}",request.getMethod()); //客戶端的ip logger.info("ip={}",request.getRemoteAddr()); //請求的類方法,通過當前類的引數傳入的JoinPoint物件獲取對應的內容 //joinPoint.getSignature().getDeclaringTypeName()獲取的是類名 //joinPoint.getSignature().getName()獲取類方法名 logger.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName()); //引數 logger.info("args={}",joinPoint.getArgs()); }
-
通過
@AfterReturning
註解獲得方法返回的內容//引數1為方法的入參變數名,引數2為使用的連線點方法名 @AfterReturning(returning="object",pointcut="log()") //返回的內容可以是很多,但是對於程式而言他們都是物件 public void doAfterReturning(Object object){ logger.info("response={}",object); }
-
訪問介面時顯示的內容
2018-09-19 13:20:31.876 INFO 44599 --- [nio-8082-exec-1] com.study.demo.aspect.HttpAspect : url=http://localhost:8082/girls 2018-09-19 13:20:31.876 INFO 44599 --- [nio-8082-exec-1] com.study.demo.aspect.HttpAspect : method=GET 2018-09-19 13:20:31.876 INFO 44599 --- [nio-8082-exec-1] com.study.demo.aspect.HttpAspect : ip=0:0:0:0:0:0:0:1 2018-09-19 13:20:31.878 INFO 44599 --- [nio-8082-exec-1] com.study.demo.aspect.HttpAspect : class_method=com.study.demo.controller.HelloController.girlList 2018-09-19 13:20:31.878 INFO 44599 --- [nio-8082-exec-1] com.study.demo.aspect.HttpAspect : args={} 2018-09-19 13:20:31.882 INFO 44599 --- [nio-8082-exec-1] c.study.demo.controller.HelloController : 執行到GirlList 2018-09-19 13:20:31.911 INFO 44599 --- [nio-8082-exec-1] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory Hibernate: select girl0_.id as id1_0_, girl0_.age as age2_0_, girl0_.cup_size as cup_size3_0_ from girl girl0_ 2018-09-19 13:20:31.991 INFO 44599 --- [nio-8082-exec-1] com.study.demo.aspect.HttpAspect : 執行方法之後的呼叫... 2018-09-19 13:20:31.991 INFO 44599 --- [nio-8082-exec-1] com.study.demo.aspect.HttpAspect : response=[]
三.統一異常處理
統一異常處理的例項
-
定義統一異常返回物件
/src/main/java/com/study/demo/domain/Result.java
package com.study.demo.domain; /** * Http請求返回的最外層物件 */ public class Result<T> { /* 錯誤碼 */ private Integer code; /* 提示資訊 */ private String msg; /* 具體內容 */ private T 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; } public T getData() { return data; } public void setData(T data) { this.data = data; } @Override public String toString() { return "Result{" + "code=" + code + ", msg='" + msg + '\'' + ", data=" + data + '}'; } }
-
定義錯誤物件生成工具方法
/src/main/java/com/study/demo/utils/ResultUtil.java
package com.study.demo.utils; import com.study.demo.domain.Result; public class ResultUtil { public static Result success(Object object){ Result result = new Result(); result.setCode(0); result.setMsg("成功"); result.setData(object); return result; } public static Result success(){ return success(null); } public static Result error(Integer code,String msg){ Result result = new Result(); result.setCode(code); result.setMsg(msg); result.setData(null); return result; } }
-
自定義異常,用於傳遞對應的錯誤編號
/src/main/java/com/study/demo/exception/GirlException.java
package com.study.demo.exception; import com.study.demo.enums.ResultEnum; public class GirlException extends RuntimeException { private Integer code; public GirlException(ResultEnum resultEnum){ super(resultEnum.getMsg()); this.code = resultEnum.getCode(); } public Integer getCode() { return code; } public void setCode(Integer code) { this.code = code; } }
-
定義異常處理內容,在方法丟擲異常時執行的對應處理方法
/src/main/java/com/study/demo/handle/ExceptionHandle.java
package com.study.demo.handle; import com.study.demo.domain.Result; import com.study.demo.exception.GirlException; import com.study.demo.utils.ResultUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice public class ExceptionHandle { public static final Logger logger = LoggerFactory.getLogger(ExceptionHandle.class); //宣告要捕獲哪個異常類 @ExceptionHandler(value = Exception.class) //由於要返回的是一個json,但是類上方又沒有@RestController註解,故需要用ResponseBody修飾 @ResponseBody public Result handle(Exception e){ if(e instanceof GirlException){ GirlException girlException = (GirlException)e; return ResultUtil.error(girlException.getCode(),girlException.getMessage()); }else{ logger.error("[系統異常]{}",e); return ResultUtil.error(-1,"未知錯誤"); } } }
-
在Controller中新增判斷物件年齡並丟擲異常測試方法
/src/main/java/com/study/demo/controller/HelloController.java
package com.study.demo.controller; import com.study.demo.domain.Girl; import com.study.demo.domain.Result; import com.study.demo.repository.GirlRepository; import com.study.demo.service.GirlService; import com.study.demo.utils.ResultUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.context.properties.bind.BindResult; import org.springframework.validation.BindingResult; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import java.util.List; import java.util.Optional; @RestController public class HelloController { public final static Logger logger = LoggerFactory.getLogger(HelloController.class); @Autowired private GirlRepository girlRepository; @Autowired public GirlService girlService; //查詢所有女生列表 @GetMapping(value="/girls") public List<Girl> girlList(){ logger.info("執行到GirlList"); return girlRepository.findAll(); } //新增女生 @PostMapping(value="/girls") public Result girlAdd(@Valid Girl girl, BindingResult bindingResult){//@Valid註解表示要驗證的物件為girl,驗證結果會返回到引數bindingResult物件中 //通過物件的hasErrors()方法獲得是否發生錯誤 if(bindingResult.hasErrors()){ return ResultUtil.error(1,bindingResult.getFieldError().getDefaultMessage()); } girl.setCupSize(girl.getCupSize()); girl.setAge(girl.getAge()); return ResultUtil.success(girlRepository.save(girl));//方法返回的就是新增的物件 } //查詢一個女生 @GetMapping(value="/girls/{id}") public Optional<Girl> girlFindOne(@PathVariable("id")Integer id){ Optional<Girl> girl = girlRepository.findById(id); return girl; } //更新 @PutMapping(value="/girls/{id}") public Girl girlUpdate(@PathVariable("id")Integer id,@RequestParam("cupSize")String cupSize,@RequestParam("age")Integer age){ Girl girl = new Girl(); girl.setId(id); girl.setCupSize(cupSize); girl.setAge(age); return girlRepository.save(girl); } //刪除 @DeleteMapping(value="/girls/{id}") public void girlDelete(@PathVariable("id")Integer id){ girlRepository.deleteById(id); } //通過年齡查詢女生列表 @GetMapping(value="/girls/age/{age}") public List<Girl>girlListByAge(@PathVariable("age")Integer age){ return girlRepository.findByAge(age); } //新增兩條資料 @PostMapping(value="/girls/two") public void girlTwo(){ girlService.insertTwo(); } //判斷物件年齡並丟擲異常測試方法 @GetMapping(value = "girls/getAge/{id}") public void getAge(@PathVariable("id")Integer id) throws Exception{ girlService.getAge(id); } }
-
在service中新增對應處理方法,針對不同情況的age內容丟擲不同錯誤碼的異常
/src/main/java/com/study/demo/service/GirlService.java
package com.study.demo.service; import com.study.demo.domain.Girl; import com.study.demo.enums.ResultEnum; import com.study.demo.exception.GirlException; import com.study.demo.repository.GirlRepository; import org.