您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關Spring AOP如何實現簡單的日志切面,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
AOP 的全稱為 Aspect Oriented Programming,譯為面向切面編程,是通過預編譯方式和運行期動態代理實現核心業務邏輯之外的橫切行為的統一維護的一種技術。AOP 是面向對象編程(OOP)的補充和擴展。 利用 AOP 可以對業務邏輯各部分進行隔離,從而達到降低模塊之間的耦合度,并將那些影響多個類的公共行為封裝到一個可重用模塊,從而到達提高程序的復用性,同時提高了開發效率,提高了系統的可操作性和可維護性。
在實際的 Web 項目開發中,我們常常需要對各個層面實現日志記錄,性能統計,安全控制,事務處理,異常處理等等功能。如果我們對每個層面的每個類都獨立編寫這部分代碼,那久而久之代碼將變得很難維護,所以我們把這些功能從業務邏輯代碼中分離出來,聚合在一起維護,而且我們能靈活地選擇何處需要使用這些代碼。
名詞 | 概念 | 理解 |
---|---|---|
通知(Advice) | 攔截到連接點之后所要執行的代碼,通知分為前置、后置、異常、最終、環繞通知五類 | 我們要實現的功能,如日志記錄,性能統計,安全控制,事務處理,異常處理等等,說明什么時候要干什么 |
連接點(Joint Point) | 被攔截到的點,如被攔截的方法、對類成員的訪問以及異常處理程序塊的執行等等,自身還能嵌套其他的 Joint Point | Spring 允許你用通知的地方,方法有關的前前后后(包括拋出異常) |
切入點(Pointcut) | 對連接點進行攔截的定義 | 指定通知到哪個方法,說明在哪干 |
切面(Aspect) | 切面類的定義,里面包含了切入點(Pointcut)和通知(Advice)的定義 | 切面就是通知和切入點的結合 |
目標對象(Target Object) | 切入點選擇的對象,也就是需要被通知的對象;由于 Spring AOP 通過代理模式實現,所以該對象永遠是被代理對象 | 業務邏輯本身 |
織入(Weaving) | 把切面應用到目標對象從而創建出 AOP 代理對象的過程。織入可以在編譯期、類裝載期、運行期進行,而 Spring 采用在運行期完成 | 切點定義了哪些連接點會得到通知 |
引入(Introduction ) | 可以在運行期為類動態添加方法和字段,Spring 允許引入新的接口到所有目標對象 | 引入就是在一個接口/類的基礎上引入新的接口增強功能 |
AOP 代理(AOP Proxy ) | Spring AOP 可以使用 JDK 動態代理或者 CGLIB 代理,前者基于接口,后者基于類 | 通過代理來對目標對象應用切面 |
AOP 是 Spring 框架中的一個核心內容。在 Spring 中,AOP 代理可以用 JDK 動態代理或者 CGLIB 代理 CglibAopProxy 實現。Spring 中 AOP 代理由 Spring 的 IOC 容器負責生成和管理,其依賴關系也由 IOC 容器負責管理。
注解 | 說明 |
---|---|
@Aspect | 將一個 java 類定義為切面類 |
@Pointcut | 定義一個切入點,可以是一個規則表達式,比如下例中某個 package 下的所有函數,也可以是一個注解等 |
@Before | 在切入點開始處切入內容 |
@After | 在切入點結尾處切入內容 |
@AfterReturning | 在切入點 return 內容之后處理邏輯 |
@Around | 在切入點前后切入內容,并自己控制何時執行切入點自身的內容 |
@AfterThrowing | 用來處理當切入內容部分拋出異常之后的處理邏輯 |
@Order(100) | AOP 切面執行順序, @Before 數值越小越先執行,@After 和 @AfterReturning 數值越大越先執行 |
其中 @Before、@After、@AfterReturning、@Around、@AfterThrowing 都屬于通知(Advice)。 |
<dependencies> <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-devtools</artifactId> <optional>true</optional> <!-- 這個需要為 true 熱部署才有效 --> </dependency> <!-- Spring AOP --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> </dependencies>
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface ControllerWebLog { String name();//所調用接口的名稱 boolean intoDb() default false;//標識該條操作日志是否需要持久化存儲 }
@Aspect @Component @Order(100) public class WebLogAspect { private static final Logger logger = LoggerFactory.getLogger(WebLogAspect.class); private ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>(); /** * 橫切點 */ @Pointcut("execution(public * cn.zwqh.springboot.controller..*.*(..))") public void webLog() { } /** * 接收請求,并記錄數據 * @param joinPoint * @param controllerWebLog */ @Before(value = "webLog()&& @annotation(controllerWebLog)") public void doBefore(JoinPoint joinPoint, ControllerWebLog controllerWebLog) { // 接收到請求 RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); // 記錄請求內容,threadInfo存儲所有內容 Map<String, Object> threadInfo = new HashMap<>(); logger.info("URL : " + request.getRequestURL()); threadInfo.put("url", request.getRequestURL()); logger.info("URI : " + request.getRequestURI()); threadInfo.put("uri", request.getRequestURI()); logger.info("HTTP_METHOD : " + request.getMethod()); threadInfo.put("httpMethod", request.getMethod()); logger.info("REMOTE_ADDR : " + request.getRemoteAddr()); threadInfo.put("ip", request.getRemoteAddr()); logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); threadInfo.put("classMethod", joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName()); logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs())); threadInfo.put("args", Arrays.toString(joinPoint.getArgs())); logger.info("USER_AGENT"+request.getHeader("User-Agent")); threadInfo.put("userAgent", request.getHeader("User-Agent")); logger.info("執行方法:" + controllerWebLog.name()); threadInfo.put("methodName", controllerWebLog.name()); threadLocal.set(threadInfo); } /** * 執行成功后處理 * @param controllerWebLog * @param ret * @throws Throwable */ @AfterReturning(value = "webLog()&& @annotation(controllerWebLog)", returning = "ret") public void doAfterReturning(ControllerWebLog controllerWebLog, Object ret) throws Throwable { Map<String, Object> threadInfo = threadLocal.get(); threadInfo.put("result", ret); if (controllerWebLog.intoDb()) { //插入數據庫操作 //insertResult(threadInfo); } // 處理完請求,返回內容 logger.info("RESPONSE : " + ret); } /** * 獲取執行時間 * @param proceedingJoinPoint * @return * @throws Throwable */ @Around(value = "webLog()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object ob = proceedingJoinPoint.proceed(); Map<String, Object> threadInfo = threadLocal.get(); Long takeTime = System.currentTimeMillis() - startTime; threadInfo.put("takeTime", takeTime); logger.info("耗時:" + takeTime); threadLocal.set(threadInfo); return ob; } /** * 異常處理 * @param throwable */ @AfterThrowing(value = "webLog()", throwing = "throwable") public void doAfterThrowing(Throwable throwable) { RequestAttributes ra = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes sra = (ServletRequestAttributes) ra; HttpServletRequest request = sra.getRequest(); // 異常信息 logger.error("{}接口調用異常,異常信息{}", request.getRequestURI(), throwable); } }
@RestController @RequestMapping("/user") public class UserController { @GetMapping("/getOne") @ControllerWebLog(name = "查詢", intoDb = true) public String getOne(Long id, String name) { return "1234"; } }
瀏覽器請求:http://127.0.0.1:8080/user/getOne?id=1&name=zwqh ,可以看到后臺日志輸出:
日志記錄只是一個簡單的示例,而 Spring AOP 的應用讓整個系統變的更加有條不紊,在其他場景應用也很強大。 它幫助我們降低模塊間耦合度,提高程序復用性,提高開發效率,提高系統可做性和可維護性。
以上就是Spring AOP如何實現簡單的日志切面,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。