[Java日志切面 ]

 

目录

 🍚前言:

🍚代码实现:

🍚优化后的代码:

🍚优化后的代码:



 🍚前言:

    最近在打英雄联盟 一下子冲到了 黄金,奥里给

🍚代码实现:

@Aspect
@Component
public class LoggingAspect {

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

    @Before("execution(* com.example.demo.service.*.*(..))")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("Before method: " + joinPoint.getSignature().getName());
    }

    @After("execution(* com.example.demo.service.*.*(..))")
    public void logAfter(JoinPoint joinPoint) {
        logger.info("After method: " + joinPoint.getSignature().getName());
    }

    @AfterReturning(pointcut = "execution(* com.example.demo.service.*.*(..))", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        logger.info("After returning method: " + joinPoint.getSignature().getName() + ", result: " + result);
    }

    @AfterThrowing(pointcut = "execution(* com.example.demo.service.*.*(..))", throwing = "exception")
    public void logAfterThrowing(JoinPoint joinPoint, Exception exception) {
        logger.error("After throwing method: " + joinPoint.getSignature().getName() + ", exception: " + exception.getMessage());
    }
}

示例中,我们使用了Spring AOP框架来实现日志切面。我们定义了一个LoggingAspect类,并使用@Aspect注解将其标记为切面类。我们还使用@Component注解将其标记为Spring组件,以便Spring可以自动扫描并将其实例化。

我们定义了四个切点方法,分别在方法执行前、执行后、执行成功返回后和执行抛出异常后执行。我们使用@Before、@After、@AfterReturning和@AfterThrowing注解来标记这些方法,并使用execution表达式指定切点。在这个示例中,我们使用了通配符来匹配com.example.demo.service包中的所有方法。

在每个切点方法中,我们使用JoinPoint参数来获取当前执行的方法的名称和参数。我们使用LoggerFactory获取一个Logger实例,并使用info()或error()方法记录日志。在@AfterReturning方法中,我们还使用返回值参数来记录方法的返回值。

最后,我们需要在Spring配置文件中启用AOP自动代理,以便Spring可以自动将切面织入我们的应用程序中。我们可以使用以下配置:

<aop:aspectj-autoproxy />

示例只是一个简单的演示,实际的日志切面可能需要更复杂的逻辑和更详细的日志记录

🍚优化后的代码:

@Aspect
@Component
public class LoggingAspect {

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

    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceMethods() {}

    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("Before method: {}", joinPoint.getSignature().getName());
    }

    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        logger.info("After returning method: {}, result: {}", joinPoint.getSignature().getName(), result);
    }

    @AfterThrowing(pointcut = "serviceMethods()", throwing = "exception")
    public void logAfterThrowing(JoinPoint joinPoint, Exception exception) {
        logger.error("After throwing method: {}, exception: {}", joinPoint.getSignature().getName(), exception.getMessage());
    }
}

示例中,我们使用了更简洁的语法来定义切点和切点方法。我们使用@Pointcut注解定义了一个名为serviceMethods的切点方法,它匹配com.example.demo.service包中的所有方法。我们还使用{}和参数占位符来更方便地记录日志。

我们还删除了@After注解,因为它不是必需的。如果我们不需要在方法执行后记录日志,我们可以完全省略它。

最后,我们还使用了final关键字来定义Logger实例,以便它不能被修改。这是一个良好的实践,因为Logger实例通常是线程安全的,所以我们不需要在每个方法调用中创建一个新的实例。

🍚优化后的代码:

@Aspect
@Component
public class LoggingAspect {

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

    @Pointcut("execution(* com.example.demo.service.*.*(..))")
    public void serviceMethods() {}

    @Before("serviceMethods()")
    public void logBefore(JoinPoint joinPoint) {
        logger.info("Before method: {}", joinPoint.getSignature().getName());
    }

    @AfterReturning(pointcut = "serviceMethods()", returning = "result")
    public void logAfterReturning(JoinPoint joinPoint, Object result) {
        logger.info("After returning method: {}, result: {}", joinPoint.getSignature().getName(), result);
    }

    @AfterThrowing(pointcut = "serviceMethods()", throwing = "exception")
    public void logAfterThrowing(JoinPoint joinPoint, Exception exception) {
        logger.error("After throwing method: {}, exception: {}", joinPoint.getSignature().getName(), exception.getMessage());
    }

    @Around("serviceMethods()")
    public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        long startTime = System.currentTimeMillis();
        Object result = null;
        try {
            result = joinPoint.proceed();
            return result;
        } finally {
            long endTime = System.currentTimeMillis();
            long duration = endTime - startTime;
            logger.info("Method: {}, execution time: {} ms", joinPoint.getSignature().getName(), duration);
        }
    }
}

示例中,我们添加了一个新的切点方法logAround,它使用@Around注解来拦截方法的执行,并在方法执行前后记录日志。我们使用ProceedingJoinPoint参数来调用原始方法,并使用try-finally块来确保我们在方法执行后记录日志。

我们还添加了一个计时器来记录方法的执行时间。我们使用System.currentTimeMillis()方法来获取当前时间,并在方法执行前和执行后记录时间。我们还使用Object类型的变量result来存储方法的返回值,并在finally块中返回它。

这个示例还演示了如何在同一个切面中使用多个切点方法来拦截不同的方法。我们可以使用不同的execution表达式来匹配不同的方法,并在不同的切点方法中记录日志。这使得我们可以更灵活地控制日志记录的粒度和详细程度。

### 3.1 使用 Spring AOP 实现日志切面功能 在 Java 中,面向切面编程(AOP)是一种用于解耦辅助功能与核心业务逻辑的编程范式,适用于日志记录、性能监控、事务管理等场景。通过 AOP,可以将这些辅助逻辑从业务代码中分离出来,提升代码的可维护性和可重用性。以下是一个使用 Spring AOP 实现日志记录功能的示例。 首先,定义一个切面类,用于实现日志记录逻辑。该类使用 `@Aspect` 注解声明为一个切面,并通过 `@Around` 注解定义环绕通知,用于在方法执行前后记录日志信息: ```java import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @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; System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms"); return proceed; } } ``` 在上述代码中,`@Around("execution(* com.example.service.*.*(..))")` 定义了切点表达式,表示对 `com.example.service` 包下的所有类和方法应用该切面[^1]。 ### 3.2 启用 AOP 支持 为了使切面生效,需要在 Spring 配置中启用 AOP 支持。如果使用的是基于 Java 的配置,可以在配置类中添加 `@EnableAspectJAutoProxy` 注解: ```java import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy public class AopConfig { } ``` 通过 `@EnableAspectJAutoProxy`,Spring 将自动代理切面并将其与匹配的业务方法进行“编织”操作,实现切面功能的注入[^2]。 ### 3.3 业务服务类示例 为了验证切面是否生效,可以编写一个简单的业务服务类: ```java import org.springframework.stereotype.Service; @Service public class SampleService { public void performTask() { System.out.println("Performing task..."); } } ``` 当调用 `performTask()` 方法时,日志切面会自动记录方法的执行时间,并输出到控制台。 ### 3.4 AOP 的优势与扩展 AOP 的核心优势在于将辅助功能(如日志、事务、安全等)从业务逻辑中分离,降低模块之间的耦合度,提高代码的可重用性和可维护性。此外,AOP 还支持多种通知类型,包括前置通知(`@Before`)、后置通知(`@After`)、返回通知(`@AfterReturning`)和异常通知(`@AfterThrowing`),可根据不同场景灵活配置[^4]。 若希望进一步扩展日志切面功能,例如仅对特定方法应用日志记录,可以通过自定义注解或更复杂的切点表达式实现。例如,定义一个自定义注解 `@Loggable`,并在切面中使用它作为切点: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Loggable { } ``` 随后在切面中修改切点表达式为: ```java @Around("@annotation(com.example.annotation.Loggable)") ``` 这样只有被 `@Loggable` 注解标记的方法才会触发日志记录功能[^5]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是汤圆丫

怎么 给1分?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值