SpringBoot @ConditionalOnProperty注解 + AOP切面控制日志打印

参考资料

  1. @ConditionalOnProperty的作用和用法


一. 前期准备

1.1 错误信息实体类

import lombok.Data;

@Data
public class ErrorItemEntity {
    
    

    // 错误信息ID
    private String errorMessageId;

    // 错误信息
    private String errorMessage;

    public static ErrorItemEntity of(String errorMessage, String errorItemId) {
    
    
        ErrorItemEntity entity = new ErrorItemEntity();
        entity.errorMessage = errorMessage;
        entity.errorMessageId = errorItemId;
        return entity;
    }
}

1.2 自定义校验异常

import lombok.Data;
import lombok.EqualsAndHashCode;

import java.util.List;

@Data
@EqualsAndHashCode(callSuper = true)
public class ValidationException extends RuntimeException {
    
    

    // 错误信息
    private List<ErrorItemEntity> errors;

    /**
     * 生成ValidationException异常对象
     *
     * @param errors 业务异常信息
     */
    public ValidationException(List<ErrorItemEntity> errors) {
    
    
        super();
        this.errors = errors;
    }
}

1.3 业务页面

1.3.1 前台HTML

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Title</title>
</head>
<body>
    <button id="btn">点击发送请求</button>
</body>
<script src="/js/public/jquery-3.6.0.min.js"></script>
<script th:inline="javascript">

    function doAjax(url, data, callback) {
      
      

        $.ajax({
      
      
            url: url,
            type: 'POST',
            data: JSON.stringify(data),
            contentType : 'application/json;charset=utf-8',
            dataType: 'json',
            success: function (data, status, xhr) {
      
      
                if (!!callback) {
      
      
                    callback(data);
                }
            },
            error: function (xhr, textStatus, errorMessage) {
      
      

                if (xhr.status !== 400) {
      
      
                    location.href = "系统错误页面URL";
                }

                // 获取错误信息,根据错误ID将画面上的错误信息标记红色
                const data = xhr.responseJSON;
                console.log(data.errors);
            }
        });
    }

    $("#btn").click(() => {
      
      

        const url = "http://localhost:8080/test3/check";
        // 错误的数据,from的值不应该比to还要大
        const info = {
      
      
            from: 100,
            to: 10
        };

        doAjax(url, info, function (data) {
      
      

            if (!data.result) {
      
      
                return;
            }

            console.log(data);
        });
    });
</script>
</html>

1.3.2 Form实体类

import lombok.Data;

@Data
public class Test3Form {
    
    

    private Integer from;

    private Integer to;
}

1.3.3 Controller层

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/test3")
public class Test3Controller {
    
    

    @Autowired
    private Test3Service service;

    @GetMapping("/init")
    public ModelAndView init() {
    
    

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("test3");
        return  modelAndView;
    }

    @PostMapping("/check")
    @ResponseBody
    public void check(@RequestBody Test3Form form) throws ValidationException {
    
    

        service.check(form);
    }
}

1.3.4 Service层

import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;

@Service
public class Test3Service {
    
    
	
    public void check(Test3Form form) throws ValidationException {
    
    
		
		// 当from值 > to值的时候,抛出自定义的校验异常
        if (form.getFrom() > form.getTo()) {
    
    
            List<ErrorItemEntity> errors = Collections.singletonList(ErrorItemEntity.of("大小不对", "id名称"));
            throw new ValidationException(errors);
        }
    }
}

二. @ConditionalOnProperty注解和AOP切面的使用

  • @ConditionalOnProperty注解一般用于控制配置类是否生效,prefix为配置文件中的前缀,name 为配置的名称,havingValue的值若与配置文件中相同,则配置文件生效;若值不同则配置类不生效,相当于该配置类并没有被Spring管理。

2.1 配置文件

custom:
  log:
    action: true

2.2 AOP切面 + @ConditionalOnProperty

  • 当被切方法执行过程中抛出异常时,会进入@AfterThrowing注解的方法中执行,在该方法中可以做一些异常的处理逻辑。要注意的是 throwing 属性的值必须要和参数一致,否则会报错。
  • 因为@ConditionalOnProperty注解的havingValue 值和配置文件中的值相同,所以下面所示方法中的定时任务会每隔5秒钟执行一次,并且切面也会执行。如果havingValue的值和配置文件中的值不同的话,则下面类中的定时任务方法和所有的切面方法都不会执行。
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.stream.Collectors;

@Aspect
@Component
@EnableScheduling
@ConditionalOnProperty(prefix = "custom.log", name = "action", havingValue = "true")
public class LogAspect {
    
    

    // 定时任务, 每五秒执行一次。
    @Scheduled(cron = "0/5 * * * * ?")
    public void sendMail(){
    
    
        System.out.println("模拟邮件已经发送出去");
    }

    @Before("execution(* com.example.jmw.controller..*(..))")
    public void beforeInit(JoinPoint join) {
    
    

        Signature signature = join.getSignature();
        Class declaringType = signature.getDeclaringType();
        String simpleName = declaringType.getSimpleName();
        String name = declaringType.getName();
        Logger log = LoggerFactory.getLogger(name);

        log.info("====================================================================");
        log.info("[" + simpleName + "." + name + "] START");
        log.info("====================================================================");
    }

    @After("execution(* com.example.jmw.controller..*(..))")
    public void afterInit(JoinPoint join) {
    
    

        Signature signature = join.getSignature();
        Logger log = LoggerFactory.getLogger(signature.getDeclaringType().getName());

        log.info("====================================================================");
        log.info("[" + signature.getDeclaringType().getSimpleName() + "." + signature.getName() + "] START");
        log.info("====================================================================");
    }

    @AfterThrowing(value = "execution(* com.example.jmw.controller..*(..))", throwing = "ex")
    public void afterExceptionThrowing(JoinPoint jp, Exception ex) {
    
    

        // 如果抛出的是我们自定义的校验异常的话
        if (ex instanceof ValidationException) {
    
    

            // 获取出所有的异常信息
            ValidationException exception = (ValidationException) ex;
            List<ErrorItemEntity> errors = exception.getErrors();
            String errorMsg = errors.stream().map(ErrorItemEntity::getErrorMessage).collect(Collectors.joining("-"));

            Signature signature = jp.getSignature();
            Logger log = LoggerFactory.getLogger(signature.getDeclaringType().getName());
            log.info("====================================================================");
            log.debug("[" + signature.getDeclaringType().getSimpleName() + "." + signature.getName() + "] Validation: {}", errorMsg);
            log.info("====================================================================");
        }
    }
}

2.3 效果

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/feyehong/article/details/130974861