springboot中多种注解的使用

一  @ControllerAdvice @ExceptionHandler注解

简介:

@ControllerAdvice注解可以简单理解为@controller注解的装饰注解,用法有三种:

  • 全局处理异常
  • 请求参数预处理
  • 预设全局数据

其中全局处理异常是比较常见的方法(和@ExceptionHandler注解)配合使用。

用法:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.ServletException;

@ControllerAdvice
public class GlobalDefaultExceptionAdvice {

    private Logger logger = LoggerFactory.getLogger(GlobalDefaultExceptionAdvice.class);

    @ResponseBody
    @ExceptionHandler(value = Exception.class)
    public ResponseDTO errorHandler(Exception ex) {
        logger.error("Exception:", ex);
        return new ResponseDTO(ResponseEnum.UNKNOWN);
    }

    @ResponseBody
    @ExceptionHandler(value = {HttpMessageNotReadableException.class, ServletException.class,})
    public ResponseDTO paramErrorHandler(Exception ex) {
        logger.error("PARAM_ERR:", ex);
        return new ResponseDTO(ResponseEnum.PARAM_ERR);
    }

    @ResponseBody
    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public ResponseDTO paramErrorHandler(MethodArgumentNotValidException ex) {
        logger.error("MethodArgumentNotValidException:", ex);
        return new ResponseDTO(ResponseEnum.PARAM_ERR, getParamErr(ex.getBindingResult()));
    }

    @ResponseBody
    @ExceptionHandler(value = BindException.class)
    public ResponseDTO paramErrorHandler(BindException ex) {
        logger.error("BindException:", ex);
        return new ResponseDTO(ResponseEnum.PARAM_ERR, getParamErr(ex.getBindingResult()));
    }

    private String getParamErr(BindingResult bindingResult) {
        StringBuilder sb = new StringBuilder();
        for (FieldError error : bindingResult.getFieldErrors()) {
            sb.append(error.getDefaultMessage());
            sb.append(". ");
        }
        return sb.toString();
    }

    @ResponseBody
    @ExceptionHandler(value = BusinessException.class)
    public ResponseDTO errorHandler(BusinessException ex) {
        logger.error("BusinessException:", ex);
        return new ResponseDTO(ex.getResponseEnum(), ex.getMessage());
    }
}

注意事项:

1:可以在异常处理之后返回通用的ResponseVO

2:可以在这里统一打印错误日志,不需要在业务中有过多打印日志的逻辑

3:其他@ControllerAdvice的两种用法可以参考:@ControllerAdvice 的介绍及三种用法

二  @Aspect @Pointcut注解

简介:

@Aspect是spring中面向切面编程的一种思想,么,作用是把当前类标识为一个切面供spring注入,通过和@Pointcut配合达到切面编程的效果。常见的用法有

  • 可以和自定义的注解做到权限控制
  • 可以方便的打印入参和返回值。
  • 可以做性能统计等。 

@Pointcut注解是定义切面范围。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。

    @Pointcut("execution(public * com.robot.web.controller .*.*(..))")
    // execution括号里public可以省略(缺省值),也可以是private(那就只对private访问级别的方法做切面)
    // execution括号里的第一个参数是返回值(*)表示任意返回值
    // execution括号里的第二个参数是定位到哪些包的哪些方法
    // execution括号里的第三个参数(..) 是指方法的参数列表

用法:

这里演示下有自定义注解才打印入参和返回值的demo

import com.alibaba.fastjson.JSON;
import com.robot.web.annoation.MethodLog;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.util.Objects;


@Aspect
@Component
@Slf4j
public class GlobalLogAspect {

    @Pointcut("execution(* com.robot.web.controller .*.*(..))")
    public void logPoint() {
    }

    @Around("logPoint()")
    public Object doLogAround(ProceedingJoinPoint pjp) throws Throwable {
        // 强转成MethodSignature
        MethodSignature methodSignature = (MethodSignature) pjp.getSignature();

        MethodLog containsMethodClass = methodSignature.getMethod().getAnnotation(MethodLog.class);
        Object result;
        if (Objects.nonNull(containsMethodClass) && containsMethodClass.logType().equals(MethodLog.LogType.ALL)) {
            log.info("invoke method={}, params={}", methodSignature.getMethod().getName(), JSON.toJSON(pjp.getArgs()));
            result = pjp.proceed();
            log.info("invoke method={}, result={}", methodSignature.getMethod().getName(), result);
        } else {
            result = pjp.proceed();
        }
        return result;
    }
}
package com.robot.web.controller;

import com.robot.base.conf.TccConf;
import com.robot.base.req.RobotReq;
import com.robot.web.annoation.MethodLog;
import com.robot.web.biz.RobotBiz;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import javax.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

@Api(tags = "机器人相关接口")
@RestController
@Slf4j
public class RobotController {

    @Resource
    private TccConf tccConf;

    @Resource
    private RobotBiz robotBiz;

    @ApiOperation(value = "测试tcc")
    @GetMapping("/tcc")
    @MethodLog
    public String testTcc(@RequestParam("key") String key) {
        return tccConf.getConfigByKey("test");
    }

    @ApiOperation(value = "处理消息")
    @PostMapping("/callBack")
    public String callBack(@RequestBody RobotReq req) {
        return robotBiz.CallBack(req);
    }
}

第一个方法会打印出入参和返回值,第二个方法不会打印。

注意事项:

1:需要把ProceedingJoinPoint的Signature强转成MethodSignature,才能获取方法签名上的注解信息。方便后续的逻辑

2:可以在切面处理过程中改变方法参数,然后再调用方法(调用的时候得带上Object args[])才能生效,通用的方法可以这样处理。之前踩过一个改参数调用jar包时候坑,返回值中的transient修饰的字段信息不能读取出来。

三 @Target @Retention @Documented注解

简介:

这三个注解是元注解,所有的注解都是需要有元注解的(@Target @Retention是必须要有的)。

@Target指定注解作用的范围,可以是类,方法,成员变量。修饰类和方法比较常见。

@Retention指定注解的生命周期,可以是源文件保留,编译时保留,运行时保留。

@Documented注解修饰的注解类会被 JavaDoc 工具提取成文档

用法:

package com.robot.web.annoation;

import lombok.Data;

import java.lang.annotation.*;


@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodLog {

    /**
     * 日志枚举
     */
    enum LogType {
        /**
         * 打印全部
         */
        ALL("全部"),

        /**
         * 打印参数
         */
        PARAM("参数"),

        /**
         * 打印返回值
         */
        RESULT("结果");

        String desc;
        LogType(String desc) {
            this.desc = desc;
        }
    }

    LogType logType() default LogType.ALL;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值