0

0

Java中如何用AOP实现日志切面

穿越時空

穿越時空

发布时间:2025-06-18 18:12:02

|

626人浏览过

|

来源于php中文网

原创

aop日志切面通过分离横切逻辑提升代码可维护性,其解决方案步骤如下:1. 添加spring aop与aspectj依赖;2. 创建使用@aspect与@component注解的切面类;3. 使用@pointcut定义切点拦截指定包下的方法;4. 通过@before、@after定义前置与后置通知记录方法出入日志;5. 在配置类中使用@enableaspectjautoproxy启用aop;6. 配置slf4j等日志框架;7. 使用@afterthrowing处理异常并记录错误信息;8. 利用threadlocal传递用户id等上下文信息并在@after清理以避免内存泄漏;9. 优化性能时应精确切点表达式、使用异步日志、降低日志级别、选用高效日志框架、避免复杂拼接、结合@around实现条件日志。

Java中如何用AOP实现日志切面

AOP(面向切面编程)在Java中用于将横切关注点(如日志、安全等)从核心业务逻辑中分离出来。通过AOP,你可以避免在每个方法中都编写重复的日志代码,从而提高代码的可维护性和可读性。

Java中如何用AOP实现日志切面

解决方案

Java中如何用AOP实现日志切面

使用Spring AOP实现日志切面,步骤如下:

立即学习Java免费学习笔记(深入)”;

Java中如何用AOP实现日志切面
  1. 添加依赖:确保你的项目中包含了Spring AOP和AspectJ的依赖。

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.3.9</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.7</version>
    </dependency>
  2. 创建日志切面类:创建一个类,使用@Aspect注解将其声明为一个切面。

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.annotation.Pointcut;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class LoggingAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
        @Pointcut("execution(* com.example.service.*.*(..))")
        public void serviceMethods() {}
    
        @Before("serviceMethods()")
        public void logBefore(JoinPoint joinPoint) {
            logger.info("Entering method: " + joinPoint.getSignature().toShortString());
            Object[] args = joinPoint.getArgs();
            if (args != null && args.length > 0) {
                logger.info("with arguments: " + java.util.Arrays.toString(args));
            }
        }
    
        @After("serviceMethods()")
        public void logAfter(JoinPoint joinPoint) {
            logger.info("Exiting method: " + joinPoint.getSignature().toShortString());
        }
    }
  3. 定义切点(Pointcut):使用@Pointcut注解定义一个切点,指定哪些方法需要被拦截。这里,我们拦截com.example.service包下所有类的所有方法。

  4. 定义通知(Advice):使用@Before@After等注解定义通知,指定在切点方法执行前、后执行哪些操作。@Before用于在方法执行前记录日志,@After用于在方法执行后记录日志。

  5. 启用AOP:在Spring配置类中启用AOP。

    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configuration
    @EnableAspectJAutoProxy
    public class AppConfig {
        // ... other configurations
    }
  6. 配置Logger:使用SLF4J和Logback或Log4j等日志框架,配置日志输出。

日志切面配置完成后,所有com.example.service包下的方法在执行前后都会自动记录日志。

AOP日志切面如何处理异常?

在AOP切面中处理异常,可以使用@AfterThrowing通知。它允许你在目标方法抛出异常后执行一些操作,比如记录异常信息。

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LoggingAspect {

    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);

    @AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Throwable ex) {
        logger.error("Exception in " + joinPoint.getSignature().toShortString() +
                     " with message = " + ex.getMessage(), ex);
    }
}

在这个例子中,logAfterThrowing方法会在com.example.service包下的方法抛出异常时被调用。throwing = "ex"指定了异常对象的名字,可以在方法中访问该异常。

MindShow
MindShow

MindShow官网 | AI生成PPT,快速演示你的想法

下载

AOP日志切面如何传递上下文信息?

有时需要在日志中包含一些上下文信息,比如用户ID、请求ID等。可以使用ThreadLocal来传递这些信息。

  1. 创建ThreadLocal:创建一个ThreadLocal来存储上下文信息。

    public class ContextHolder {
        private static final ThreadLocal<String> userId = new ThreadLocal<>();
    
        public static void setUserId(String id) {
            userId.set(id);
        }
    
        public static String getUserId() {
            return userId.get();
        }
    
        public static void clear() {
            userId.remove();
        }
    }
  2. 在切面中设置和获取上下文信息:在切面的@Before通知中设置上下文信息,在需要记录日志的地方获取。

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class LoggingAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
        @Before("execution(* com.example.service.*.*(..))")
        public void logBefore(JoinPoint joinPoint) {
            // 假设从请求头中获取用户ID
            String userId = "someUserId"; // 实际场景中从Request上下文中获取
            ContextHolder.setUserId(userId);
    
            logger.info("User " + ContextHolder.getUserId() + " entering method: " + joinPoint.getSignature().toShortString());
        }
    }
  3. 清理ThreadLocal:为了避免内存泄漏,在请求处理完成后,需要清理ThreadLocal。可以使用@After通知来清理。

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class LoggingAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
        @After("execution(* com.example.service.*.*(..))")
        public void logAfter(JoinPoint joinPoint) {
            logger.info("Exiting method: " + joinPoint.getSignature().toShortString());
            ContextHolder.clear();
        }
    }

AOP日志切面的性能影响如何优化?

AOP会引入额外的处理步骤,因此可能会对性能产生影响。优化AOP日志切面性能的一些方法:

  1. 精确的切点定义:避免使用过于宽泛的切点表达式,只拦截需要记录日志的方法。例如,避免使用execution(* com.example..*(..)),而是使用更具体的表达式,如execution(* com.example.service.*.*(..))

  2. 异步日志记录:将日志记录操作放入异步线程中执行,避免阻塞主线程。可以使用java.util.concurrent.ExecutorService或Spring的@Async注解来实现异步日志记录。

    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.After;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.scheduling.annotation.Async;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class LoggingAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
        @After("execution(* com.example.service.*.*(..))")
        @Async
        public void logAfterAsync(JoinPoint joinPoint) {
            logger.info("Exiting method: " + joinPoint.getSignature().toShortString());
        }
    }

    需要确保在Spring配置中启用了异步支持:

    import org.springframework.context.annotation.Configuration;
    import org.springframework.scheduling.annotation.EnableAsync;
    
    @Configuration
    @EnableAsync
    public class AppConfig {
        // ... other configurations
    }
  3. 减少日志级别:在生产环境中,使用较低的日志级别(如INFO、WARN、ERROR),避免记录过多的调试信息。

  4. 使用高效的日志框架:选择性能较好的日志框架,如Logback。

  5. 避免在日志中进行复杂的计算:尽量避免在日志消息中使用复杂的字符串拼接或计算,可以先将结果计算好再记录。

  6. 使用条件日志:只有在满足特定条件时才记录日志,例如,只有当方法执行时间超过一定阈值时才记录日志。

    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.stereotype.Component;
    
    @Aspect
    @Component
    public class LoggingAspect {
    
        private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);
    
        @Around("execution(* com.example.service.*.*(..))")
        public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
            long start = System.currentTimeMillis();
            Object proceed = joinPoint.proceed();
            long executionTime = System.currentTimeMillis() - start;
    
            if (executionTime > 100) { // 超过100ms才记录日志
                logger.warn(joinPoint.getSignature() + " executed in " + executionTime + "ms");
            }
            return proceed;
        }
    }

通过上述方法,可以有效地优化AOP日志切面的性能,使其对系统性能的影响降到最低。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
spring框架介绍
spring框架介绍

本专题整合了spring框架相关内容,想了解更多详细内容,请阅读专题下面的文章。

155

2025.08.06

Java Spring Security 与认证授权
Java Spring Security 与认证授权

本专题系统讲解 Java Spring Security 框架在认证与授权中的应用,涵盖用户身份验证、权限控制、JWT与OAuth2实现、跨站请求伪造(CSRF)防护、会话管理与安全漏洞防范。通过实际项目案例,帮助学习者掌握如何 使用 Spring Security 实现高安全性认证与授权机制,提升 Web 应用的安全性与用户数据保护。

88

2026.01.26

scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

492

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

377

2023.10.25

js 字符串转数组
js 字符串转数组

js字符串转数组的方法:1、使用“split()”方法;2、使用“Array.from()”方法;3、使用for循环遍历;4、使用“Array.split()”方法。本专题为大家提供js字符串转数组的相关的文章、下载、课程内容,供大家免费下载体验。

760

2023.08.03

js截取字符串的方法
js截取字符串的方法

js截取字符串的方法有substring()方法、substr()方法、slice()方法、split()方法和slice()方法。本专题为大家提供字符串相关的文章、下载、课程内容,供大家免费下载体验。

220

2023.09.04

java基础知识汇总
java基础知识汇总

java基础知识有Java的历史和特点、Java的开发环境、Java的基本数据类型、变量和常量、运算符和表达式、控制语句、数组和字符串等等知识点。想要知道更多关于java基础知识的朋友,请阅读本专题下面的的有关文章,欢迎大家来php中文网学习。

1564

2023.10.24

字符串介绍
字符串介绍

字符串是一种数据类型,它可以是任何文本,包括字母、数字、符号等。字符串可以由不同的字符组成,例如空格、标点符号、数字等。在编程中,字符串通常用引号括起来,如单引号、双引号或反引号。想了解更多字符串的相关内容,可以阅读本专题下面的文章。

649

2023.11.24

Go高并发任务调度与Goroutine池化实践
Go高并发任务调度与Goroutine池化实践

本专题围绕 Go 语言在高并发任务处理场景中的实践展开,系统讲解 Goroutine 调度模型、Channel 通信机制以及并发控制策略。内容包括任务队列设计、Goroutine 池化管理、资源限制控制以及并发任务的性能优化方法。通过实际案例演示,帮助开发者构建稳定高效的 Go 并发任务处理系统,提高系统在高负载环境下的处理能力与稳定性。

4

2026.03.10

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
成为PHP架构师-自制PHP框架
成为PHP架构师-自制PHP框架

共28课时 | 2.6万人学习

YMP在线手册
YMP在线手册

共64课时 | 49.6万人学习

ThinkPHP6.x 微实战--十天技能课堂
ThinkPHP6.x 微实战--十天技能课堂

共26课时 | 1.8万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号