AOP切面日志
最近在公司做项目的时候,需要实现甲方的一个需求,修改操作的记录。
XXX被谁修改为了XXX
这个看起来很简单,例如修改只要得到参数的实体类Vo然后进行记录就行,但是在进行修改记录和新增记录的时候就会有问题了,首先update方法是可以得到主键ID的,因为是updateById,而新增是进行主键自增的,而删除如果是记录之前的数据就需要根据ID先去查然后再去删除。
这三个其实对应的就是切面的三个顺序
@After @Around @Before
对于删除操作应该对应的是@Before需要在执行之间,先去完成查询,然后再由进程完成删除。
对应修改则应该使用@Before。
对应新增应该是@After操作,不然无法获得到Id。
其实AOP的注解还有另外两种。
- @Before: 前置通知, 在方法执行之前执行
- @After: 后置通知, 在方法执行之后执行 。
- @AfterRunning: 返回通知, 在方法返回结果之后执行
- @AfterThrowing: 异常通知, 在方法抛出异常之后
- @Around: 环绕通知, 围绕着方法执行
简单说一下我是如何实现日志记录的。
首先是需求:
由于在权限方面我是用了shiro进行管理,所以我可以通过shiro的subject获得到我需求的“人名”
String username = ((MemberEntity) SecurityUtils.getSubject().getPrincipal()).getName();
然后我需要获得到发生切面的具体模块,这边可以有很多方法,例如通过方法判断
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod();
我选择是的根据注解名字的不同来进行判断,然后通过具体的Key值得到我需要的value。
if ("新增合同".equals(syslog.value())) { //处理参数 Object[] args = joinPoint.getArgs(); //处理为Json字符串 String params = new Gson().toJson(args); //处理字符串的[] String replace = params.replace("[", ""); String s = replace.replace("]", ""); System.out.println(s); //处理为Json对象 JSONObject jsonObject = JSON.parseObject(s); //得到合同ID号 Object contractId = jsonObject.get("contractId"); //得到炼厂计划ID Object filiale_plan_id = jsonObject.get("filialePlanId"); Integer ID = Integer.valueOf(String.valueOf(filiale_plan_id)); //得到合同号 Object contracts = jsonObject.get("contracts"); //得到合同价格 Object agreement_price = jsonObject.get("agreementPrice"); //得到资源大体流向 Object resource_stream = jsonObject.get("resourceStream"); log.append(username).append("新增了合同,合同号为:").append(contracts).append(",合同价格为:").append(agreement_price); sysLog.setParams(log.toString()); sysLog.setFilialePlanId(ID); }
这样写的缺点就是无法复用,只能针对某一个去写,所以这个需求我一共写了388行代码.....很笨重,后续是肯定要修改这个方法的。
AOP只是一个概念并没有设定具体语言的实现,它能克服那些只有单继承特性语言的缺点。实现AOP的技术主要分为两类:一类是采用动态代理技术利用截取消息的方式,对消息进行装饰以取代原有对象行为的执行。另一类是采用静态织入的方式,引入特定语法创建切面,从而使编译器可以在编译期间织入相关的切面代码。
切面的使用:
1.切入点:对哪些方法进行拦截,拦截后怎样处理。应用通知进行增强的目标方法
2.切面:也就是具体的写代码的地方(例如日志模块)。是切入点和通知的结合
3.连接点:连接点是程序执行过程中明确的点,一般是类中方法的调用。连接点是程序在运行过程中能够插入切面的地点,比如方法调用、异常抛出、字段修改等。连接点就是可以应用通知进行增强的方法
案例代码:
@Aspect @Component public class SysLogAspect { @Pointcut("@annotation(io.ref.common.annotation.SysLog)") public void logPointCut() { } @Before("logPointCut()") public Object before(JoinPoint point) throws Throwable { long beginTime = System.currentTimeMillis(); //执行方法 Object result = point.getTarget(); //执行时长(毫秒) long time = System.currentTimeMillis() - beginTime; //保存日志 saveSysLog(point, time); return result; } private void saveSysLog(JoinPoint joinPoint, long time) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); SysLogEntity sysLog = new SysLogEntity(); SysLog syslog = method.getAnnotation(SysLog.class); if(syslog != null){ //注解上的描述 sysLog.setOperation(syslog.value()); } //获取request HttpServletRequest request = HttpContextUtils.getHttpServletRequest(); //设置IP地址 sysLog.setIp(IPUtils.getIpAddr(request)); //用户名 String username = ((MemberEntity) SecurityUtils.getSubject().getPrincipal()).getName(); sysLog.setName(username);
smartcat.994