最近做项目实现操作记录添加日志,由于aop这两种实现方式各有优缺点,所以都实现了一下以后根据具体业务选择。
1实现方式一注入:
1.1首先在xml中开启aop注入,需要引入的包此处省略,可百度自己查找。
<aop:aspectj-autoproxy />
1.2添加链接点
package com.oasis.wyvern.res.service.base.logService; import java.lang.annotation.*; @Target({ElementType.PARAMETER, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface SysSecureServiceLog { String description() default ""; }
1.3添加切入点,可以添加多个切入点同理也可添加多个链接点
package com.oasis.wyvern.res.service.base.logService; import com.oasis.wyvern.res.common.biz.enums.base.type.ActionType; import com.oasis.wyvern.res.common.biz.enums.base.type.ServiceAopType; import com.oasis.wyvern.res.common.biz.vo.record.oplog.BizOpLogVo; import com.oasis.wyvern.res.dao.biz.record.oplog.SecureOpLogDao; import com.oasis.wyvern.res.model.base.context.secure.SecurityContextHolder; import com.oasis.wyvern.res.model.biz.record.oplog.SecureOpLog; import com.oasis.wyvern.res.service.base.record.oplog.BizOpLogService; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; import javax.annotation.Resource; import java.lang.reflect.Method; import java.util.Arrays; @Aspect @Component public class Aop2Log { private static final Logger logger = LoggerFactory.getLogger(Aop2Log.class); @Resource private BizOpLogService bizOpLogVoService; @Resource private SecureOpLogDao secureOpLogDao; /** * 用户Service层切点 */ @Pointcut("@annotation(com.oasis.wyvern.res.service.base.logService.ServiceLog)") public void serviceAspect() { } /** * 系统安全层面切入点 */ @Pointcut("@annotation(com.oasis.wyvern.res.service.base.logService.SysSecureServiceLog)") public void sysSecureServiceAspect() { } /** * 用户service * @param point */ @AfterReturning("serviceAspect()") public void doAfter2Service(JoinPoint point){ try { String actor =""; if(SecurityContextHolder.getContext().getUser()!=null){ actor = SecurityContextHolder.getContext().getUser().getLoginName(); } String method = point.getSignature().getName(); String entity = point.getTarget().getClass().getSimpleName(); String args = getAvailableArgs(point.getArgs()); BizOpLogVo bizOpLogVo = new BizOpLogVo(); bizOpLogVo.setActor(actor); bizOpLogVo.setEntityType(entity.replace("ServiceImpl","")); bizOpLogVo.setAction(methodType(method)); bizOpLogVo.setUdf1(args.length()>200?args.substring(0,200):args); bizOpLogVo.setUdf2(method); bizOpLogVo.setUdf3(getServiceMthodDescription(point,"ServiceLog")); bizOpLogVoService.createBizOpLog(bizOpLogVo); } catch (Exception e) { logger.error("日志类异常"); logger.error(e.getMessage()); } } /** * 用户操作异常 * @param jp * @param e * @throws Exception */ @AfterThrowing(pointcut = "serviceAspect()", throwing = "e") public void afterThrowing2Service(JoinPoint jp,Throwable e) throws Exception{ logger.error("异常:"+jp.getTarget().getClass().getName()+"."+jp.getSignature().getName()); logger.error(e.getMessage()); } /** * 系统服务层操作 * @param point */ @AfterReturning("sysSecureServiceAspect()") public void doAfter2Sys(JoinPoint point){ try { String actor =""; if(SecurityContextHolder.getContext().getUser()!=null){ actor = SecurityContextHolder.getContext().getUser().getLoginName(); } String method = point.getSignature().getName(); String entity = point.getTarget().getClass().getSimpleName(); String args = getAvailableArgs(point.getArgs()); SecureOpLog secureOpLog = new SecureOpLog(); secureOpLog.setActor(actor); secureOpLog.setEntityType(entity.replace("ServiceImpl","")); secureOpLog.setAction(methodType(method)); secureOpLog.setUdf1(args.length()>200?args.substring(0,200):args); secureOpLog.setUdf2(method); secureOpLog.setUdf3(getServiceMthodDescription(point,"SysSecureServiceLog")); secureOpLogDao.insert(secureOpLog); } catch (Exception e) { logger.error("日志类异常"); logger.error(e.getMessage()); } } //在方法抛出异常是拦截 @AfterThrowing(pointcut = "sysSecureServiceAspect()", throwing = "e") public void afterThrowing2Sys(JoinPoint jp,Throwable e) throws Exception{ logger.error("异常:"+jp.getTarget().getClass().getName()+"."+jp.getSignature().getName()); logger.error(e.getMessage()); } private ActionType methodType(String method){ method = method.toLowerCase(); if(method.contains("create")||method.contains("insert")||method.contains("save")){ return ActionType.CREATE; }else if(method.contains("delete")){ return ActionType.DELETE; }else if(method.contains("edit")||method.contains("update")){ return ActionType.UPDATE; }else if(method.contains("frozen")){ return ActionType.FROZEN; }else if(method.contains("unfrozen")) { return ActionType.UNFROZEN; } else { return ActionType.SEARCH; } } private String getAvailableArgs(Object [] args){ String strArgs = Arrays.toString(args); String []params= strArgs.split(","); String argsStr=""; for (String arg:params){ if(arg.contains("[")&&!arg.endsWith(">")){ argsStr = arg.substring(arg.lastIndexOf("[")); }else if(!arg.contains("[")&&!arg.endsWith(">")){ argsStr+=","+arg; } } return argsStr; } private String getServiceMthodDescription(JoinPoint joinPoint ,String which) throws Exception { String targetName = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); Object[] arguments = joinPoint.getArgs(); Class targetClass = Class.forName(targetName); Method[] methods = targetClass.getMethods(); String description = ""; for (Method method : methods) { if (method.getName().equals(methodName)) { Class[] clazzs = method.getParameterTypes(); if (clazzs.length == arguments.length) { if(which.equals(ServiceAopType.ServiceLog.toString())) { description = method.getAnnotation(ServiceLog.class).description(); }else if(which.equals(ServiceAopType.SysSecureServiceLog.toString())){ description = method.getAnnotation(SysSecureServiceLog.class).description(); } break; } } } return description; } }
1.4在具体的方法上需要时添加链接点
@Override @SysSecureServiceLog(description= "delete user") public int deleteUser(Long userId){ int res = userDao.delete(userId); associateDao.deleteByAssoc(AssociateTable.ROLE_USER,userId); return res; }
2方式二通过xml声明配置实现:
2.1首先在xml配置如下:因为考虑到日志保存在操作异常或者事务回滚的情况下 操作日志不需要写入数据库,或者也需要回滚,故用了 配置的order顺序来解决。
<!--添加操作日志--> <bean id = "logs" class="com.oasis.wyvern.res.service.base.logService.Aop2Log"/> <aop:config > <aop:aspect ref="logs" order="3"> <!-- 定义切入点 --> <!-- aop包下的所有以Service结尾的类的方法 --> <aop:pointcut id="doMethod" expression="(execution(* com.oasis.wyvern.res.service.biz..*Service.save*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.create*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.insert*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.update*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.change*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.lock*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.unLock*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.reset*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.delete*(..)) )" /> <aop:after-returning method="doAfter" pointcut-ref="doMethod"/> </aop:aspect> </aop:config> <!--异常时--> <aop:config> <aop:aspect ref="logs" order="1"> <aop:pointcut id="throwinglog" expression="(execution(* com.oasis.wyvern.res.service.biz..*Service.save*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.create*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.insert*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.update*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.change*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.lock*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.unLock*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.reset*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.delete*(..)) )" /> <aop:after-throwing pointcut-ref="throwinglog" throwing="e" method="afterThrowing"/> </aop:aspect> </aop:config> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <tx:method name="save*" propagation="REQUIRED" isolation="READ_COMMITTED"/> <tx:method name="add*" propagation="REQUIRED" isolation="READ_COMMITTED"/> <tx:method name="update*" propagation="REQUIRED" isolation="READ_COMMITTED"/> <tx:method name="delete*" propagation="REQUIRED" isolation="READ_COMMITTED"/> <tx:method name="*"/> </tx:attributes> </tx:advice> <!--事务回滚--> <aop:config> <aop:pointcut id="serviceMethods" expression="(execution(* com.oasis.wyvern.res.service.biz..*Service.save*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.create*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.insert*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.update*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.change*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.lock*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.unLock*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.reset*(..)) or execution(* com.oasis.wyvern.res.service.biz..*Service.delete*(..)) )" /> <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceMethods" order="2"/> </aop:config>
2.2包路径图,别把切入点的类添加到连接点扫描中:execution表达式可百度,根据业务需要配置不同的拦截规则。
2.3切入点代码
package com.oasis.wyvern.res.service.base; import com.oasis.wyvern.res.common.biz.enums.base.type.ActionType; import com.oasis.wyvern.res.common.biz.vo.record.oplog.BizOpLogVo; import com.oasis.wyvern.res.model.base.context.secure.SecurityContextHolder; import com.oasis.wyvern.res.service.base.record.oplog.BizOpLogService; import org.aspectj.lang.JoinPoint; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import java.util.Arrays; public class Aop2Log { private static final Logger logger = LoggerFactory.getLogger(Aop2Log.class); @Autowired private BizOpLogService bizOpLogVoService; public void doAfter(JoinPoint point){ String actor =""; if(SecurityContextHolder.getContext().getUser()!=null){ actor = SecurityContextHolder.getContext().getUser().getLoginName(); } String method = point.getSignature().getName(); String entity = point.getTarget().getClass().getSimpleName(); String args = getAvailableArgs(Arrays.toString(point.getArgs())); BizOpLogVo bizOpLogVo = new BizOpLogVo(); bizOpLogVo.setActor(actor); bizOpLogVo.setEntityType(entity.replace("ServiceImpl","")); bizOpLogVo.setAction(methodType(method)); bizOpLogVo.setUdf1(args.length()>200?args.substring(0,200):args); bizOpLogVo.setUdf2(method); bizOpLogVoService.createBizOpLog(bizOpLogVo); } //在方法抛出异常是拦截 public void afterThrowing(JoinPoint jp,Throwable e) throws Exception{ logger.error("异常:"+jp.getTarget().getClass().getName()+"."+jp.getSignature().getName()); logger.error(e.getMessage()); } private ActionType methodType(String method){ method = method.toLowerCase(); if(method.contains("create")||method.contains("insert")||method.contains("save")){ return ActionType.CREATE; }else if(method.contains("delete")){ return ActionType.DELETE; }else if(method.contains("edit")||method.contains("update")){ return ActionType.UPDATE; }else if(method.contains("frozen")){ return ActionType.FROZEN; }else if(method.contains("unfrozen")) { return ActionType.UNFROZEN; } else { return ActionType.SEARCH; } } private String getAvailableArgs(String args){ String []params= args.split(","); String argsStr=""; for (String arg:params){ if(arg.contains("[")&&!arg.endsWith(">")){ argsStr = arg.substring(arg.lastIndexOf("[")); }else if(!arg.contains("[")&&!arg.endsWith(">")){ argsStr+=","+arg; } } return argsStr; } }
好了,到此为止两种方法完全介绍完整。