學習springBoot 進階AOP處理請求(五)
AOP概述
如圖中顯示,當我們請求的操作繼續往下走的時候都是相類似的, 那這個時候我們就可以把具體的業務操作程式碼提取出來作為公共的操作,這樣就有了面向切面程式設計AOP
下面來舉例子說明 如何AOP統一處理請求日誌
pom.xml 檔案中引入AOP依賴
<!-- 引入AOP依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
建立一個AOP統一攔截處理類
HttpAspect 類
package com.zhang.aspect; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.springframework.stereotype.Component; /** * AOP統一攔截處理類 * @author Administrator * */ @Aspect @Component public class HttpAspect { /** * 這個註解表示在請求GirlController類中的girlLsit()方法前攔截 * 方法中的的(..)表示傳任何的引數都可以不限制,都會攔截。不會因為你傳參的問題而不攔截 * * 如果我們想每個方法都要攔截的話把方法改變成 * (星號),這樣springBoot就會幫你把全部的方法都配置要攔截處理 */ @Before("execution(public * com.zhang.contorller.GirlController.girlLsit(..))") public void log(){ System.out.println("111111111111111111"); } }
Postman 測試一下呼叫girlList()方法會不會被攔截,結果攔截成功
如果我們想每個方法都要攔截的話把方法改變成 * (星號),這樣springBoot就會幫你把全部的方法都配置要攔截處理,修改好之後重新啟動專案在測試一下是否攔截成功
Postman 測試一下呼叫查詢一個女生方法,測試攔截成功
為了在確認是否是在請求之前攔截的,在girlLsit()這方法中也列印一下。我們可以通過列印的前面再次確認攔截是在請求前還是在請求後實現
測試看到列印的結果,確實是在請求前攔截的
有攔截前就有攔截後,在HttpAspect類中加入攔截後的方法
/**
* 這個註解表示在請求GirlController類中的girlLsit()方法後攔截
*/
@After("execution(public * com.zhang.contorller.GirlController.*(..))")
public void doAfter(){
System.out.println("22222222222222");
}
Postman 測試一下呼叫查詢一個女生集合方法,測試攔截成功
高類聚低耦合,避免老是寫重複的程式碼,我們又把抽離出一個公共的攔截方法,測試效果和之前一樣就說明抽離成功
HttpAspect 類
package com.zhang.aspect;
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.springframework.stereotype.Component;
/**
* AOP統一攔截處理類
* @author Administrator
*
*/
@Aspect
@Component
public class HttpAspect {
/**
* 定義一個公共的攔截方法 讓別的方法呼叫,高類聚低耦合
*/
@Pointcut("execution(public * com.zhang.contorller.GirlController.*(..))")
public void log(){
}
/**
* 這個註解表示在請求GirlController類中的girlLsit()方法前攔截
* 方法中的的(..)表示傳任何的引數都可以不限制,都會攔截。不會因為你傳參的問題而不攔截
*
* 如果我們想每個方法都要攔截的話把方法改變成 * (星號),這樣springBoot就會幫你把全部的方法都配置要攔截處理
*/
@Before("log()")
public void doBefore(){
System.out.println("111111111111111111");
}
/**
* 這個註解表示在請求GirlController類中的girlLsit()方法後攔截
*/
@After("log()")
public void doAfter(){
System.out.println("22222222222222");
}
}
測試結果,抽離成功
下面在轉換成用日誌的形式打印出來,
HttpAspect 類 logger一定是選擇spring自帶的 import org.slf4j.Logger;
package com.zhang.aspect;
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.springframework.stereotype.Component;
/**
* AOP統一攔截處理類
* @author Administrator
*
*/
@Aspect
@Component
public class HttpAspect {
private final static Logger logger= org.slf4j.LoggerFactory.getLogger(HttpAspect.class);
/**
* 定義一個公共的攔截方法 讓別的方法呼叫,高類聚低耦合
*/
@Pointcut("execution(public * com.zhang.contorller.GirlController.*(..))")
public void log(){
}
/**
* 這個註解表示在請求GirlController類中的girlLsit()方法前攔截
* 方法中的的(..)表示傳任何的引數都可以不限制,都會攔截。不會因為你傳參的問題而不攔截
*
* 如果我們想每個方法都要攔截的話把方法改變成 * (星號),這樣springBoot就會幫你把全部的方法都配置要攔截處理
*/
@Before("log()")
public void doBefore(){
logger.info("1111111111111111");
}
/**
* 這個註解表示在請求GirlController類中的girlLsit()方法後攔截
*/
@After("log()")
public void doAfter(){
logger.info("22222222222222");
}
}
重新啟動專案測試一下, 我們要輸出的內容全部都以日誌的形式打印出來,同時建議所以的攔截都是以日誌的形式輸出為更佳的方案
GirlController 類也新增上日誌物件
package com.zhang.contorller;
import java.util.List;
import javax.validation.Valid;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import com.zhang.aspect.HttpAspect;
import com.zhang.domain.Girl;
import com.zhang.repository.GirlRepository;
import com.zhang.service.GirlService;
@RestController
public class GirlController {
@Autowired
private GirlRepository girlRepository;
@Autowired
private GirlService girlService;
private final static Logger logger= org.slf4j.LoggerFactory.getLogger(HttpAspect.class);
/**
* 查詢girl物件集合
* @return
*/
@GetMapping(value="/girls")
public List<Girl> girlLsit(){
logger.info("girlLsit");
return girlRepository.findAll();
}
/**
* 新增一個女孩
*/
@PostMapping(value="/girlsAdd")
public Girl girlAdd(@Valid Girl girl , BindingResult bindingResult){
if(bindingResult.hasErrors()){
System.out.println(bindingResult.getFieldError().getDefaultMessage());
return null;
}
girl.setCupSize(girl.getCupSize());
girl.setAge(girl.getAge());
return girlRepository.save(girl);
}
/**
* 查詢一個女生
*/
@GetMapping(value = "/girls/{id}")
public Girl girlFindOne(@PathVariable("id") Integer id){
return girlRepository.findOne(id);
}
/**
* 更新女生資料
*/
@PutMapping(value="/girls/{id}")
public Girl girlUpdate(@PathVariable("id") Integer id ,
@RequestParam("cupSize") String cupSize,
@RequestParam("age") Integer age){
Girl girl = new Girl();
girl.setEid(id);
girl.setCupSize(cupSize);
girl.setAge(age);
return girlRepository.save(girl);
}
/**
* 刪除女生資料
*/
@DeleteMapping(value="/girls/{id}")
public void girlDelete(@PathVariable("id") Integer id){
girlRepository.delete(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();
}
}
重新啟動專案測試一下, 我們要輸出的內容全部都以日誌的形式打印出來
接下來開始舉例記錄HTTP請求
HttpAspect 類
package com.zhang.aspect;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.aspectj.lang.JoinPoint;
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.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
/**
* AOP統一攔截處理類
* @author Administrator
*
*/
@Aspect
@Component
public class HttpAspect {
private final static Logger logger= org.slf4j.LoggerFactory.getLogger(HttpAspect.class);
/**
* 定義一個公共的攔截方法 讓別的方法呼叫,高類聚低耦合
*/
@Pointcut("execution(public * com.zhang.contorller.GirlController.*(..))")
public void log(){
}
/**
* 這個註解表示在請求GirlController類中的girlLsit()方法前攔截
* 方法中的的(..)表示傳任何的引數都可以不限制,都會攔截。不會因為你傳參的問題而不攔截
*
* 如果我們想每個方法都要攔截的話把方法改變成 * (星號),這樣springBoot就會幫你把全部的方法都配置要攔截處理
*/
@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());
//類方法
logger.info("class_mothod={}",joinpoint.getSignature().getDeclaringTypeName()+"."+joinpoint.getSignature().getName());
//引數
logger.info("args={}",joinpoint.getArgs());
}
/**
* 這個註解表示在請求GirlController類中的girlLsit()方法後攔截
*/
@After("log()")
public void doAfter(){
logger.info("22222222222222");
}
}
獲取到HTTP請求中屬性以及屬性對應的值
獲取到HTTP請求返回的內容
HttpAspect 類 新增方法
/**
* 獲取到HTTP請求返回的內容
*/
@AfterReturning(returning="object",pointcut="log()")
public void doAfterReturning( Object object){
logger.info("response={}",object.toString());
}
girl 類中重寫tostring 方法
@Override
public String toString() {
return "Girl [eid=" + eid + ", cupSize=" + cupSize + ", age=" + age + "]";
}
測試結果,獲取到HTTP請求中屬性以及屬性對應的值 與 獲取到HTTP請求返回的內容