AOP實現操作日志的記錄功能


參考

https://blog.csdn.net/chenxihua1/article/details/82703745

需求描述

在開發某系統時,遇到了這樣的一個需求:記錄該系統用戶的所有操作細節,只要鼠標點擊了界面,對數據庫進行了增刪改查操作,就必修記錄下來。而且這種記錄,不是給軟件維護者查閱的,是要給用戶查閱的。

這么看來,就不能夠直接記錄函數(方法)的名稱,必須要轉化成用戶看的懂的信息。

因為要添加到數據庫中,並且幾乎每個方法中都要記錄,直接來做的話工作量太大,而且還是和日志相關,自然而然就能想到了利用AOP來實現。

實現過程

自定義注解

因為要自定義方法的名稱,不能簡單的輸出原方法名,所以考慮到用自定義的注解來記錄需要暴露給用戶的名稱。

@Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Inherited //如果有子類,子類也可以獲取到該類的注解信息
@Documented public @interface Operation { String name(); }

使用的時候只要這么標注就可以了

@RestController @RequestMapping("/front/task") @Operation(name = "任務管理") public class TaskController { @PostMapping("/getTaskByPage") @Operation(name = "任務查詢") public RestResult getTaskListByPage(@RequestBody Map<String, Object> map){ RestResult result = new RestResult(); return result; } //任務執行
    @PostMapping("/taskExecute") @Operation(name = "任務執行") public RestResult doTaskAction(@Valid @RequestBody TaskDao taskDao) throws Exception { RestResult result = new RestResult(); return result; } }

AOP實現與注解解析

@Component @Aspect public class OperationAop { private static final Logger logger = LoggerFactory.getLogger(OperationAop.class); @Autowired OperationHistoryService operationHistoryService; @Pointcut("execution(* czams.front.controller.*.*(..))") public void method(){} @After("method()") public void after(JoinPoint joinPoint){ } @AfterReturning("method()") public void afterReturning(JoinPoint joinPoint){ //獲取httpServlet對象
        HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); //獲取本AOP管轄范圍內的字節碼對象 //?表示 extends Object
        Class<?>clazz = joinPoint.getTarget().getClass(); //獲取類名
        String controllerName = clazz.getName(); //如果該類標注了自定義的注解,則替換默認的名字
        if(clazz.isAnnotationPresent(Operation.class)){ controllerName = clazz.getAnnotation(Operation.class).name(); } //獲取方法名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); String methodName = method.getName(); //如果該方法標注了自定義的注解,則替換默認的名字
        if(method.isAnnotationPresent(Operation.class)){ methodName = method.getDeclaredAnnotation(Operation.class).name(); } //獲取參數,由於本項目只通過json傳參,所以取第一個就好
        Object[] args = joinPoint.getArgs(); String argName = "沒有條件"; if(args != null && args.length>0){ argName = args[0].toString(); } //保存到數據庫或者文件
        OperationHistoryDao operationHistoryDao = new OperationHistoryDao(); operationHistoryDao.setOperModule(controllerName); operationHistoryDao.setOperAction(methodName); operationHistoryDao.setOperDesc(argName); operationHistoryDao.setOperId(getRemoteHost(request)); System.out.println(operationHistoryDao.getOperAction()); operationHistoryService.addOperationHistory(operationHistoryDao); logger.info("ip為: "+getRemoteHost(request)+ "用戶執行了 "+controllerName +" 模塊下的 "+ methodName + " 條件為 "+ argName); } private String getRemoteHost(HttpServletRequest request) { // 獲取請求主機IP地址,如果通過代理進來,則透過防火牆獲取真實IP地址
        String ip = request.getHeader("X-Forwarded-For"); if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { ip = request.getRemoteAddr(); } } else if (ip.length() > 15) { String[] ips = ip.split(","); for (String s : ips) { if (!("unknown".equalsIgnoreCase((String) s))) { ip = s; break; } } } return ip; } }

部分業務相關的代碼可以忽略,大體流程如上。

另外,有時候controller包下面某些類或者說某些方法並不需要進行日志記錄,那么可以通過改變命名的方式來實現這樣的功能。

例如可以規定Controller結尾的才需要記錄日志:

 @Pointcut("execution(* czams.front.controller.*Controller.*(..))") public void method(){}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM