相同类中方法间调用时日志Aop失效处理

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 本篇分享的内容是在相同类中方法间调用时Aop失效处理方案,该问题我看有很多文章描述了,不过大多是从事务角度分享的,本篇打算从日志aop方面分享(当然都是aop,失效和处理方案都是一样),以下都是基于springboot演示;快速定义个日志Appender快速定义个拦截器和日志注解(aop)...

本篇分享的内容是在相同类中方法间调用时Aop失效处理方案,该问题我看有很多文章描述了,不过大多是从事务角度分享的,本篇打算从日志aop方面分享(当然都是aop,失效和处理方案都是一样),以下都是基于springboot演示;

  • 快速定义个日志Appender
  • 快速定义个拦截器和日志注解(aop)
  • 模拟相同类中方法间调用时aop失效
  • Aop失效处理方案(就两种足够了)

快速定义个日志Appender

日志我还是喜欢log4j,大部分朋友也同样吧,这里lombok与log4j结合来完成我们的日志,如下maven包(最新mvn还是建议去官网找):

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>2.0.0-alpha0</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>2.0.0-alpha0</version>
        </dependency>

先继承log4j的AppenderSkeleton重写下append方法,简单记录下就行,如下:

public class MyLogAppend extends AppenderSkeleton {
    private String author;

    public void setAuthor(String author) {
        this.author = author;
    }

    @Override
    protected void append(LoggingEvent loggingEvent) {
        System.out.println(
                JsonUtil.formatMsg("date -- {},level -- {},message -- {}",
                        LocalDate.now(),
                        loggingEvent.getLevel(),
                        loggingEvent.getMessage()));
    }

    @Override
    public void activateOptions() {
        super.activateOptions();
        System.out.println("author:" + this.author);
    }

    @Override
    public void close() {
        this.closed = true;
    }

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

然后项目根目录增加log4j.properties配置文件,配置内容定义info级别,就此完成了log4j自定义记录日志了:

log4j.rootLogger=info,MyLogAppend
log4j.appender.MyLogAppend=com.sm.component.log.MyLogAppend
log4j.appender.MyLogAppend.author=shenniu003

快速定义个拦截器和日志注解(aop)

通常同类中不同方法调用是常事,可以直接用this.xx();有时有这样需求,需要各个调用方法时候的参数记录下来,因此我们需要个拦截器,再增加个自定义注解方便使用:

@Aspect
@Component
@Slf4j
public class MyLogInterceptor {

    private final String pointcut = "@annotation(com.sm.component.ServiceLog)";

    @Pointcut(pointcut)
    public void log() {
    }

    @Before(value = "log()")
    void before(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        log.info(
                JsonUtil.formatMsg("method:{},params:{}",
                        signature.toLongString(),
                        joinPoint.getArgs()));
    }
}
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ServiceLog {
}

拦截器拦截带有@ServiceLog注解的方法,然后记录请求参数和方法名

模拟相同类中方法间调用时aop失效

利用上面完成的日志注解,这里在OrderService类中用getOrderDetail方法去调用getOrderLog方法,他两都标记日志注解便于记录参数日志;同时getOrderDetail方法也调用另外一个UserService类中的getNickName方法,便于比较:

@Service
public class OrderService {

    @Autowired
    UserService userService;

    @ServiceLog
    public String getOrderDetail(String orderNum) {
        String des = "订单号【" + orderNum + "】月饼一盒";

        userService.getNickName(orderNum);

        this.getOrderLog(orderNum + "11111");

        return des;
    }

    @ServiceLog
    public List<String> getOrderLog(String orderNum) {
        List<String> logs = new ArrayList<>();
        IntStream.range(0, 5).forEach(b -> {
            logs.add("用户" + b + "购买成功");
        });
        return logs;
    }
}
@Service
public class UserService {
    @ServiceLog
    public String getNickName(String userId) {
        return "神牛" + userId;
    }
}

方法调用重点截图:
image
然后运行程序,接口触发调用getOrderDetail方法,以下拦截器中记录的日志信息:
image
能够看出拦截器只记录到了getOrderDetail和getNickName方法的日志,因此可以肯定getOrderLog根本没有走拦截器,尽管在方法上加了日志@ServiceLog注解也没用。

Aop失效处理方案(就两种足够了)

就上面相同类中方法间调用拦截器(aop)没起作用,我们有如下常用两种方式处理方案;

  1. 用@Autowired或Resource引入自身依赖
  2. 开启暴露代理类,AopContext.currentProxy()方式获取代理类

第一种:主要使用注解方法引入自身代理依赖,不要使用构造的方式会有循环依赖问题,以下使用方式:
image
第二种:通过暴露代理类方式,实际原理是把代理类添加到当前请求的ThreadLocal里面,然后在使用时从ThreadLocal中获取代理类,再调用对应的方法,开启方式需要:

@EnableAspectJAutoProxy(exposeProxy = true)

然后方法中如下使用即可:
image
最后来看下使用这两种方式正常走拦截器效果:
image

不管是日志拦截器或事务,他们都是aop的方式,底层原理走的代理方式,只有使用代理类才会正常执行拦截器,而this.xxx()使用的是自身实例对象,因此会出现上面失效的情况。

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
3月前
|
SQL 监控 Java
在IDEA 、springboot中使用切面aop实现日志信息的记录到数据库
这篇文章介绍了如何在IDEA和Spring Boot中使用AOP技术实现日志信息的记录到数据库的详细步骤和代码示例。
在IDEA 、springboot中使用切面aop实现日志信息的记录到数据库
|
3月前
|
Kubernetes Ubuntu Windows
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
【Azure K8S | AKS】分享从AKS集群的Node中查看日志的方法(/var/log)
121 3
|
5天前
|
JSON Java 数据库
SpringBoot项目使用AOP及自定义注解保存操作日志
SpringBoot项目使用AOP及自定义注解保存操作日志
23 1
|
2月前
|
设计模式 SQL 安全
PHP中的设计模式:单例模式的深入探索与实践在PHP的编程实践中,设计模式是解决常见软件设计问题的最佳实践。单例模式作为设计模式中的一种,确保一个类只有一个实例,并提供全局访问点,广泛应用于配置管理、日志记录和测试框架等场景。本文将深入探讨单例模式的原理、实现方式及其在PHP中的应用,帮助开发者更好地理解和运用这一设计模式。
在PHP开发中,单例模式通过确保类仅有一个实例并提供一个全局访问点,有效管理和访问共享资源。本文详细介绍了单例模式的概念、PHP实现方式及应用场景,并通过具体代码示例展示如何在PHP中实现单例模式以及如何在实际项目中正确使用它来优化代码结构和性能。
43 2
|
2月前
|
存储 运维 监控
超级好用的C++实用库之日志类
超级好用的C++实用库之日志类
36 0
|
3月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
41 0
Spring高手之路22——AOP切面类的封装与解析
|
3月前
|
Java Spring 容器
SpringBoot整合AOP实现打印方法执行时间切面
SpringBoot整合AOP实现打印方法执行时间切面
43 1
|
3月前
|
数据可视化 应用服务中间件 Apache
优化集中式日志记录的方法:添加 Logstash 过滤器
优化集中式日志记录的方法:添加 Logstash 过滤器
41 1
|
3月前
|
开发者 前端开发 编解码
Vaadin解锁移动适配新境界:一招制胜,让你的应用征服所有屏幕!
【8月更文挑战第31天】在移动互联网时代,跨平台应用开发备受青睐。作为一款基于Java的Web应用框架,Vaadin凭借其组件化设计和强大的服务器端渲染能力,助力开发者轻松构建多设备适应的Web应用。本文探讨Vaadin与移动设备的适配策略,包括响应式布局、CSS媒体查询、TouchKit插件及服务器端优化,帮助开发者打造美观且实用的移动端体验。通过这些工具和策略的应用,可有效应对屏幕尺寸、分辨率及操作系统的多样性挑战,满足广大移动用户的使用需求。
55 0
|
3月前
|
存储 运维 监控
Entity Framework Core 实现审计日志记录超棒!多种方法助你跟踪数据变化、监控操作,超实用!
【8月更文挑战第31天】在软件开发中,审计日志记录对于跟踪数据变化、监控用户操作及故障排查至关重要。Entity Framework Core (EF Core) 作为强大的对象关系映射框架,提供了多种实现审计日志记录的方法。例如,可以使用 EF Core 的拦截器在数据库操作前后执行自定义逻辑,记录操作类型、时间和执行用户等信息。此外,也可通过在实体类中添加审计属性(如 `CreatedBy`、`CreatedDate` 等),并在保存实体时更新这些属性来记录审计信息。这两种方法都能有效帮助我们追踪数据变更并满足合规性和安全性需求。
64 0