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

简介: 【七】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日志并进行多维度分析。
目录
相关文章
|
13天前
|
缓存 Java Sentinel
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
Springboot 中使用 Redisson+AOP+自定义注解 实现访问限流与黑名单拦截
|
13天前
|
分布式计算 DataWorks 关系型数据库
DataWorks操作报错合集之在DataWorks中设置了一个任务节点的调度时间,并将其发布到生产环境,但到了指定时间(例如17:30)却没有产生运行实例和相关日志如何解决
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
12天前
|
Java
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
|
12天前
|
Java
java使用AOP切面获取请求日志并记录
java使用AOP切面获取请求日志并记录
|
13天前
|
JavaScript Java API
【JavaEE】Spring Boot - 日志文件
【JavaEE】Spring Boot - 日志文件
6 0
|
13天前
|
XML Java Maven
Springboot整合与使用log4j2日志框架【详解版】
该文介绍了如何在Spring Boot中切换默认的LogBack日志系统至Log4j2。首先,需要在Maven依赖中排除`spring-boot-starter-logging`并引入`spring-boot-starter-log4j2`。其次,创建`log4j2-spring.xml`配置文件放在`src/main/resources`下,配置包括控制台和文件的日志输出、日志格式和文件切分策略。此外,可通过在不同环境的`application.yml`中指定不同的log4j2配置文件。最后,文章提到通过示例代码解释了日志格式中的各种占位符含义。
|
13天前
|
SQL Java 数据库连接
Springboot框架整合Spring JDBC操作数据
JDBC是Java数据库连接API,用于执行SQL并访问多种关系数据库。它包括一系列Java类和接口,用于建立数据库连接、创建数据库操作对象、定义SQL语句、执行操作并处理结果集。直接使用JDBC涉及七个步骤,包括加载驱动、建立连接、创建对象、定义SQL、执行操作、处理结果和关闭资源。Spring Boot的`spring-boot-starter-jdbc`简化了这些步骤,提供了一个在Spring生态中更便捷使用JDBC的封装。集成Spring JDBC需要添加相关依赖,配置数据库连接信息,并通过JdbcTemplate进行数据库操作,如插入、更新、删除和查询。
|
13天前
|
SQL Java 数据库连接
Springboot框架整合Spring Data JPA操作数据
Spring Data JPA是Spring基于ORM和JPA规范封装的框架,简化了数据库操作,提供增删改查等接口,并可通过方法名自动生成查询。集成到Spring Boot需添加相关依赖并配置数据库连接和JPA设置。基础用法包括定义实体类和Repository接口,通过Repository接口可直接进行数据操作。此外,JPA支持关键字查询,如通过`findByAuthor`自动转换为SQL的`WHERE author=?`查询。
|
13天前
|
XML Java 数据库连接
Springboot整合mybatisPlus操作数据库
MyBatis-Plus是MyBatis的增强工具,简化开发、提高效率。它提供官网文档,便于集成到SpringBoot项目中。集成步骤包括添加mybatis-plus-boot-starter和数据库驱动依赖,配置数据源,扫描Mapper类包。Mapper接口继承BaseMapper即可使用基本的CRUD操作。示例代码展示了Service层的增删改查实现。MyBatisPlus还支持逻辑删除、自动填充等功能,同时可与Mybatis XML配合使用,通过调整配置指定XML映射文件位置。
|
13天前
|
Dubbo Java Serverless
Serverless 应用引擎操作报错合集之Nacos中nacos启动正常,访问白页,启动日志显示正常如何解决
Serverless 应用引擎(SAE)是阿里云提供的Serverless PaaS平台,支持Spring Cloud、Dubbo、HSF等主流微服务框架,简化应用的部署、运维和弹性伸缩。在使用SAE过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
Serverless 应用引擎操作报错合集之Nacos中nacos启动正常,访问白页,启动日志显示正常如何解决