背景
现有项目有两类日志系统,一个是logback日志,一个是框架层面自己实现的对特定库表数据增删改查操作的日志。但是对于前台的用于生成文档、或是生成库表数据的按钮并没有加以控制,无法得知操作人员进行了哪些操作。项目经理要求加这么个通用模块,也的确很有必要。
思路
- 对于这个通用日志模块,首先肯定是要设计一个库表,自行设计即可。用户、模块、方法功能、参数列表、参数值、时间戳等等。
- 采用何种方式去实现该功能?系统现有的两个日志框架,对于这个通用日志模块的开发,并无太大用处。需要另辟蹊径。
- 实现该功能应该注意哪些?由于是对原有系统功能加操作日志,首先不能影响原有功能的实现,其次不能对原有代码造成过大的侵入性(这也是我拒绝在原有代码中进行将操作日志插入库表的原因),还有不能影响效率等等。
- 考虑到这,我的选择是AOP,因为切面的方式可以完美解决上述考虑。
- 但是,我们对哪些操作需要进行记录操作日志是有选择的,考虑到项目比较大,总不能指定所有的包吧?所以我选择了使用自定义注解的方式。
- 最终,自定义注解+AOP的实现方式被我采纳。
实现
自定义注解:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| package com.born2do.emsys.annotation;
import java.lang.annotation.*;
@Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface LogAnnotation {
String module();
String function();
String remark() default ""; }
|
切面:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95
| package com.born2do.emsys.aop;
import com.born2do.emsys.annotation.LogAnnotation; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; import org.springframework.context.annotation.EnableAspectJAutoProxy; import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
@Aspect @Component @EnableAspectJAutoProxy(proxyTargetClass = true) public class LogAnnotationAspect { @Pointcut("@annotation(com.born2do.emsys.annotation.LogAnnotation)") public void pointcutConfig() { }
@Before("pointcutConfig()") public void doBefore(JoinPoint joinPoint) { System.out.println("前置通知--方法前执行" + joinPoint); }
@After("pointcutConfig()") public void doAfter(JoinPoint joinPoint) { System.out.println("后置通知--方法后执行" + joinPoint); }
@AfterReturning("pointcutConfig()") public void doAfterReturning(JoinPoint joinPoint) { System.out.println("返回通知--调用获得返回值后执行" + joinPoint); }
@AfterThrowing("pointcutConfig()") public void doAfterThrowing(JoinPoint joinPoint) { System.out.println("异常通知--抛出异常后执行" + joinPoint); }
@Around("pointcutConfig()") public Object doAround(ProceedingJoinPoint pjp) { Object result = null;
Class targetClass = pjp.getTarget().getClass(); Method[] methods = targetClass.getMethods(); String methodName = pjp.getSignature().getName(); Object[] argsValue = pjp.getArgs();
for (Method method : methods) { if (method.getName().equals(methodName)) { LogAnnotation logAnnotation = method.getAnnotation(LogAnnotation.class); String module = logAnnotation.module(); String function = logAnnotation.function(); String remark = logAnnotation.remark();
Object[] param = method.getParameters(); } }
System.out.println("前置通知:目标类名:" + targetClass.getName());
try { result = pjp.proceed();
System.out.println("返回通知:目标方法名" + methodName + ",返回结果为:" + result); } catch (Throwable e) { System.out.println("异常通知:目标方法名" + methodName + ",异常为:" + e.getMessage()); }
System.out.println("后置通知:目标方法名" + methodName);
return result; } }
|
测试类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| package com.born2do.emsys.controller;
import com.born2do.emsys.annotation.LogAnnotation; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController;
@RestController public class TestController {
@LogAnnotation(module = "测试模块", function = "用于测试SpringMVC项目框架是否搭建成功") @RequestMapping("/test") public String test(@RequestParam("id") int id) { System.out.println("进入 /test 路径"); return "OK"; }
}
|
通用日志功能的实现主要依靠于前两个类,当然,在目标方法上添加自定义注解也很重要。
如想运行上述代码,还请自行新建SpringBoot项目,将代码嵌入项目中。
测试结果
浏览器访问 localhost:50000/test?id=1
控制台输出如下日志:
1 2 3 4 5 6 7
| 前置通知:目标类名:com.born2do.emsys.controller.TestController 前置通知--方法前执行execution(String com.born2do.emsys.controller.TestController.test(int)) 进入 /test 路径 返回通知--调用获得返回值后执行execution(String com.born2do.emsys.controller.TestController.test(int)) 后置通知--方法后执行execution(String com.born2do.emsys.controller.TestController.test(int)) 返回通知:目标方法名test,返回结果为:OK 后置通知:目标方法名test
|