前言

AOP的原理:AOP将业务逻辑组件和切面类都加入到容器中,负责在业务逻辑运行的时候将日志进行打印,切面类负责动态感知MathCalculator.div运行到哪里然后执行。通过@Aspect通知注解给切面类的目标方法标注何时何地运行。



文章目录

  • 前言
  • 一、Aop是什么?
  • 二、使用步骤
  • 1.导入AOP依赖
  • 2.自定义操作日志注解
  • 3.设置操作日志切入点,在注解的位置切入
  • 4.request获取的参数数组转Map便于存储
  • 5.连接点正常执行完成后执行(连接点抛出异常,则不会执行)
  • 6.Controller添加创建的注解@OperLog
  • 7.PostMan测试
  • 更多功能(抛出异常日志记录)



一、Aop是什么?

Aop,面向切面编程,“切面”就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来的部分,Aop的存在有助于减少系统的冗余代码,降低模块间的耦合度,能有效提高可操作性和可维护性。
其作用,就是把业务中冗余的代码整合提取,在不变更业务代码的同时,为已存在业务增加可扩展性。(例如:日志记录,性能统计,安全控制,事务处理,异常处理及扩展)

二、使用步骤

1.导入AOP依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.自定义操作日志注解

代码如下(示例):

@Target(ElementType.METHOD)/*注解放置的目标位置,METHOD是可注解在方法级别上*/
@Retention(RetentionPolicy.RUNTIME)/*注解在哪个阶段执行,RUNTIME是注释将由编译器记录在类文件中,并在运行时由VM保留*/
@Documented
public @interface OperLog {
    /**
     * 创建注解-操作描述
     * @return
     */
    String operDescribe() default "";
}

3.设置操作日志切入点,在注解的位置切入

@Pointcut("@annotation(com.XX.XX.OperLog)")
    public void operLogPoinCut() {
    }

4.request获取的参数数组转Map便于存储

public Map<String, String> ParameterMapToMap(Map<String, String[]> parameterMap) {
        Map<String, String> map = new HashMap<>();
        for (String key : parameterMap.keySet()) {
            map.put(key, parameterMap.get(key)[0]);
        }
        return map;
    }

5.连接点正常执行完成后执行(连接点抛出异常,则不会执行)

@AfterReturning(value = "operLogCutIn()", returning = "returnParameters")
    public void saveOperLog(JoinPoint joinPoint, Object returnParameters) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();/*获取RequestAttributes*/
        HttpServletRequest request = (HttpServletRequest) requestAttributes/*从获取RequestAttributes中获取HttpServletRequest的信息*/
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);
        OperLogDto operLogDto = new OperLogDto();
        try {
            // 从切面织入点处通过反射机制获取织入点处的方法
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            // 获取切入点所在的方法
            Method method = signature.getMethod();
            // 获取注解
            OperLog opLog = method.getAnnotation(OperLog.class);
            if (ObjectUtils.isNotEmpty(opLog)) {
                operLogDto.setLogDescribe(opLog.operDescribe());/*操作描述*/
            }
            String className = joinPoint.getTarget().getClass().getName();/*获取请求的类名*/
            String methodName = method.getName();/*获取请求的方法名*/
            operLogDto.setLogMethod(className + "." + methodName);/*类名.方法名==请求方法*/
            operLogDto.setLogRequestParameters(String.valueOf(ParameterMapToMap(request.getParameterMap()))); /*请求参数*/
            operLogDto.setLogReturnParameters(String.valueOf(returnParameters)); /*返回结果*/
            operLogDto.setLogIp(IpUtils.getIP(request)); /*请求IP*/
            operLogDto.setLogUrl(request.getRequestURI()); /*请求URI*/
            operLogDto.setLogOperateUserId("获取当前登录用户ID"); /*请求用户ID*/
            operLogDto.setLogOperateUserName("获取当前登录用户姓名"); /*请求用户名称*/
            operLogDto.setLogCreateTime(new Date());/*操作时间*/
            operLogDto.setSystemName(systemName); /*操作系统*/
            operLogService.save(operLogDto);/*储存日志*/
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

6.Controller添加创建的注解@OperLog

/**
     * 分页列表查询
     * @param dto
     * @param page
     * @return
     */
    @GetMapping(value = "/getAllByPage")
    @OperLog(operDescribe = "分页列表查询日志记录")/*自定义日志记录-操作描述*/
    public Map<String, Object> getAllByPage(OperLogDto dto, Page page) {
        return operLogService.queryPage(dto, page);
    }

7.PostMan测试