AOP 记录日志

该博客展示了如何使用Spring AOP进行日志记录,特别是在更新审核流程时。通过自定义注解`@FieldInterface`来标识字段,并在后置通知中遍历对象字段,获取并打印注解值,实现变更详情的记录。此外,还涉及到不同类型的DTO转换为DO的过程,用于日志入库。
package com.ipr.audit.aop;

import java.lang.annotation.*;


/**
 * @author xnALPHA
 */
@Documented
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)

public @interface FieldInterface {



    String name() default "";

}
public class ProcessFormDTO implements Serializable{

    private static final long serialVersionUID = -8689138748150278713L;

    private Long id;
    /**
     * 申请流程名称
     */
    @FieldInterface(name = "申请流程名称")
    private String processName;

    /**
     * 申请流程说明
     */
    @FieldInterface(name = "申请流程说明")
    private String description;

    /**
     * 申请流程名称
     */
    @FieldInterface(name = "申请流程名称")
    private Long categoryId;

    /**
     * 审核类型名字
     */
    @FieldInterface(name = "审核类型名字")
    private String categoryName;

    /**
     * 审核流程说明(0 普通 1交互)
     */
    @FieldInterface(name = "审核流程说明")
    private Byte processType;

    /**
     * 创建人id
     */
    @FieldInterface(name = "创建人id")
    private Long createManager;

    /**
     * 创建人名称
     */
    @FieldInterface(name = "创建人名称")
    private String createManagerName;

    /**
     * 归属人id
     */
    @FieldInterface(name = "归属人id")
    private Long ownerManager;

    /**
     * 归属人名称
     */
    @FieldInterface(name = "归属人名称")
    private String ownerManagerName;

    /**
     * 归属人组织id
     */
    @FieldInterface(name = "归属人组织id")
    private Long ownerGroup;

    /**
     * 归属人组织名称
     */
    @FieldInterface(name = "归属人组织名称")
    private String ownerGroupName;
}
package com.ipr.audit.aop;

import com.alibaba.fastjson.JSON;
import com.ipr.audit.dto.process.ProcessFormDTO;
import com.ipr.dataobject.AuditProcessDO;
import com.ipr.mapper.IAuditProcessMapper;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 审核流程日志记录
 *
 * @author xnALPHA
 */
@Aspect
@Component
public class AuditLog {

    private Logger logger = LoggerFactory.getLogger(AuditLog.class);

    @Resource
    private IAuditProcessMapper auditProcessMapperSlave;

    /**
     * 日志记录切入点
     */
    @Pointcut("execution(* com.ipr.biz.impl.ProcessBizImpl.updateProcess(..))")
    public void doLog() {
    }

    /**
     * 后置通知
     */
    @AfterReturning("doLog()")
    public void after(JoinPoint point) throws Exception {
        StringBuffer changeDetail = new StringBuffer();
        Object[] args = point.getArgs();
        ProcessFormDTO dto = (ProcessFormDTO) args[0];
        AuditProcessDO originalAuditProcess = auditProcessMapperSlave.getAuditProcessByProcessId(dto.getId());

        Class<? extends ProcessFormDTO> cls = dto.getClass();
        Field[] fields = cls.getDeclaredFields();
        for (Field f : fields) {
            f.setAccessible(true);
            String fName = f.getName();
            Object fValue = f.get(dto);
            // 判断是否方法上存在注解
            boolean annotationPresent = f.isAnnotationPresent(FieldInterface.class);
            if (annotationPresent && fValue != null) {
                System.out.println("属性名:" + fName + " 属性值:" + fValue);
                FieldInterface fieldDescription = f.getAnnotation(FieldInterface.class);
                // 获取注解值
                String nameStr = fieldDescription.name();
                System.out.println(nameStr);
            }
        }
//        Method[] methods = cls.getDeclaredMethods();
//        for (Method str : methods) {
//            // 判断是否方法上存在注解
//            boolean annotationPresent = str.isAnnotationPresent(FieldInterface.class);
//            if (annotationPresent) {
//                // 获取自定义注解对象
//                FieldInterface fieldInterface = str.getAnnotation(FieldInterface.class);
//                // 根据对象获取注解值
//                String isNotNullStr = fieldInterface.name();
//                System.out.println("------------------" + isNotNullStr);
//            }
//        }

        // todo 根据方法不同,入mongo库
        System.out.println("==============监听到更新流程===============");
        System.out.println("==============更新流程====================");
        System.out.println("==============流程=======================");
        System.out.println("==============程=========================");
    }

    /**
     * 环绕通知
     */
//    @Around("doLog()")
    public void around(ProceedingJoinPoint point) {
        Object arg = null;
        System.out.println("************* 环绕前..... *************");
        System.out.println(point);
        //得到参数
        Object[] args = point.getArgs();
        System.out.println("args=> " + Arrays.toString(args));
        System.out.println(args[0] instanceof ProcessFormDTO);
        if (args[0] instanceof ProcessFormDTO) {
            ProcessFormDTO dto = (ProcessFormDTO) args[0];
            System.out.println(JSON.toJSONString(dto.getId()));
        }
        // 得到切入点的类型
        String kind = point.getKind();
        System.out.println("kind => " + kind);
        // 切入点 得到被增强方法的方法签名
        MethodSignature methodSignature = (MethodSignature) point.getSignature();

        // 方法名 到被增强方法的方法
        Method method = methodSignature.getMethod();
        System.out.println("method => " + method);

        // 方法名 到被增强方法的方法 上面的注解
        Signature signature = point.getSignature();
        String name = signature.getName();
        Class declaringType = signature.getDeclaringType();
        int modifiers = signature.getModifiers();
        String declaringTypeName = signature.getDeclaringTypeName();
        System.out.println("    name => " + name + "\\\n    declaringType => "
                + declaringType + "\\\n    modifiers => " + modifiers + "\\\n    declaringTypeName => " + declaringTypeName);
        System.out.println("signature => " + signature);
        // 得到目标
        Object target = point.getTarget();
        System.out.println("target => " + target);

        System.out.println("************* 环绕后 *************");
    }

}

开发初步完成效果:

package com.ipr.audit.aop;

import com.alibaba.fastjson.JSON;
import com.ipr.audit.dto.process.ProcessFormDTO;
import com.ipr.audit.dto.role.RoleFormDTO;
import com.ipr.audit.dto.step.AuditStepFormDTO;
import com.ipr.biz.ILogBiz;
import com.ipr.biz.convert.ProcessConvert;
import com.ipr.biz.convert.RoleConvert;
import com.ipr.biz.convert.StepConvert;
import com.ipr.dataobject.AuditLogDO;
import com.ipr.dataobject.AuditProcessDO;
import com.ipr.dataobject.AuditRoleDO;
import com.ipr.dataobject.AuditStepDO;
import com.ipr.mapper.IAuditProcessMapper;
import com.zhubajie.common.dto.ActionInfo;
import com.zhubajie.common.dto.Request;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Objects;

/**
 * 审核流程日志记录
 *
 * @author xnALPHA
 */
@Aspect
@Component
public class AuditLog {

    private Logger logger = LoggerFactory.getLogger(AuditLog.class);

    @Resource
    private IAuditProcessMapper auditProcessMapperSlave;
    @Resource
    private ILogBiz logBiz;

    /**
     * 日志记录切入点
     */
//    @Pointcut("execution(* com.ipr.biz.impl.ProcessBizImpl.updateProcess(..))")
    @Pointcut("@annotation(com.ipr.audit.aop.AddUpdateLog)")
    public void doLog() {
    }

    /**
     * 后置通知
     */
    @AfterReturning("doLog()")
    public void after(JoinPoint point) throws Exception {
        AuditLogDO auditLogDO = new AuditLogDO();
        // 接口请求参数
        Request args = (Request) point.getArgs()[0];
        ActionInfo actionInfo = args.getActionInfo();
        String operator = "SYSTEM";
        Integer operatorId = 0;
        if (!Objects.isNull(actionInfo)) {
            operator = actionInfo.getNickname();
            operatorId = actionInfo.getUserId();
        }
        auditLogDO.setOperatorId(operatorId.longValue());
        auditLogDO.setOperatorName(operator);
        // 变更详情
        StringBuffer changeDetail = new StringBuffer(operator);

        Field[] fields = null;
        Object original = new Object();
        if (args.getData() instanceof ProcessFormDTO) {
            ProcessFormDTO arg = (ProcessFormDTO) args.getData();
            AuditProcessDO auditProcessDO = ProcessConvert.fromProcessFormDTO(arg);
            auditLogDO.setProcessId(auditProcessDO.getProcessId());
            original = auditProcessMapperSlave.getAuditProcessByProcessId(auditProcessDO.getProcessId());
            Class<? extends ProcessFormDTO> cls = arg.getClass();
            fields = cls.getDeclaredFields();
        } else if (args.getData() instanceof RoleFormDTO) {
            RoleFormDTO arg = (RoleFormDTO) args.getData();
            AuditRoleDO roleDO = RoleConvert.fromRoleFormDTO(arg);
            auditLogDO.setRoleId(roleDO.getId());
            auditLogDO.setProcessId(roleDO.getProcessId());
            Class<? extends RoleFormDTO> cls = arg.getClass();
            fields = cls.getDeclaredFields();
        } else if (args.getData() instanceof AuditStepFormDTO) {
            AuditStepFormDTO arg = (AuditStepFormDTO) args.getData();
            AuditStepDO auditStepDO = StepConvert.fromAuditStepFormDTO(arg);
            auditLogDO.setRoleId(auditStepDO.getRoleId());
            auditLogDO.setStepId(auditStepDO.getStepId());
            Class<? extends AuditStepFormDTO> cls = arg.getClass();
            fields = cls.getDeclaredFields();
        }
        assert fields != null;
        for (Field f : fields) {
            // 判断是否方法上存在注解
            boolean annotationPresent = f.isAnnotationPresent(FieldInterface.class);
            f.setAccessible(true);
            String fName = f.getName();
            Object newValue = f.get(args.getData());
            if (annotationPresent && newValue != null) {
                System.out.println("属性名:" + fName + " 属性值:" + newValue);
                FieldInterface fieldDescription = f.getAnnotation(FieldInterface.class);
                // 获取注解值
                String nameStr = fieldDescription.name();
                String convertField = fieldDescription.convertField();
                System.out.println(nameStr);
                // 反射获取旧值
                String getLetters = "";
                if (StringUtils.isEmpty(convertField)) {
                    getLetters = fName.substring(0, 1).toUpperCase() + fName.substring(1);
                } else {
                    getLetters = convertField.substring(0, 1).toUpperCase() + convertField.substring(1);
                }
                String getter = "get" + getLetters;
                Method method = original.getClass().getMethod(getter);
                Object value = method.invoke(original);
                if (value instanceof Boolean) {
                    changeDetail.append("将").append(nameStr).append("由:\"").append(value).append("\"变更为:\"").append(newValue.equals(1)).append("\";\n");
                } else {
                    changeDetail.append("将").append(nameStr).append("由:\"").append(value).append("\"变更为:\"").append(newValue).append("\";\n");
                }
            }
        }

        System.out.println(changeDetail);
        auditLogDO.setOperateDetail(changeDetail.toString());
        // 入库
        addLogs(auditLogDO);
        System.out.println("==============监听到更新流程===============");
        System.out.println("==============更新流程====================");
        System.out.println("==============流程=======================");
        System.out.println("==============程=========================");
    }

    /**
     * 环绕通知
     */
//    @Around("doLog()")
    public void around(ProceedingJoinPoint point) {
        Object arg = null;
        System.out.println("************* 环绕前..... *************");
        System.out.println(point);
        //得到参数
        Object[] args = point.getArgs();
        System.out.println("args=> " + Arrays.toString(args));
        System.out.println(args[0] instanceof ProcessFormDTO);
        if (args[0] instanceof ProcessFormDTO) {
            ProcessFormDTO dto = (ProcessFormDTO) args[0];
            System.out.println(JSON.toJSONString(dto.getId()));
        }
        // 得到切入点的类型
        String kind = point.getKind();
        System.out.println("kind => " + kind);
        // 切入点 得到被增强方法的方法签名
        MethodSignature methodSignature = (MethodSignature) point.getSignature();

        // 方法名 到被增强方法的方法
        Method method = methodSignature.getMethod();
        System.out.println("method => " + method);

        // 方法名 到被增强方法的方法 上面的注解
        Signature signature = point.getSignature();
        String name = signature.getName();
        Class declaringType = signature.getDeclaringType();
        int modifiers = signature.getModifiers();
        String declaringTypeName = signature.getDeclaringTypeName();
        System.out.println("    name => " + name + "\\\n    declaringType => "
                + declaringType + "\\\n    modifiers => " + modifiers + "\\\n    declaringTypeName => " + declaringTypeName);
        System.out.println("signature => " + signature);
        // 得到目标
        Object target = point.getTarget();
        System.out.println("target => " + target);

        System.out.println("************* 环绕后 *************");
    }

    /**
     * 日志表新增记录
     *
     * @param auditLogDO
     */
    private void addLogs(AuditLogDO auditLogDO) {
//        Request<AuditLogDO> request = new Request<>(auditLogDO);
        int i = logBiz.insertAuditLog(auditLogDO);
        System.out.println(i);
    }
}

### 使用AOP实现日志记录的方法 在Java开发中,Spring框架提供了强大的AOP(面向切面编程)支持,可以用来解决诸如日志记录这样的横切关注点。以下是关于如何通过AOP实现日志记录的具体方法以及示例。 #### 配置依赖项 为了使用Spring AOP功能,需要确保项目的`pom.xml`文件中包含了必要的依赖项: ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> ``` 此依赖会引入AspectJ库和其他必需的支持工具[^1]。 --- #### 创建切面类 创建一个切面类来定义切入点和通知逻辑。下面是一个典型的例子: ```java import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Aspect public class LoggingAspect { private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class); @Pointcut("execution(* com.example.service.*.*(..))") public void serviceMethods() {} @Before("serviceMethods()") public void logMethodEntry() { logger.info("Entering method..."); } @AfterReturning(pointcut = "serviceMethods()", returning = "result") public void logMethodExit(Object result) { logger.info("Exiting method with result={}", result); } @AfterThrowing(pointcut = "serviceMethods()", throwing = "exception") public void logException(Exception exception) { logger.error("Exception occurred", exception); } } ``` 上述代码片段展示了如何定义一个名为`LoggingAspect`的切面类,并设置了三个主要的通知: - `@Before`: 方法进入前的日志记录。 - `@AfterReturning`: 方法正常返回后的日志记录。 - `@AfterThrowing`: 当方法抛出异常时的日志记录[^2]。 --- #### 定义切入点表达式 切入点表达式的语法决定了哪些方法会被拦截并应用相应的通知。例如,上面的例子中的`@Pointcut`注解指定了所有位于`com.example.service`包下的公共方法都会被拦截[^3]。 如果希望更精确地控制切入点范围,可以通过调整表达式的内容完成。比如限定到特定类或带有某些参数签名的方法上。 --- #### 日志事件发布与监听机制 (可选扩展) 对于更加复杂的应用场景,还可以结合Spring事件机制进一步完善日志管理流程。例如,在发生错误时触发自定义事件对象并将之广播给其他组件处理: ```java // 自定义事件类 public class ExceptionLogEvent extends ApplicationEvent { public ExceptionLogEvent(ExceptionLog source) { super(source); } } // 发布者部分省略... // 监听器 @Component public class LogEventListener implements ApplicationListener<ExceptionLogEvent> { @Override public void onApplicationEvent(ExceptionLogEvent event) { System.out.println("Received custom event - " + event.getSource()); } } ``` 这里展示了一个简单的基于事件驱动架构的设计模式实例[^4]。 --- ### 总结 以上介绍了利用Spring AOP技术实现基本及高级别的日志记录方式。它不仅简化了跨模块间重复操作的需求,同时也提高了系统的可维护性和清晰度。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值