記錄一下Spring AOP切入DAO層,進行數據監控。
在寫這個需求時,從網上找了不少的資料,大部分都是沒有解決相關問題的。固然也有少數部分解決,但用的仍是SSM架構的xml配置。一開始個人出發點是經過註解@annotation的方式來切入DAO須要監控的方法,可是並無用。接下來剖析下我的的實現和思路。java
在這裏只針對ADD、UPDATE、DELETE作相關數據處理。mysql
直接使用註解@annotation的方式是不能實現,因此我先經過execution的方式切到DAO層,再經過一個自定義註解區分數據操做的類型以及區分所操做的是哪一張表,具體詳情以下:git
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface AuditAction { /** * 操做類型 */ Action action() default Action.GET; /** * 目標table */ String targetTable() default ""; }
public enum Action { ADD, DELETE, UPDATE, GET }
@Mapper public interface CompanyDao { Company getCompanyByNum(int companyNum); @AuditAction(action = Action.ADD, targetTable = "company") int addCompany(Company company); @AuditAction(action = Action.UPDATE, targetTable = "company") int updateCompany(Company company); @AuditAction(action = Action.DELETE, targetTable = "company") int deleteCompany(int companyNum); }
@Slf4j @Aspect @Component public class SystemAudioAspect { // 省略相關業務代碼,詳細代碼請看博客末尾 }
/** * 攔截DELETE操做,記錄被刪除的數據 * @param joinPoint */ @Before(value = "execution(public * com.jtcoding.auditlog.dao..*.delete*(..))") public void doBefore(JoinPoint joinPoint) { // 獲取方法中的參數 Object[] args = joinPoint.getArgs(); // 獲取該方法上的 @AuditAction註解 AuditAction audioAction = this.getAudioActionByJoinPoint(joinPoint); if (audioAction != null && audioAction.action() == Action.DELETE) { Object obj = null; String targetTable = audioAction.targetTable(); switch (targetTable) { case "company": int companyNum = (int) args[0]; obj = companyService.getCompanyByNum(companyNum); break; case "plan": int planNum = (int) args[0]; obj = planService.getPlanByNum(planNum); break; } if (obj != null) { this.addAudioLog(obj, AuditLogDao.DELETE, targetTable, null); } } }
/** * 攔截ADD操做,記錄新增的數據 * @param joinPoint */ @After(value = "execution(public * com.jtcoding.auditlog.dao..*.add*(..))") public void doAfter(JoinPoint joinPoint) { // 獲取該方法上的 @AuditAction註解 AuditAction audioAction = this.getAudioActionByJoinPoint(joinPoint); if (audioAction != null && audioAction.action() == Action.ADD) { Object obj = joinPoint.getArgs()[0]; this.addAudioLog(obj, AuditLogDao.ADD, audioAction.targetTable(), null); } }
/** * 攔截UPDATE操做,記錄更新先後的數據 * @param pjp * @return * @throws Throwable */ @Around(value = "execution(public * com.jtcoding.auditlog.dao..*.update*(..))") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { AuditAction audioAction = this.getAudioActionByJoinPoint(pjp); Object proceed = null; if (audioAction != null && audioAction.action() == Action.UPDATE) { String uuid = UUID.randomUUID().toString(); Object originalObj = null; Object arg = pjp.getArgs()[0]; String targetTable = audioAction.targetTable(); switch (targetTable) { case "company": Company company = (Company) arg; originalObj = companyService.getCompanyByNum(company.getCompanyNum()); break; case "plan": Plan plan = (Plan) arg; originalObj = planService.getPlanByNum(plan.getPlanNum()); break; } AuditLog auditLog = null; if (originalObj != null) { // TODO 在執行原方法以前,記錄舊數據 auditLog = this.addAudioLog(originalObj, AuditLogDao.UPDATE, targetTable, null); } // 執行原方法 proceed = pjp.proceed(); // TODO 在執行原方法以後,記錄新數據 if (auditLog != null) { this.addAudioLog(arg, AuditLogDao.UPDATE, targetTable, auditLog.getLogNum()); } } if (proceed == null) { return pjp.proceed(); } return proceed; }
至此,有關切面核心的邏輯已經代碼已經完成,相關Service和Controller代碼,請看這裏(源碼)web
具體請求以下圖spring
新增Companysql
刪除Company數據庫
獲取某一個Companymybatis
修改Company架構
數據庫數據app
以上是我的的思路實現,有不對或者須要優化之處,請指出,謝謝。