SpringBoot-AOP
使用AOP統一處理請求日志
1.AOP的概念
AOP:AOP是一種編程范式,與語言無關,是一種程序設計思想
面向切面(AOP) Aspect Oriented Programming
面向對象(OOP) Object Oriented Programming
面向過程(POP) Procedure Oriented Programming
面向過程到面向對象:
功能:下雨了,我打開了雨傘
面向過程:假如下雨了,我打開了雨傘
面向對象:天氣-->下雨;我-->打傘
即:換個角度看世界,換個姿勢處理問題
面向對象:關注的是將需求功能垂直劃分為不同的并且相對獨立的,會封裝成良好的類并且讓他們有屬于自己的行為。
面向切面:利用橫切的技術,將面向對象構建的龐大的類的體系進行水平的切割,并且將其中會影響到多個類的公共行為封裝成一個可重用的模塊,該模塊就成為切面
AOP的思想:將通用的邏輯從業務邏輯中分離出來
2.實現AOP
網絡請求和數據庫操作請求:
1.jpg
使用AOP思想:
2.jpg
3.實現使用AOP記錄每一個http請求
需求:授權訪問(必須先登錄才能訪問)
第一步:添加依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<!--項目的基本描述-->
<groupId>com.hcx</groupId>
<artifactId>girl</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>girl</name>
<description>Demo project for Spring Boot</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--web項目必須引入的依賴-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--單元測試時需要用到的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
第二步:編寫切面類:
@Aspect
@Component
public class HttpAspect {
//在http請求到方法之前記錄
/**
* @Before注解:在方法執行之前執行
* com.hcx包下的girlcontroller里面的girlList方法,不管是什么參數都會被攔截
* 所有方法都攔截:execution(public * com.hcx.controller.GirlController.*(..))
*/
@Before("execution(public * com.hcx.controller.GirlController.girlList(..))")
public void log(){
System.out.println("被攔截了");
}
@After("execution(public * com.hcx.controller.GirlController.girlList(..))")
public void doAfter(){
System.out.println("我是方法執行之后被攔截");
}
}
第三步:攔截controller中的方法:
@RestController
public class GirlController {
@Autowired
private GirlRepository girlRepository;
@Autowired
private GirlService girlService;
/**
* 查詢所有女生
* @return
*/
@GetMapping(value = "/girls")
public List<Girl> girlList(){
System.out.println("我是girlList方法");
return girlRepository.findAll();
}
}
運行結果:
3.jpg
4.jpg
注意:以上代碼可以看出有重復,在切面類中優化:
HttpAspect:
@Aspect
@Component
public class HttpAspect {
private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class); //該類是org.slf4j的
//在http請求到方法之前記錄
/**
* @Before注解:在方法執行之前執行
* com.hcx包下的girlcontroller里面的girlList方法,不管是什么參數都會被攔截
* 所有方法都攔截:execution(public * com.hcx.controller.GirlController.*(..))
*/
/*@Before("execution(public * com.hcx.controller.GirlController.girlList(..))")
public void log(){
System.out.println("被攔截了");
}*/
/**
* 公用的方法,使用@Pointcut注解
*/
@Pointcut("execution(public * com.hcx.controller.GirlController.girlList(..))")
public void log(){
}
@Before("log()")
public void doBefore(){
// System.out.println("我是方法執行之前被攔截");
logger.info("前日志信息"); //打印info、error等日志
}
@After("log()")
public void doAfter(){
// System.out.println("我是方法執行之后被攔截");
logger.info("后日志信息");
}
}
GirlController:
@RestController
public class GirlController {
/**
* getLogger方法中的參數與類名對應
*/
private final static Logger logger = LoggerFactory.getLogger(GirlController.class);
@Autowired
private GirlRepository girlRepository;
@Autowired
private GirlService girlService;
/**
* 查詢所有女生
* @return
*/
@GetMapping(value = "/girls")
public List<Girl> girlList(){
// System.out.println("我是girlList方法");
logger.info("我是girlList方法");
return girlRepository.findAll();
}
}
5.jpg
6.jpg
記錄http請求:
HttpAspect:
@Aspect
@Component
public class HttpAspect {
private final static Logger logger = LoggerFactory.getLogger(HttpAspect.class); //該類是org.slf4j的
/**
* 公用的方法,使用@Pointcut注解
*/
@Pointcut("execution(public * com.hcx.controller.GirlController.girlList(..))")
public void log(){
}
@Before("log()")
public void doBefore(JoinPoint joinPoint){
/**
* 請求路徑:url
* 請求方式:method
* 客戶端ip:ip
* 請求的是哪個類方法:類方法
* 方法的參數
*/
ServletRequestAttributes attributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();//注意:選擇的HttpServletRequest選的是javax.servlet.http的
//url
logger.info("url={}",request.getRequestURL());
//method
logger.info("method={}",request.getMethod());
//ip
logger.info("ip={}",request.getRemoteAddr());
//getDeclaringTypeName():獲取類名,getName:獲取類方法
logger.info("class_method={}",joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
//方法的參數
logger.info("args={}",joinPoint.getArgs());
}
@After("log()")
public void doAfter(){
// System.out.println("我是方法執行之后被攔截");
logger.info("后日志信息");
}
/**獲取返回的內容:
* {
"id": 2,
"cupSize": "B",
"age": 19
}
*/
@AfterReturning(returning = "object",pointcut = "log()")
public void doAfterReturning(Object object){
logger.info("response={}",object.toString());
}
}
GirlController:
@RestController
public class GirlController {
/**
* getLogger方法中的參數與類名對應
*/
private final static Logger logger = LoggerFactory.getLogger(GirlController.class);
@Autowired
private GirlRepository girlRepository;
@Autowired
private GirlService girlService;
/**
* 查詢所有女生
* @return
*/
@GetMapping(value = "/girls")
public List<Girl> girlList(){
// System.out.println("我是girlList方法");
logger.info("我是girlList方法");
return girlRepository.findAll();
}
}
Girl:
@Entity //該注解表示該類在數據庫中有對應的表 不用創建該表
public class Girl {
@Id
@GeneratedValue
private Integer id;
private String cupSize;
/**
* 給年齡加上限制:年齡必須大于18歲
* value:值
* message:提示信息
*/
@Min(value =18,message = "年齡必須大于18歲")
private Integer age;
public Girl() {
}
@Override
public String toString() {
return "Girl{" +
"id=" + id +
", cupSize='" + cupSize + '\'' +
", age=" + 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;
}
}
7.jpg