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

简介: 本篇分享的内容是在相同类中方法间调用时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月前
|
存储 监控 iOS开发
iOS应用崩溃了,如何通过崩溃手机连接电脑查找日志方法
在iOS应用开发过程中,调试日志和奔溃日志是开发者必不可少的工具。当iOS手机崩溃时,我们可以连接电脑并使用Xcode Console等工具来查看日志。然而,这种方式可能不够方便,并且处理奔溃日志也相当繁琐。克魔助手的出现为开发者带来了极大的便利,本文将详细介绍其功能和使用方法。 克魔助手会提供两种日志,一种是实时的,一种的是崩溃的。(由于崩溃日志的环境很麻烦,目前只展示实时日志操作步骤)
|
1月前
|
Linux 应用服务中间件 nginx
【PUSDN】centos查看日志文件内容,包含某个关键字的前后5行日志内容,centos查看日志的几种方法
【PUSDN】centos查看日志文件内容,包含某个关键字的前后5行日志内容,centos查看日志的几种方法
46 0
|
4月前
|
Kubernetes 容器
在Kubernetes(k8s)中部署Higress时,查看Wasm插件日志的方法如下
在Kubernetes(k8s)中部署Higress时,查看Wasm插件日志的方法如下
68 1
|
4月前
|
Kubernetes Shell Linux
linux|shell脚本|有趣的知识---格式化输出日志和脚本调试方法以及kubernetes集群核心服务重启和集群证书备份脚本
linux|shell脚本|有趣的知识---格式化输出日志和脚本调试方法以及kubernetes集群核心服务重启和集群证书备份脚本
57 0
|
1月前
|
SQL NoSQL Java
【七】springboot整合AOP实现日志操作
【七】springboot整合AOP实现日志操作
41 0
|
3月前
|
Dubbo Java 应用服务中间件
微服务框架(八)Spring Boot AOP 日志切面实现
  此系列文章将会描述Java框架Spring Boot、服务治理框架Dubbo、应用容器引擎Docker,及使用Spring Boot集成Dubbo、Mybatis等开源框架,其中穿插着Spring Boot中日志切面等技术的实现,然后通过gitlab-CI以持续集成为Docker镜像。   本文为使用Spring Boot AOP 实现日志切面、分离INFO和ERROR级别日志
|
3月前
|
Java 程序员 数据库
业务需求-用AOP记录系统操作日志
全栈老司机 程序员林中酒 更新了本文详细介绍了如何使用AOP(面向切面编程)记录系统操作日志的业务需求,包括需求分析、技术实现分析、数据库设计和代码实现等各个环节。您将了解如何高效、规范地实现这一功能
|
3月前
|
Linux
百度搜索:蓝易云【Linux清空日志的五种方法】
这些是在Linux中清空日志文件的五种常用方法。根据您的具体需求和环境,可以选择适合您的方法进行日志清空操作。请注意,清空日志文件可能会导致丢失日志数据,因此在清空之前请确保已备份重要的日志信息。
32 0
|
4月前
|
Java Spring
spring boot aop 实践---记录日志
spring boot aop 实践---记录日志
28 0
|
4月前
|
Linux
linux下实现日志切割的两种方法 logrotate
linux下实现日志切割的两种方法 logrotate
127 0