【七】springboot整合AOP实现日志操作

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: 【七】springboot整合AOP实现日志操作


       介绍:接下来我会把学习阶段学到的框架等知识点进行整合,每一次整合是在前一章的基础上进行的,所以后面的整合不会重复放前面的代码。每次的demo我放在结尾,本次是接着上一章的内容延续的,只增加新增的或者修改的代码。

       上一章进行了Redis的整合,实现了一个用户对应一个token,用户登录失败次数锁定。

本章将整合AOP,实现请求接口后,对日志进行处理,不直接写到逻辑里面,尽量解耦,采用切面方式。

       首先展示下我的目录结构:

       勾选部分为本章整合后新增部分代码。

第一步:新增日志实体类

@Data
@TableName(value = "syslog")
@Accessors(chain = true)
public class SysLog {
 
    private static final long serialVersionUID = 1L;
 
    @TableId(type = IdType.UUID)
    private String id;//id
 
    @TableField("operationUser")
    private String operationUser;//操作人
 
    @TableField("path")
    private String path;//请求路径
 
    @TableField("time")
    private String time;//方法执行时间
 
    @TableField("parameter")
    private String parameter;//方法入参
 
    @TableField("title")
    private String title;//操作方法
 
    @TableField("action")
    private String action;//方法描述
 
    @TableField("sysType")
    private Integer sysType;//系统类型
 
    @TableField("opType")
    private Integer opType;//操作类型
 
    public SysLog(String operationUser, String path, String time,
                      String parameter, String title, String action, Integer sysType, Integer opType) {
        super();
        this.operationUser = operationUser;
        this.path = path;
        this.time = time;
        this.parameter = parameter;
        this.title = title;
        this.action = action;
        this.sysType = sysType;
        this.opType = opType;
    }
 
}

       ps:此处所涉及的注解请不要漏写,关系到mybatis-plus的使用,此处id的自动设置不一定使用 IdType.UUID。

可以采用前面整合的代码生成器进行生成,我在此处就是使用代码生成器生成的,但是需要改进一些地方,不然会报错。

第二步:新增各层代码

第三步:编写切面需要依赖的注解(次重点)

       ps:自定义注解,需要加上框选部分的注解,里面参数的选择在csdn上面有大佬有细讲文章。

第四步:编写切面(重点)

@Aspect
@Component
@EnableAsync
public class SystemLogAspect {
    @Resource
    private SyslogMapper logMapper;//日志 mapper
 
    private String requestPath = null ; // 请求地址
    private long startTimeMillis = 0; // 开始时间
    private long endTimeMillis = 0; // 结束时间
    private String user = null; // 操作人
    private HttpServletRequest request = null;//请求
 
    /**
     * 注解的位置
     */
    @Pointcut("@annotation(com.swagger.demo.config.OperationAnnotation)")
    public void logPointCut() {}
 
    /**
     * @param joinPoint
     * @Description 前置通知  方法调用前触发   记录开始时间,从session中获取操作人
     */
    @Before(value="logPointCut()")
    public void before(JoinPoint joinPoint){
        startTimeMillis = System.currentTimeMillis();
    }
    /**
     * @param joinPoint
     * @Description 获取入参方法参数
     * @return
     */
    public Map<String, Object> getNameAndValue(JoinPoint joinPoint) {
        Map<String, Object> param = new HashMap<>();
        Object[] paramValues = joinPoint.getArgs();
        String[] paramNames = ((CodeSignature)joinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < paramNames.length; i++) {
            if(paramValues[i] instanceof Integer || paramValues[i] instanceof String) {
                param.put(paramNames[i], paramValues[i]);
            }
        }
        return param;
    }
    /**
     * @param joinPoint
     * @Description 后置通知    方法调用后触发   记录结束时间 ,操作人 ,入参等
     */
    @After(value="logPointCut()")
    public void after(JoinPoint joinPoint) {
        request = getHttpServletRequest();
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class<?> targetClass = null;
        try {
            targetClass = Class.forName(targetName);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        Method[] methods = targetClass.getMethods();
        String title;
        String action;
        Integer sysType;
        Integer opType;
        Class<?>[] clazzs;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                clazzs = method.getParameterTypes();
                if (clazzs!=null&&clazzs.length == arguments.length
                        &&method.getAnnotation(OperationAnnotation.class)!=null) {
                    request = getHttpServletRequest();
                    requestPath=request.getServletPath();
                    HttpSession session = request.getSession();
                    user = session.getAttribute("userName").toString();
                    title = method.getAnnotation(OperationAnnotation.class).content();
                    action = method.getAnnotation(OperationAnnotation.class).action();
                    sysType = method.getAnnotation(OperationAnnotation.class).sysType();
                    opType = method.getAnnotation(OperationAnnotation.class).opType();
                    endTimeMillis = System.currentTimeMillis();
 
                    SysLog log=new SysLog(user, requestPath,
                            (endTimeMillis-startTimeMillis)+"ms",
                            getNameAndValue(joinPoint).toString(), title, action,sysType,opType);
                    System.out.println("增加参数:"+log);
                    logMapper.insert(log);
//                    break;
                }
            }
        }
    }
    /**
     * @Description: 获取request
     */
    public HttpServletRequest getHttpServletRequest(){
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes)ra;
        HttpServletRequest request = sra.getRequest();
        return request;
    }
    /**
     * @param joinPoint
     * @return 环绕通知
     * @throws Throwable
     */
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        return null;
    }
    /**
     * @param joinPoint
     * @Description 异常通知
     */
    public void throwing(JoinPoint joinPoint) {
        System.out.println("异常通知");
    }
}

       ps:下面对该切面进行一个解释。

       aspect注解表示将该类定义为切面。component注解表示将该类注册到spring容器。

      pointcut注解表示定义切入点,此处表示,切入到 OperationAnnotation注解的位置,定义切入点的参数特别多,可以去细细了解一下。

       before注解表示前置通知,相当于BeforeAdvice的功能,value的值为切入点的名字。

       此方法是获取方法的入参。

       after注解表示后置通知,即使切入点注解切入位置执行完之后执行该部分代码。此处从session里面获取当前用户,表示日志的操作人(在登录时将用户名存入session),实际使用也可以采用其他办法。

       使用mybatis-plus的insert方法直接新增的记录,也可以自己写sql。

补充:改造了一下登录接口,登录成功后报错了用户名到session,如下。

       ps:使用session需要在入参处加上HttpServletRequest request。

       从登录的controller方法到实现类都需要加上该入参。

第五步:演示

       请求登录接口

       结果:

       已经新增成功。

       本期整合到此完毕,接下来会继续更新加强整合,尽情期待。

       访问地址:http://localhost:8088/swagger-ui.html或者http://localhost:8088/doc.html

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
1月前
|
Java API 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——Spring Boot 中的 AOP 处理
本文详细讲解了Spring Boot中的AOP(面向切面编程)处理方法。首先介绍如何引入AOP依赖,通过添加`spring-boot-starter-aop`实现。接着阐述了如何定义和实现AOP切面,包括常用注解如`@Aspect`、`@Pointcut`、`@Before`、`@After`、`@AfterReturning`和`@AfterThrowing`的使用场景与示例代码。通过这些注解,可以分别在方法执行前、后、返回时或抛出异常时插入自定义逻辑,从而实现功能增强或日志记录等操作。最后总结了AOP在实际项目中的重要作用,并提供了课程源码下载链接供进一步学习。
60 0
|
1月前
|
Java 开发者 微服务
微服务——SpringBoot使用归纳——Spring Boot中的切面AOP处理——什么是AOP
本文介绍了Spring Boot中的切面AOP处理。AOP(Aspect Oriented Programming)即面向切面编程,其核心思想是分离关注点。通过AOP,程序可以将与业务逻辑无关的代码(如日志记录、事务管理等)从主要逻辑中抽离,交由专门的“仆人”处理,从而让开发者专注于核心任务。这种机制实现了模块间的灵活组合,使程序结构更加可配置、可扩展。文中以生活化比喻生动阐释了AOP的工作原理及其优势。
42 0
|
1月前
|
Java 微服务 Spring
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录——使用Logger在项目中打印日志
本文介绍了如何在项目中使用Logger打印日志。通过SLF4J和Logback,可设置不同日志级别(如DEBUG、INFO、WARN、ERROR)并支持占位符输出动态信息。示例代码展示了日志在控制器中的应用,说明了日志配置对问题排查的重要性。附课程源码下载链接供实践参考。
72 0
|
1月前
|
存储 Java 文件存储
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— logback.xml 配置文件解析
本文解析了 `logback.xml` 配置文件的详细内容,包括日志输出格式、存储路径、控制台输出及日志级别等关键配置。通过定义 `LOG_PATTERN` 和 `FILE_PATH`,设置日志格式与存储路径;利用 `&lt;appender&gt;` 节点配置控制台和文件输出,支持日志滚动策略(如文件大小限制和保存时长);最后通过 `&lt;logger&gt;` 和 `&lt;root&gt;` 定义日志级别与输出方式。此配置适用于精细化管理日志输出,满足不同场景需求。
138 1
|
1月前
|
SQL Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录—— application.yml 中对日志的配置
在 Spring Boot 项目中,`application.yml` 文件用于配置日志。通过 `logging.config` 指定日志配置文件(如 `logback.xml`),实现日志详细设置。`logging.level` 可定义包的日志输出级别,例如将 `com.itcodai.course03.dao` 包设为 `trace` 级别,便于开发时查看 SQL 操作。日志级别从高到低为 ERROR、WARN、INFO、DEBUG,生产环境建议调整为较高级别以减少日志量。本课程采用 yml 格式,因其层次清晰,但需注意格式要求。
112 0
|
1月前
|
Java API 开发者
微服务——SpringBoot使用归纳——Spring Boot使用slf4j进行日志记录——slf4j 介绍
在软件开发中,`System.out.println()`常被用于打印信息,但大量使用会增加资源消耗。实际项目推荐使用slf4j结合logback输出日志,效率更高。Slf4j(Simple Logging Facade for Java)是一个日志门面,允许开发者通过统一方式记录日志,无需关心具体日志系统。它支持灵活切换日志实现(如log4j或logback),且具备简洁占位符和日志级别判断等优势。阿里巴巴《Java开发手册》强制要求使用slf4j,以保证日志处理方式的统一性和维护性。使用时只需通过`LoggerFactory`创建日志实例即可。
41 0
|
3月前
|
开发框架 运维 监控
Spring Boot中的日志框架选择
在Spring Boot开发中,日志管理至关重要。常见的日志框架有Logback、Log4j2、Java Util Logging和Slf4j。选择合适的日志框架需考虑性能、灵活性、社区支持及集成配置。本文以Logback为例,演示了如何记录不同级别的日志消息,并强调合理配置日志框架对提升系统可靠性和开发效率的重要性。
|
1月前
|
监控 Java 应用服务中间件
Tomcat log日志解析
理解和解析Tomcat日志文件对于诊断和解决Web应用中的问题至关重要。通过分析 `catalina.out`、`localhost.log`、`localhost_access_log.*.txt`、`manager.log`和 `host-manager.log`等日志文件,可以快速定位和解决问题,确保Tomcat服务器的稳定运行。掌握这些日志解析技巧,可以显著提高运维和开发效率。
108 13
|
1月前
|
SQL 存储 关系型数据库
简单聊聊MySQL的三大日志(Redo Log、Binlog和Undo Log)各有什么区别
在MySQL数据库管理中,理解Redo Log(重做日志)、Binlog(二进制日志)和Undo Log(回滚日志)至关重要。Redo Log确保数据持久性和崩溃恢复;Binlog用于主从复制和数据恢复,记录逻辑操作;Undo Log支持事务的原子性和隔离性,实现回滚与MVCC。三者协同工作,保障事务ACID特性。文章还详细解析了日志写入流程及可能的异常情况,帮助深入理解数据库日志机制。
121 0
|
1月前
|
缓存 Java 编译器

热门文章

最新文章