利用 Spring AOP 的 @Aspect 與 execution 切點表達式,結合 Log4j 實現無侵入式業務日誌記錄。本文展示如何精準攔截 Service 層特定方法,記錄修改前後的實體快照與操作人信息。
execution(* com.example.service.impl..PromoteServiceImpl.updatePromote(..))
| 片段 | 含義 |
|---|---|
execution | 切點函數,匹配方法執行 |
* (第一個) | 返回類型任意 |
com.example.service.impl.. | 包路徑,.. 表示當前包及所有子包 |
PromoteServiceImpl | 目標類名 |
updatePromote | 目標方法名 |
(..) | 任意參數列表 |
@Component
@Aspect
public class ServiceAspect extends BaseAction<Object> {
private static final Logger log = Logger.getLogger(ServiceAspect.class);
@Autowired private PromoteService promoteService;
@Pointcut("execution(* com.example.service.impl..PromoteServiceImpl.updatePromote(..))")
public void updatePromotePointcut() {}
@Before("updatePromotePointcut()")
public void before(JoinPoint jp) {
Object[] args = jp.getArgs(); // [PromoteVO, Long promoteId]
Long id = (Long) args[1];
PromoteVO before = promoteService.queryPromoteDetail(id);
SessionInfo si = (SessionInfo) getSession().getAttribute("sessionInfo");
log.info("【修改活動】時間:{} 用戶:{} 賬號:{}", new Date(), si.getUserName(), si.getUserAcct());
log.info("修改前: {}", before);
}
@After("updatePromotePointcut()")
public void after(JoinPoint jp) {
Object[] args = jp.getArgs();
Long id = (Long) args[1];
PromoteVO after = promoteService.queryPromoteDetail(id);
log.info("修改後: {}", after);
}
}
<beans xmlns:aop="https://www.springframework.org/schema/aop" ...>
<aop:aspectj-autoproxy proxy-target-class="true" /> <!-- 支援類代理 -->
</beans>
JoinPoint.getArgs() 返回陣列,須按簽名順序強轉@Before 在事務開啟前,@AfterReturning 在提交後,若需事務內數據請用 @Around 手動控制try-catch 吞噬並記錄錯誤日誌@Around("updatePromotePointcut()")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
long start = System.currentTimeMillis();
try { return pjp.proceed(); }
finally { log.info("updatePromote 耗時: {}ms", System.currentTimeMillis() - start); }
}