Spring Boot切面Aspect实现日志记录

Spring Boot切面Aspect实现日志记录

刚接触到公司项目的时候还是能学到学校里学不到的东西,比如项目里将每个前端请求都记录在日志中,持久化到数据库,后来细看代码才发现,是使用切面实现的。

阅读这篇文章,你可能需要了解Spring Boot的一些知识,例如切面编程AOP

1、Maven依赖

首先肯定是要先引入依赖,依赖如下:

        <!--spring切面aop依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

2、自定义注解,注入点方法注解

为什么要自定义注解,这里要提到面向切面编程思想:

这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。

AOP即面向切面编程,可以说是OOP面向对象编程的补充和完善。
AOP的思想为在代码执行过程中,动态嵌入其他代码,叫做面向切面编程。常见的使用场景有事务日志

知道这点后,就不难察觉AOP中有个服务的中心,就是目标方法,因为是对一个方法进行横向的扩展,而在Spring Boot中标注一个方法,使用注解十分方便。

总而言之,自定义的这个注解,标注的就是需要横向拓展的方法。代码如下:

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

//注解放置的目标位置,METHOD是可注解在方法级别上的
@Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.TYPE})
//注解在哪个阶段执行
@Retention(RetentionPolicy.RUNTIME)
public @interface ActionLog {
    String value() default "";
}

3、实现切面日志前的一些准备

这一步主要是将日志持久化进数据库的一些实现。

3.1、定义一个日志类,用于封装日志信息

import lombok.Data;
import com.fasterxml.jackson.annotation.JsonFormat;

@Data
public class LogDto {
    private String className;
    private String methodName;
    private String params;
    private Long executionTime;
    private String result;
    private Short status;
    private String operation;
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private String createDate;
}

3.2、实现日志LogDto写进数据库操作,这里省略,简化为Service接口。

public interface LogDtoService {
    /**
     * 将logDto写入到数据库
     */
    public Result<String> addLog(LogDto logDto);
}

4、使用切面完成日志的收集

这里才是重点戏,使用找到AOP切面,并对切面进行扩展,大致来说就是,找到标注了@ActionLog注解的Controller方法,然后在方法执行完成后,将执行过程的一些参数封装进LogDto中,最后写进数据库。

为了方便理解,这个类拆分几部分记录

4.1、先定义一个类

import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect     //声明这是一个切面
@Component
public class ActionLogAspect1 {

    @Autowired
    LogDtoService logDtoService;

    /**
     * 下面开始拆分方法进行记录
     */
    //method1(){}
    //method2(){}

}

4.2、定义切点 ,即是标注了@ActionLog注解的Controller方法

    /**
     * 定义切点 (controller切点,注解拦截)
     */
    @Pointcut("@annotation(com.jankin.annotation.ActionLog)")
	//com.jankin.annotation.ActionLog是自定义注解即ActionLog的全类名
    public void logPointCut() {
    }

表示凡是标注了@ActionLog这个注解的方法都会进行切面写日志

4.3、定义切面,即在切点(方法)执行的哪个位置进行扩展

这里使用的是环绕通知,忘记了的话就自己去查

    /**
     * 定义切面
     *
     * @param joinPoint 环绕通知
     */
    @Around(value = "logPointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object result;
        long beginTime = System.currentTimeMillis();
        try {
            //执行目标方法,将结果记录并返回
            result = joinPoint.proceed();
        } catch (Throwable e) {
            //执行出错,记录出错日志
            long time = System.currentTimeMillis() - beginTime;
            //将错误日志写进数据库
            //saveLog是本类中封装的方法,在下面有说
            saveLog(joinPoint, time, 0, e.getMessage().trim());
            throw e;
        }
        long time = System.currentTimeMillis() - beginTime;
       //将成功日志写进数据库
        saveLog(joinPoint, time, 1, result);
        return result;
    }

4.4、上面引用的saveLog方法

saveLog方法中传过来joinPoint参数,可以通过其获得更多的信息

    /**
     * 将操作记录存入数据库
     *
     * @param joinPoint jo
     * @author xuhongchun
     * @date 2020/6/30 1:35 下午
     */
    private void saveLog(ProceedingJoinPoint joinPoint, Long time, Integer status, String message) {
        
        LogDto logDto=new LogDto();
        //获取类名className,目标方法的类名
        String className = joinPoint.getTarget().getClass().getName();
        //获取方法名methodName
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        String methodName = signature.getName();
        //获取参数params
        Object[] argValues = joinPoint.getArgs();
        Map<String, Object> param = new HashMap<>(8);
        if (argValues != null) {
            for (int i = 0; i < argValues.length; i++) {
                Object o = argValues[i];
                if (o instanceof HttpServletRequest || o instanceof HttpServletResponse) {
                    continue;
                }
                param.put(argNames[i], argValues[i]);
            }
        }
        //获取操作所需时长executionTime,方法参数已经传进来time
        //获取操作需要的记录result,成功的话返回操作结果,失败的话返回异常信息,方法参数已经传进来message
        //获取操作状态status,1代表成功,0代表失败,方法参数已经传进来status
        //获取操作操作解释operation,即是标注在目标方法上的@ActionLog默认带的参数
        Method method = signature.getMethod();
        String operation = "";
        if (null != method) {
            ActionLog log = method.getAnnotation(ActionLog.class);
            operation = log.value();
        }
        //将以上各参数封装进日志类LogDto中
        logDto.setMethodName(methodName);
        logDto.setParams(params);
        logDto.setExecutionTime(executionTime);
        logDto.setResult(result);
        logDto.setStatus(status);
        logDto.setOperation(operation);
		//LoginDto持久化写入数据库
        logDtoService.addLog(logDto);
        //至此,日志结束
    }

5、测试

编写一个Controller,然后通过接口访问handler方法,通过浏览器或者postman访问接口即可

Controller代码如下:

    @ActionLog("登陆")
    @PostMapping("login")
    public Result<String> login(@RequestBody LoginDTO loginDTO) {
        return loginService.login(loginDTO);
    }

6、总结

这个实现还是比较直观的,花点小心机就能很好掌握,主要是通过这个小例子,还能很好地了解面向切面编程的思想,日后还可以考虑通过自定义注解+Aspect实现更多更有趣的功能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 Spring Boot 中,可以使用 AOP(面向切面编程)来实现切面环绕。通过使用 AOP,我们可以在应用程序的不同部分添加横切关注点,而无需修改这些部分的源代码。 下面是一个示例,展示了如何在 Spring Boot 中使用 AOP 进行切面环绕: 1.首先,我们需要创建一个切面类,该类需要使用 @Aspect 注解进行标注。 ```java @Aspect @Component public class LoggingAspect { } ``` 2.在切面类中,我们需要创建一个环绕通知方法,该方法需要使用 @Around 注解进行标注。 ```java @Around("execution(* com.example.demo.service.*.*(..))") public Object logMethodExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); Object result = joinPoint.proceed(); long endTime = System.currentTimeMillis(); logger.info(joinPoint.getSignature() + " executed in " + (endTime - startTime) + "ms"); return result; } ``` 3.在上面的代码中,我们使用 @Around 注解来标注 logMethodExecutionTime 方法,该方法将会在 service 包下的所有方法执行前后进行环绕通知。在方法中,我们记录了方法的执行时间,并通过日志打印出来。 4.最后,我们需要在 Spring Boot 应用程序启动类上添加 @EnableAspectJAutoProxy 注解来启用 AOP 功能。 ```java @SpringBootApplication @EnableAspectJAutoProxy public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } ``` 通过上述步骤,我们就可以在 Spring Boot 应用程序中使用 AOP 进行切面环绕了。 ### 回答2: Spring Boot是一个开发框架,可以简化Java开发过程。切面Spring AOP(面向切面编程)的一个重要概念。环绕通知则是AOP中一种通知类型。 在Spring Boot中,我们可以使用切面环绕来实现一些跨越多个模块的通用逻辑。切面环绕可以在目标方法执行之前和之后执行一些自定义的业务逻辑,同时也可以修改目标方法的返回值或处理异常。 首先,我们需要创建一个切面类,通过使用Spring提供的注解来定义切面的具体逻辑。我们可以使用@Aspect注解将一个类标记为切面类,接着使用@Before、@After、@Around等注解来定义对应的切入点和通知类型。 在切面类中,我们需要定义一个环绕通知的方法,并在方法上使用@Around注解。在环绕通知方法中,我们可以通过ProceedingJoinPoint参数来获取目标方法的信息。可以利用这个参数,在目标方法执行之前编写一些逻辑,在目标方法执行之后编写一些逻辑。同时,我们还可以通过proceed方法调用目标方法,并修改返回值或处理异常。 当定义好切面类后,我们需要在应用的配置类中启用切面。可以使用@EnableAspectJAutoProxy注解来启用切面的自动代理功能。这样,Spring Boot会自动根据切面类的注解配置生成对应的代理类,并在目标方法执行的时候,触发切面的逻辑。 总结来说,Spring Boot切面环绕是一种通过使用切面类来定义环绕通知,实现一些跨越多个模块的通用逻辑的方式。通过切面环绕可以在目标方法执行之前和之后编写自定义的业务逻辑,同时还可以修改返回值或处理异常。 ### 回答3: Spring Boot是一个用于构建独立的、生产级的Spring应用程序的框架。切面Aspect)是Spring框架中的一个重要概念,用于对程序中的特定方法进行拦截、增强或修改等操作。 在Spring Boot中使用切面环绕的方式,可以实现对目标方法进行前置、后置、异常和最终通知的处理。首先,我们需要创建一个切面类,并使用@Aspect注解进行标记。在切面类中,我们可以定义切点(Pointcut)和通知(Advice)。 切点指定了在何处拦截方法,可以使用@Pointcut注解进行定义。通知是在方法执行前后或发生异常时执行的一段代码,可以使用@Before、@After、@AfterReturning和@AfterThrowing等注解进行标记。在通知中,我们可以获取方法的参数、返回值和异常等信息,并根据需要进行处理。 除了使用注解方式,我们还可以通过编程方式配置切面环绕。可以继承org.aspectj.lang.annotation.AspectJProxyFactory类,并在其中添加切点和通知等配置。然后,通过该类的getProxy()方法获取代理对象,并使用代理对象调用目标方法。 在使用切面环绕时,我们可以通过在应用的配置文件中添加@EnableAspectJAutoProxy注解,来启用Spring的AOP功能。此外,我们还可以使用@Order注解来确定切面的执行顺序,使用@Around注解来实现对目标方法的环绕处理。 总之,Spring Boot切面环绕是一种强大的功能,它可以对程序中的特定方法进行拦截和增强等操作。通过使用切面环绕,我们可以实现非侵入式的功能扩展,提高代码的复用性和可维护性,并实现更好的业务逻辑控制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值