1. 程式人生 > >SpringBoot進階之Web進階

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
    public class Girl{ @Id @GeneratedValue private Integer id; private String cupSize; //表示age最小值為18,錯誤提示為:未成年少女禁止入內 @Min(value=18,message = "未成年少女禁止入內") private Integer age; public Integer getId() { return id; } public void setId(Integer id) { this
    .id = id; } public String getCupSize() { return cupSize; } public void setCupSize(String cupSize) { this.cupSize = cupSize; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @Override
    public String toString() { return "Girl{" + "id=" + id + ", cupSize='" + cupSize + '\'' + ", age=" + age + '}'; } }

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.