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 效果

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值