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

Posted 挖坑大王

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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表达式可百度,根据业务需要配置不同的拦截规则。

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

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

 

以上是关于aop为系统添加操作日志,注入或配置声明的方式来实现的主要内容,如果未能解决你的问题,请参考以下文章

Solon Aop 特色开发注入或手动获取Bean

Spring的几道选择题

AOP 面向切面编程

AOP之AspectJ - 代码注入

AOP之AspectJ - 代码注入

AOP 日志切面