aop为系统添加操作日志,注入或配置声明的方式来实现

时间:2023-03-09 15:09:23
aop为系统添加操作日志,注入或配置声明的方式来实现

最近做项目实现操作记录添加日志,由于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表达式可百度,根据业务需要配置不同的拦截规则。

aop为系统添加操作日志,注入或配置声明的方式来实现

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;
}
}

好了,到此为止两种方法完全介绍完整。