SpringBoot第二十五篇:SpringBoot與AOP
- 2019 年 10 月 15 日
- 筆記
作者:追夢1819
原文:https://www.cnblogs.com/yanfei1819/p/11457867.html
版權聲明:本文為部落客原創文章,轉載請附上博文鏈接!
引言
作者在實際項目中碰到一個問題,就是需要在系統中加入操作日誌功能。但是目前系統開發已經接近尾聲,功能介面達到一百幾十個。
如果按照新手的思維(項目組中有人就這樣提建議),但是這樣的話,工作量之大、冗餘程式碼之多,可想而知。對於這種需求,我們應該考慮到 Java 的面向切面編程思想,使用 Spring AOP。下面,闡述在 SpringBoot 中使用 AOP。
在項目中的使用
為了演示更加完整的功能,此處我將我在實際項目中的需求抽取出來:將整個系統中的操作記錄(比例數據的增、刪、改、登錄、退出等)入庫。
為了演示實際的應用場景,本處程式碼不做專門的 demo 處理。將本人在真實項目中的實際程式碼展示出來,以便讀者做更好的理解。
以下是核心程式碼:
package com.sunwin.aspect; import com.sunwin.common.ErrorCode; import com.sunwin.db.dao.ActionLogDao; import com.sunwin.db.dto.ActionLogDTO; import com.sunwin.exception.BusinessException; import com.sunwin.util.DateUtil; import com.sunwin.util.IPUtil; import com.sunwin.util.UserUtil; 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.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.text.SimpleDateFormat; import java.util.Date; /** * Created by 追夢1819 on 2019-07-08. */ @Aspect @Component public class LogAspect { @Autowired private ActionLogDao actionLogDao; private final static Logger logger = LoggerFactory.getLogger(LogAspect.class); // ..表示包及子包 該方法代表controller層的所有方法 @Pointcut( "execution(public * com.sunwin.web.controller.*.add*(..)) || " + "execution(public * com.sunwin.web.controller.*.insert*(..)) || " + "execution(public * com.sunwin.web.controller.*.update*(..)) || " + "execution(public * com.sunwin.web.controller.*.delete*(..))||" + // "execution(public * com.sunwin.web.controller.*.login*(..))"+ "execution(public * com.sunwin.web.controller.*.logout*(..))" ) public void controllerMethod() { } @Pointcut("execution(public * com.sunwin.web.controller.*.login*(..))") public void afterController() { } @After("afterController()") public void afterLogRequestInfo(JoinPoint joinPoint) throws Exception { common(joinPoint); } @Before("controllerMethod()") public void LogRequestInfo(JoinPoint joinPoint) throws Exception { common(joinPoint); } // aop業務處理邏輯 private void common(JoinPoint joinPoint) throws BusinessException { ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); StringBuffer requestLog = new StringBuffer(); requestLog.append("請求資訊:") .append("URL = {" + request.getRequestURI() + "},t") .append("HTTP_METHOD = {" + request.getMethod() + "},t") .append("IP = {" + request.getRemoteAddr() + "},t") .append("CLASS_METHOD = {" + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName() + "}t"); ActionLogDTO actionLog = new ActionLogDTO(); actionLog.setContent("調用資訊是:" + requestLog); String ip = IPUtil.getLocalIPAddress(); actionLog.setIp(ip); String user = UserUtil.getUser() == null ? "" : UserUtil.getUser(); actionLog.setOperator(user); SimpleDateFormat format = new SimpleDateFormat(DateUtil.DateFormat_yyyyMMddHHmmss); String now = format.format(new Date()); actionLog.setActionDate(now); String name = joinPoint.getSignature().getName(); // 類型、操作時間、操作者、內容、ip // 操作類型:1-登錄;2-退出;3-新增;4-編輯;5-刪除;99-未知 if (name.startsWith("delete")) { // 刪 actionLog.setActionType(5); } else if (name.startsWith("update")) { // 改 actionLog.setActionType(4); } else if (name.startsWith("add") || name.startsWith("insert")) { // 增 actionLog.setActionType(3); } else if (name.startsWith("login")) { actionLog.setActionType(1); } else if (name.startsWith("logout")) { actionLog.setActionType(2); } else { actionLog.setActionType(99); } int count = actionLogDao.insert(actionLog); if (count < 1) { throw new BusinessException(ErrorCode.INSERT_ERROR); } } }
以上是項目中的部門真實程式碼。讀者需要關注的有以下幾點:
- 註解:@Aspect、@Pointcut、@After、@Before;
- 攔截的方法命名規則要統一,比如新增時 addXXX 或者 insertXXX,更新是 updateXXX 或者 editXXX,查詢是 queryXXX 或者 selectXXX ,刪除是 deleteXXX 等;
- 處理相關的業務邏輯,比如,數據入庫,導出日誌文件等等;
- 需要理解 Spring aop 中的相關概念,比如切點、切面、增強/通知等。SpringBoot Aop 還是對 Spring Aop 的封裝。
下面是本項目中的日誌展示效果:
總結
本文希望傳到的不僅僅是一個解決方案,更希望傳達一種思想。在需求實現時,多思考,而不是在沒有思考的前提下,就橫衝直撞,胡寫一通。正如方騰飛所說,聰明人的幾個特質:
1、問題是什麼?
2、解決方案有哪些?
3、哪一個方案是所有方案中的捷徑?