Spring-Aop处理记录日志

AOP的几种通知类型:

1,前置通知(Before advice):在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行
    配置文件中使用 <aop:before>进行声明
    注解使用 @Before 进行声明

2,后置通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。
    配置文件中使用<aop:after>进行声明
    注解使用 @After 进行声明

3,返回后通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。
    配置文件中使用<after-returning>进行声明
    注解使用 @AfterReturning 进行声明

4,环绕通知(Around advice):包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。
  可以在方法的调用前后完成自定义的行为,也可以选择不执行。
    配置文件中使用<aop:around>进行声明
    注解使用 @Around 进行声明

5,抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。
    配置文件中使用<aop:after-throwing>进行声明
    注解使用 @AfterThrowing 进行声明

需要先引入相关jar包,
如果用Maven则在pom文件中加入下面配置,此时Maven会自动导入相关jar包

    <!-- 引入AOP所需要的jar包 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.7.2</version>
        </dependency>

Spring的applicationContext.xml 文件中的配置

这三个是aop的
xmlns:aop=”http://www.springframework.org/schema/aop

http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:task="http://www.springframework.org/schema/task"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:util="http://www.springframework.org/schema/util"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/task
           http://www.springframework.org/schema/task/spring-task-3.0.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
           http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util-3.0.xsd">

    <!---注解配置开始  如果使用注解配置Aop则需要配置这两个参数 -->

    <!-- 激活组件扫描功能,在包cn.ysh.studio.spring.aop及其子包下面自动扫描通过注解配置的组件 -->
    <!--
    <context:component-scan base-package="cn.ysh.studio.spring.aop"/>
    -->
    <!-- 激活自动代理功能 -->
    <!--
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    -->
    <!---注解配置结束 -->




    <aop:config>
            <!-- 声明一个切面,并注入切面Bean,相当于@Aspect -->
          <aop:aspect id="myAop" ref="auditLogAop">

             <!--配置一个切点,相当于@Pointcut

            *  表示通配所有类型返回值或则没有返回值都可以
            com.unionpay.techjoin.common.service..*.*(..)
            表示com.unionpay.techjoin.common.service包下所以类  
            (..)表示所有参数类型
            -->
             <aop:pointcut id="myAuditLog" expression="execution(* com.unionpay.techjoin.common.service..*.*(..))" />

             <!--配置环绕通知 相当于@Around-->
            <aop:around method="addAuditLog" pointcut-ref="myAuditLog"/>
         </aop:aspect>
    </aop:config>

如果需要通知中获取Request对象,则需要在web.xml中加入如下配置

<listener>
        <listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>

日志实体类

package com.unionpay.techjoin.common.domain;

import java.io.Serializable;
import java.sql.Timestamp;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

/**
 * 审计日志实体类
 * @author mszhou
 *
 */
@Entity
@Table(name = "tbl_tjmgm_audit_log")
public class AuditLog implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    /**主键id*/
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    /**类别 01表示产品 02表示解决方案 03表示API 04表示FAQ 05表示文档*/
    @Column(name = "target_type")
    private String targetType;

    /**关联id  */
    @Column(name = "target_id")
    private Integer targetId;

    /**该操作简单的简单描述 如 “admin修改产品”  */
    @Column(name = "operate_content")
    private String operateContent;

    /**操作类别 01表示新增 02表示修改 03表示删除*/
    @Column(name = "operate_type")
    private String operateType;

    /**操作人Id*/
    @Column(name = "operator_id")
    private String operatorId;

    /** 操作时间 */
    @Column(name = "operate_ts")
    private Timestamp operateTs;

    /**操作人的IP*/
    @Column(name = "operator_ip")
    private String operatorIp;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTargetType() {
        return targetType;
    }

    public void setTargetType(String targetType) {
        this.targetType = targetType;
    }

    public Integer getTargetId() {
        return targetId;
    }

    public void setTargetId(Integer targetId) {
        this.targetId = targetId;
    }

    public String getOperateContent() {
        return operateContent;
    }

    public void setOperateContent(String operateContent) {
        this.operateContent = operateContent;
    }

    public String getOperateType() {
        return operateType;
    }

    public void setOperateType(String operateType) {
        this.operateType = operateType;
    }

    public String getOperatorId() {
        return operatorId;
    }

    public void setOperatorId(String operatorId) {
        this.operatorId = operatorId;
    }

    public Timestamp getOperateTs() {
        return operateTs;
    }

    public void setOperateTs(Timestamp operateTs) {
        this.operateTs = operateTs;
    }

    public String getOperatorIp() {
        return operatorIp;
    }

    public void setOperatorIp(String operatorIp) {
        this.operatorIp = operatorIp;
    }

}

日志Dao类

package com.unionpay.techjoin.common.dao;

import java.io.Serializable;
import org.springframework.stereotype.Repository;
import com.unionpay.techjoin.common.domain.AuditLog;
/**
 * 审计日志Dao
 * @author mszhou
 */
@Repository
public class AuditLogDao  extends HibernateBaseDao<AuditLog,Serializable> {

}

日志业务类

package com.unionpay.techjoin.common.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.unionpay.techjoin.common.dao.AuditLogDao;
import com.unionpay.techjoin.common.domain.AuditLog;

@Service
@Transactional
public class AuditLogService {

    @Autowired
    private AuditLogDao auditLogDao;

    /**
     * 新增
     * @param auditLog
     * @author mszhou
     */
    public void add(AuditLog auditLog){
        auditLogDao.save(auditLog);
    }

}

切面类


package com.unionpay.techjoin.admin.controller;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.unionpay.common.authorization.user.UserDetail;
import com.unionpay.techjoin.admin.utils.UserInfoUtil;
import com.unionpay.techjoin.common.domain.AuditLog;
import com.unionpay.techjoin.common.service.AuditLogService;


@Component(value="auditLogAop")//实例化注解 
public class AuditLogAop {

    @Autowired
    private AuditLogService auditLogService;

    /*
     *(环绕通知)  -日志切点
     * 参数 JoinPoint joinPoint
     * ProceedingJoinPoint是JoinPoint的子类,在环绕通知中用到
     * 其它通知则直接用JoinPoint类型参数
     * 
     * 环绕通知也可以这样写
     * ((ProceedingJoinPoint) joinPoint).proceed();
     */
    @SuppressWarnings("finally")
    public Object addAuditLog(ProceedingJoinPoint pjp) throws Throwable{

        Object obj=pjp.proceed();//执行方法
        //========生成日志开姿============
        try{
            //得到Request对象
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            if(request!=null){
                //pjp.getTarget().getClass().getName()得到请求的Service类全名(包名.类名)
                //调用方法getTargetType得到类型01表示产品 02表示解决方案 03表示API 04表示FAQ 05表示文档
                String targetType =getTargetType(pjp.getTarget().getClass().getName());

                //pjp.getSignature().getName()得到请求的方法名
                //调用getOperateType方法得到操作类型 01表示新增 02表示修改 03表示删除
                String operateType=getOperateType(pjp.getSignature().getName());//方法 

                if(targetType!=null&&!"".equals(targetType)&&operateType!=null&&!"".equals(operateType)){

                    //获取当前用户,这里调用了UserInfoUtil工具类
                    UserDetail user= UserInfoUtil.getUserInfo(request);
                    AuditLog auditLog=new AuditLog();//创建审计日志对象
                    auditLog.setTargetType(targetType);//设置请求类别

                    //设置关联Id,这里调用了下面getpArgs方法得到id
                    auditLog.setTargetId(getpArgs(pjp,operateType));

                    //设置简单操作描述,这里调用了下面getOperateContent方法获得                         
              auditLog.setOperateContent(getOperateContent(user.getUserId(),targetType,operateType));

                    auditLog.setOperateType(operateType);//设置操作类别
                    auditLog.setOperatorId(user.getUserId());//设置操作人id
                    auditLog.setOperateTs(new Timestamp(new Date().getTime()));//设置操作时间

                    //获取请求ip
                    String ip = request.getRemoteAddr();
                    auditLog.setOperatorIp(ip);//设置操作人ip
                    auditLogService.add(auditLog);//保存审计日志
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            return obj;
        }
        //=======生成日志结束==========

    }

    /**
     * 转换请求类型 01表示产品 02表示解决方案 03表示API 04表示FAQ 05表示文档
     * @param className
     * @return
     */
    private String getTargetType(String className){
        String returnType=null;
        if(className!=null){
            String name=className.substring(className.lastIndexOf(".")+1);
            if(name!=null){
                if("ProductService".equals(name)){//01 产品
                    returnType="01";
                }else if("SolutionService".equals(name)){//02 解决方案
                    returnType="02";
                }else if("APIInfoService".equals(name)){//03 API
                    returnType="03";
                }else if("FaqService".equals(name)){//04 FAQ
                    returnType="04";            
                }else if("FileInfoService".equals(name)){//05 文档
                    returnType="05";        
                }
            }
        }
        return returnType;
    }

    /**
     * 转换操作类别  01表示新增 02表示修改 03表示删除
     * @param methodName
     * @return
     */
    private String getOperateType(String methodName){
        String returnType=null;
        if(methodName!=null){
            if(methodName.startsWith("add")||methodName.startsWith("save")){//01 新增
                returnType="01";
            }else if(methodName.startsWith("upd")||methodName.startsWith("modify")){//02 修改
                returnType="02";
            }else if(methodName.startsWith("del")){//03 删除
                returnType="03";
            }
        }
        return returnType;
    }

    /**
     * 获取所有请求参敿
     * @param pjp
     * @return
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
     */
    private Integer getpArgs(ProceedingJoinPoint pjp,String operateType) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
        Integer id=0;
        Object[] objs=pjp.getArgs();//得到所有参数
        if(objs==null||objs.length<=0){
            return id;
        }
        //判断如果是删除,则直接取参数中的第一个参数表示id,
        //否则通过反射判断对象中是否有getId方法,
        //如果有则通过反射调用该方法得到对应的id
        //例如 是新增产品,则得到的是产品id
        if("03".equals(operateType)){//是删除
            Object obj = objs[0];//获取第一个参数
            if(obj!=null){//判断不为空
                 String strNum=obj.toString();//转String

                 if(strNum!=null&&!"".equals(strNum)){
                    //将id转换成Integer类型
                    id=new Integer(strNum);
                 }
            }
        }else{//不是删除,是增加或修改
            for (Object info : objs) {//遍历参数对象  

               //获取该参数对象的所有方法 
               Method[] methods = info.getClass().getDeclaredMethods(); 
                //遍历所有方法
                for (Method method : methods){
                    //得到方法名称
                    String methodName = method.getName();  
                    //判断是否为getId方法
                    if (methodName!=null&&methodName.endsWith("getId")) {
                        //调用该方法
                        Object obj = method.invoke(info);
                        //返回值不能空
                        if(obj!=null){
                            //转String
                             String strNum=obj.toString();
                             if(strNum!=null&&!"".equals(strNum)){
                                //将id转换成Integer类型
                                id=new Integer(strNum);
                             }
                        }
                    }  
                }   
            } 
        }

        return id;//返回id
    }

    /**
     * 转换简单的操作描述
     * @param targetType
     * @param operateType
     * @return
     */
    private String getOperateContent(String userName,String targetType,String operateType){
        String result=userName;
        if("01".equals(operateType)){//01 新增
            result+="新增";
        }else if("02".equals(operateType)){//02 修改
            result+="修改";
        }else if("03".equals(operateType)){//03 删除
            result+="删除";
        }

        if("01".equals(targetType)){
            result+="产品";
        }else if("02".equals(targetType)){//02
            result+="解决方案";
        }else if("03".equals(targetType)){//03 
            result+="API";
        }else if("04".equals(targetType)){//04 
            result+="FAQ";          
        }else if("05".equals(targetType)){//05
            result+="文档";   
        }
        return result;
    }

}

注解方式配置切面类

package com.unionpay.techjoin.admin.controller;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.sql.Timestamp;
import java.util.Date;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import com.unionpay.common.authorization.user.UserDetail;
import com.unionpay.techjoin.admin.utils.UserInfoUtil;
import com.unionpay.techjoin.common.domain.AuditLog;
import com.unionpay.techjoin.common.service.AuditLogService;
import org.aspectj.lang.annotation.Aspect


@Component(value="auditLogAop")//实例化注解 
@Aspect//声明这是一个切面Bean
public class AuditLogAop {

    @Autowired
    private AuditLogService auditLogService;

    //配置切入点,该方法无方法体,主要为方便同类中其他方法使用此处配置的切入点
    //aspect 该方法名自己取的
    @Pointcut("execution(* com.unionpay.techjoin.common.service..*.*(..))")
    public void aspect(){   }

    /*
     * (环绕通知)  -日志切点
     * 参数 JoinPoint joinPoint
     * ProceedingJoinPoint是JoinPoint的子类,在环绕通知中用到
     * 其它通知则直接用JoinPoint类型参数
     * 
     * 环绕通知也可以这样写
     * ((ProceedingJoinPoint) joinPoint).proceed();
     */
    @SuppressWarnings("finally")
    @Around("aspect()")//配置环绕通知,使用在方法aspect()上注册的切入点
    public Object addAuditLog(ProceedingJoinPoint pjp) throws Throwable{

        Object obj=pjp.proceed();//执行方法
        //========生成日志开姿============
        try{
            //得到Request对象
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            if(request!=null){
                //pjp.getTarget().getClass().getName()得到请求的Service类全名(包名.类名)
                //调用方法getTargetType得到类型01表示产品 02表示解决方案 03表示API 04表示FAQ 05表示文档
                String targetType =getTargetType(pjp.getTarget().getClass().getName());

                //pjp.getSignature().getName()得到请求的方法名
                //调用getOperateType方法得到操作类型 01表示新增 02表示修改 03表示删除
                String operateType=getOperateType(pjp.getSignature().getName());//方法 

                if(targetType!=null&&!"".equals(targetType)&&operateType!=null&&!"".equals(operateType)){

                    //获取当前用户,这里调用了UserInfoUtil工具类
                    UserDetail user= UserInfoUtil.getUserInfo(request);
                    AuditLog auditLog=new AuditLog();//创建审计日志对象
                    auditLog.setTargetType(targetType);//设置请求类别

                    //设置关联Id,这里调用了下面getpArgs方法得到id
                    auditLog.setTargetId(getpArgs(pjp,operateType));

                    //设置简单操作描述,这里调用了下面getOperateContent方法获得                         
              auditLog.setOperateContent(getOperateContent(user.getUserId(),targetType,operateType));

                    auditLog.setOperateType(operateType);//设置操作类别
                    auditLog.setOperatorId(user.getUserId());//设置操作人id
                    auditLog.setOperateTs(new Timestamp(new Date().getTime()));//设置操作时间

                    //获取请求ip
                    String ip = request.getRemoteAddr();
                    auditLog.setOperatorIp(ip);//设置操作人ip
                    auditLogService.add(auditLog);//保存审计日志
                }
            }
        }catch(Exception e){
            e.printStackTrace();
        }finally{
            return obj;
        }
        //=======生成日志结束==========

    }

    /**
     * 转换请求类型 01表示产品 02表示解决方案 03表示API 04表示FAQ 05表示文档
     * @param className
     * @return
     */
    private String getTargetType(String className){
        String returnType=null;
        if(className!=null){
            String name=className.substring(className.lastIndexOf(".")+1);
            if(name!=null){
                if("ProductService".equals(name)){//01 产品
                    returnType="01";
                }else if("SolutionService".equals(name)){//02 解决方案
                    returnType="02";
                }else if("APIInfoService".equals(name)){//03 API
                    returnType="03";
                }else if("FaqService".equals(name)){//04 FAQ
                    returnType="04";            
                }else if("FileInfoService".equals(name)){//05 文档
                    returnType="05";        
                }
            }
        }
        return returnType;
    }

    /**
     * 转换操作类别   01表示新增 02表示修改 03表示删除
     * @param methodName
     * @return
     */
    private String getOperateType(String methodName){
        String returnType=null;
        if(methodName!=null){
            if(methodName.startsWith("add")||methodName.startsWith("save")){//01 新增
                returnType="01";
            }else if(methodName.startsWith("upd")||methodName.startsWith("modify")){//02 修改
                returnType="02";
            }else if(methodName.startsWith("del")){//03 删除
                returnType="03";
            }
        }
        return returnType;
    }

    /**
     * 获取所有请求参敿
     * @param pjp
     * @return
     * @throws InvocationTargetException 
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
     */
    private Integer getpArgs(ProceedingJoinPoint pjp,String operateType) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException{
        Integer id=0;
        Object[] objs=pjp.getArgs();//得到所有参数
        if(objs==null||objs.length<=0){
            return id;
        }
        //判断如果是删除,则直接取参数中的第一个参数表示id,
        //否则通过反射判断对象中是否有getId方法,
        //如果有则通过反射调用该方法得到对应的id
        //例如 是新增产品,则得到的是产品id
        if("03".equals(operateType)){//是删除
            Object obj = objs[0];//获取第一个参数
            if(obj!=null){//判断不为空
                 String strNum=obj.toString();//转String

                 if(strNum!=null&&!"".equals(strNum)){
                    //将id转换成Integer类型
                    id=new Integer(strNum);
                 }
            }
        }else{//不是删除,是增加或修改
            for (Object info : objs) {//遍历参数对象  

               //获取该参数对象的所有方法 
               Method[] methods = info.getClass().getDeclaredMethods(); 
                //遍历所有方法
                for (Method method : methods){
                    //得到方法名称
                    String methodName = method.getName();  
                    //判断是否为getId方法
                    if (methodName!=null&&methodName.endsWith("getId")) {
                        //调用该方法
                        Object obj = method.invoke(info);
                        //返回值不能空
                        if(obj!=null){
                            //转String
                             String strNum=obj.toString();
                             if(strNum!=null&&!"".equals(strNum)){
                                //将id转换成Integer类型
                                id=new Integer(strNum);
                             }
                        }
                    }  
                }   
            } 
        }

        return id;//返回id
    }

    /**
     * 转换简单的操作描述
     * @param targetType
     * @param operateType
     * @return
     */
    private String getOperateContent(String userName,String targetType,String operateType){
        String result=userName;
        if("01".equals(operateType)){//01 新增
            result+="新增";
        }else if("02".equals(operateType)){//02 修改
            result+="修改";
        }else if("03".equals(operateType)){//03 删除
            result+="删除";
        }

        if("01".equals(targetType)){
            result+="产品";
        }else if("02".equals(targetType)){//02
            result+="解决方案";
        }else if("03".equals(targetType)){//03 
            result+="API";
        }else if("04".equals(targetType)){//04 
            result+="FAQ";          
        }else if("05".equals(targetType)){//05
            result+="文档";   
        }
        return result;
    }

}
上一篇:如何访问 redis 中的海量数据?避免事故产生


下一篇:数据治理——企业数字化转型的基石