面向切面編程
認識AOP
AOP(Aspect Oriented Program,面向切面編程)把業務功能分爲核心、非核心兩部分。
核心業務功能 | 非核心業務功能 |
---|---|
用戶登錄,增加數據,刪除數據 | 性能統計,日誌,事務管理 |
在Spring 的面向切面編程(AOP)思想裏,非核心業務功能被定義爲切面。核心業務功能和切面功能先被分別進行獨立開發,然後把切面功能和核心業務功能編織在一起,這就是AOP。
AOP中的概念
- 切入點(pointcut):在哪些類、哪些方法上切入
- 通知(advice):在方法前、方法後、方法前後做什麼
- 切面(aspect):切面=切入點+通知。也就是在什麼時機、什麼地方,做什麼
- 織入(weaving):把切面加入對象,並創建出代理對象的過程
- 環繞通知:AOP中最強大、靈活的通知,它集成了前置和後置通知,保留了連接點原有的方法。
實例:用AOP方式管理日誌
實驗結果
登錄網址
http://localhost:8080/aoptest
查看到
同時控制檯顯示信息
URL:http://localhost:8080/aoptest
HTTP方法:GET
IP地址:0:0:0:0:0:0:0:1
類的方法:com.example.demo.controller.AopLogController.aVoid
參數:null
應答值:hello aop test
費時:12
pom.xml添加依賴
這裏初學者踩坑:配置切面@Aspect不管用,是因爲沒有下面的spring-boot-starter-aop依賴
<!-- SpringBoot 攔截器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
代碼
編寫AOP日誌註解類
AopLog.java
下面代碼的解釋:
@Before:在切入點開始處切入內容
@After:在切入點結尾處切入內容
@AfterReturning:在切入點返回內容之後切入內容,可以用來對處理返回值做一些加工處理
@Around:在切入點前後切入內容,並控制何時執行切入點自身的內容
@AfterThrowing:用來處理當切入內容部分拋出異常之後的處理邏輯
@Aspect:標記爲切面類
@Component:把切面類加入IoC容器中,讓Spring來進行管理
package com.example.demo.aop;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;//這裏包含.Aspect
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web. context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.processing.SupportedSourceVersion;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
@Aspect//使之成爲切面類
@Component//把切面類加入loC容器中
public class AopLog {
private Logger logger=LoggerFactory.getLogger(this.getClass());
//線程局部的變量,用於解決多線程中相同變量的訪問衝突問題
ThreadLocal<Long> startTime=new ThreadLocal<>();
//定義切點
@Pointcut("execution(public * com.example..*.*(..))")
public void aopWebLog(){
}
@Before("aopWebLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable{
startTime.set(System.currentTimeMillis());
//接收到請求,記錄請求內容
ServletRequestAttributes attributes=(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request=attributes.getRequest();
//記錄下請求內容
logger.info("URL:"+request.getRequestURL().toString());
logger.info("HTTP方法:"+request.getMethod());
logger.info("IP地址:"+request.getRemoteAddr());
logger.info("類的方法:"+joinPoint.getSignature().getDeclaringTypeName()+"."+joinPoint.getSignature().getName());
logger.info("參數:"+request.getQueryString());
}
@AfterReturning(pointcut = "aopWebLog()",returning = "retObject")
public void doAfterReturning(Object retObject) throws Throwable{
//處理完請求,返回內容
logger.info("應答值:"+retObject);
logger.info("費時:"+(System.currentTimeMillis()-startTime.get()));
}
//方法拋出異常退出時執行的通知
@AfterThrowing(pointcut = "aopWebLog()",throwing = "ex")
public void addAfterThrowingLogger(JoinPoint joinPoint,Exception ex){
logger.error("執行 "+" 異常",ex);
}
}
2.控制器
AopLogController.java
package com.example.demo.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AopLogController {
@GetMapping("/aoptest")
public String aVoid(){
return "hello aop test";
}
}