aop 基于注解 记录日志

aop 的应用-

记录日志

1,自定义注解

1.1 @EnableRequestLog

用于启动日志记录功能

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@EnableAspectJAutoProxy
@Import(LogConfigurationRegistrar.class)
public @interface EnableRequestLog {

    Class<? extends Annotation>[] mark() default {}; // 添加一个属性,用于指定需要记录日志的注解

    /**
     * 记录日志逻辑
     * @return {@link Class}<{@link ?} {@link extends} {@link TimeCost}>
     */
    Class<? extends TimeCost> ref() default DefaultMethodLog.class;

}

1.2 @Loggable 标记需要记录日志的类或请求

@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {

    String value() default "";

}

1.3 LogConfigurationRegistrar 导入配置

public class LogConfigurationRegistrar implements ImportBeanDefinitionRegistrar {


    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
        Map<String, Object> annotationAttributes = annotationMetadata.getAnnotationAttributes(EnableRequestLog.class.getName());
        Object o = annotationAttributes.get("mark");
        Class<? extends Annotation>[] arg = (Class<? extends Annotation>[]) o;
        BeanDefinition beanDefinition = null;
        //获取切点
        Pointcut pointcut = forAnnotations(arg);
        //实际记录日志
        Object ref = annotationAttributes.get("ref");

        Class<? extends TimeCost> refClass = (Class<? extends TimeCost>) ref;


        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(refClass);

        builder.addConstructorArgValue(pointcut);


        beanDefinition = builder.getBeanDefinition();
        registry.registerBeanDefinition(TimeCost.class.getName(), beanDefinition);


    }

    public Pointcut forAnnotations(Class<? extends Annotation>... annotations) {
        ComposablePointcut pointcut = null;
        for (Class<? extends Annotation> annotation : annotations) {
            if (pointcut == null) {
                pointcut = new ComposablePointcut(classOrMethod(annotation));
            } else {
                pointcut.union(classOrMethod(annotation));
            }
        }
        return pointcut;
    }

    private Pointcut classOrMethod(Class<? extends Annotation> annotation) {
        return Pointcuts.union(new AnnotationMatchingPointcut(null, annotation, true),
                new AnnotationMatchingPointcut(annotation, true));
    }
}

2 接口相关

2.1 记录日志,与aop 顶级接口

public interface TimeCost extends MethodInterceptor, PointcutAdvisor, AopInfrastructureBean {

    //打印参数
    public void logParam(Object arg);

}

2.2 LogStorage 持久化日志

public interface LogStorage {

    public void saveLog(Object arg);

}

2.2.1 默认实现

@Component
public class DefaultLogStorage  implements  LogStorage{
    @Override
    public void saveLog(Object arg) {
        System.out.println("arg = " + arg);
    }
}

2.3 aop 实际逻辑 抽象类 MethodLogAspectSupport ,真正的实现要依靠 其实现类

public abstract class MethodLogAspectSupport implements TimeCost, ApplicationContextAware {

    private Pointcut pointcut;
    private ApplicationContext applicationContext;
    private LogStorage logStorage;
    public MethodLogAspectSupport(Pointcut pointcut ) {
        this.pointcut = pointcut;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return doInvoke(invocation);
    }

    public abstract Object doInvoke(MethodInvocation methodInvocation) throws Throwable;

    @Override
    public Pointcut getPointcut() {

        return this.pointcut;
    }

    @Override
    public Advice getAdvice() {
        return this;
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }


    public String convertToCurl() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

        // 获取请求 URL
        String url = request.getRequestURL().toString();

        // 构建 cURL 命令
        StringBuilder curlCmd = new StringBuilder();
        curlCmd.append("curl -X ").append(request.getMethod()).append(" '").append(url).append("'");

        // 添加请求头
        Enumeration<String> headerNames = request.getHeaderNames();
        while (headerNames.hasMoreElements()) {
            String headerName = headerNames.nextElement();
            String headerValue = request.getHeader(headerName);
            curlCmd.append(" -H '").append(headerName).append(": ").append(headerValue).append("'");
        }

        // 添加请求参数
        Enumeration<String> paramNames = request.getParameterNames();
        while (paramNames.hasMoreElements()) {
            String paramName = paramNames.nextElement();
            String paramValue = request.getParameter(paramName);
            curlCmd.append(" --data '").append(paramName).append("=").append(paramValue).append("'");
        }
        return curlCmd.toString();
    }


    public void setPointcut(Pointcut pointcut) {
        this.pointcut = pointcut;
    }

    @Override
    public void logParam(Object arg) {
        if (logStorage != null) {
            logStorage.saveLog(arg);
        }
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        LogStorage bean = this.applicationContext.getBean(  LogStorage.class);
        if (bean != null) {
            this.logStorage = bean;
        }
        if (this.logStorage == null) {
            this.logStorage = new DefaultLogStorage();
        }

    }
}

2.3.1 自定义保存 逻辑 CustomMethodLog

@Slf4j
public class CustomMethodLog extends MethodLogAspectSupport {
    public CustomMethodLog(Pointcut pointcut) {
        super(pointcut);
    }
    @Override
    public Object doInvoke(MethodInvocation methodInvocation) throws Throwable {
        Loggable loggable = methodInvocation.getMethod().getAnnotation(Loggable.class);

        if (loggable == null) {
            Method method = methodInvocation.getMethod();
            Class<?> declaringClass = method.getDeclaringClass();
            loggable = declaringClass.getAnnotation(Loggable.class);
        }
        //模拟~~~~
        log.info("自定义{},执行时间:{} curl:{}", loggable.value(),           LocalDateTime.now(),convertToCurl());
        Map<String, Object> objectMap = new HashMap<>();
        RequestLog requestLog = new RequestLog();
        requestLog.setMethod(methodInvocation.getMethod().getName());
        objectMap.put("温度", "103");
        objectMap.put("湿度", "50");
        requestLog.setAttribute(objectMap);
        this.logParam(requestLog);
        return methodInvocation.proceed();
    }
}

具体来说,这个功能包括以下几个关键部分:

@EnableRequestLog 注解:这是一个自定义的注解,用于启用日志记录功能。它可以指定需要记录日志的注解类型,以及日志记录的实现类。

@Loggable 注解:这是用来标记需要记录日志的方法或类的注解。

LogConfigurationRegistrar 类:这个类实现了 ImportBeanDefinitionRegistrar 接口,用于注册Bean定义。它会根据 @EnableRequestLog 注解的配置信息创建 TimeCost 的实现类,并将其注册到Spring容器中。

TimeCost 接口:这是一个顶级接口,定义了记录日志的方法和一些AOP相关的方法。

MethodLogAspectSupport 抽象类:这是 TimeCost 接口的抽象实现类,实际的日志记录逻辑需要在其子类中实现。

CustomMethodLog 类:这是 MethodLogAspectSupport 的一个具体子类,实现了自定义的日志记录逻辑。

总体来说,这个功能允许你使用 @Loggable 注解标记需要记录日志的方法或类,然后通过 @EnableRequestLog 注解启用日志记录功能,并指定日志记录的实现类。当方法被调用时,AOP会拦截这些方法,并在拦截器中执行日志记录逻辑

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容