Spring Boot 之自定义注解&&切面日志&&前端响应

一,了解自定义注解

1.1 自定义注解的定义、描述

定义:
注解是一种能被添加到java源代码中的元数据,方法、类、参数和包都可以用注解来修饰。

注解可以看作是一种特殊的标记,可以用在方法、类、参数和包上,程序在编译或者运行时可以检测到这些标记而进行一些特殊的处理。

注解不会对所修饰的代码产生直接的影
使用范围:
注解又许多用法,其中有:为编译器提供信息 - 注解能被编译器检测到错误或抑制警告。
编译时和部署时的处理 - 软件工具能处理注解信息从而生成代码,XML文件等等。
运行时的处理 - 有些注解在运行时能被检测到。

1.2 自定义注解的基本语法

注解类型的声明部分:

注解在Java中,与类、接口、枚举类似,因此其声明语法基本一致,只是所使用的关键字有所不同@interface。

在底层实现上,所有定义的注解都会自动继承java.lang.annotation.Annotation接口。

格式:public @Interface 注解名 { 定义内容 }

public @interface MyAnnotation {
}

注解类型的实现部分:

根据我们在自定义类的经验,在类的实现部分无非就是书写构造、属性或方法。但是,在自定义注解中,其实现部分只能定义一个东西:注解类型元素(annotation type element)。咱们来看看其语法:

public @interface MyAnnotation {
    public String name();
	int age();
}

创建注解的基本元素:

修饰符
访问修饰符必须为public,不写默认为pubic;
关键字
关键字为@interface;
注解名称
注解名称为自定义注解的名称,例如上面的XinLinLog 就是注解名称
注解类型元素
注解类型元素是注解中内容,根据需要标志参数,例如上面的注解的value;

1.3 常用的自定义注解(@Target、@Retention、@Inherited、@Documented)

1.3.1 @Target:用于描述注解的使用范围,该注解可以使用在什么地方

Target类型	描述
ElementType.TYPE	应用于类、接口(包括注解类型)、枚举
ElementType.FIELD	应用于属性(包括枚举中的常量)
ElementType.METHOD	应用于方法
ElementType.PARAMETER	应用于方法的形参
ElementType.CONSTRUCTOR	应用于构造函数
ElementType.LOCAL_VARIABLE	应用于局部变量
ElementType.ANNOTATION_TYPE	应用于注解类型
ElementType.PACKAGE	应用于包

备注:例如@Target(ElementType.METHOD),标志的注解使用在方法上,但是我们在这个注解标志在类上,就会报错

1.3.2 @Retention:表明该注解的生命周期

生命周期类型	描述
RetentionPolicy.SOURCE	编译时被丢弃,不包含在类文件中
RetentionPolicy.CLASS	JVM加载时被丢弃,包含在类文件中,默认值
RetentionPolicy.RUNTIME	由JVM 加载,包含在类文件中,在运行时可以被获取到

1.3.3 @Inherited:是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的。如果一个使用了@Inherited修饰的annotation类型被用于一个class,则这个annotation将被用于该class的子类。

1.3.4 @Documented:表明该注解标记的元素可以被Javadoc 或类似的工具文档化

    public static void main(String[] args) {
//      利用反射在程序运行时拿到注解
        for(Annotation a :TestController.class.getAnnotations()){
            System.out.println(a);
            if(a instanceof MyAnnotation){
                System.out.println(((MyAnnotation) a).message());
            }
        }
//      判断遍历出来的注解是否属于某个注解
        MyAnnotation myAnnotation = TestController.class.getAnnotation(MyAnnotation.class);
            if (myAnnotation!=null){
                System.out.println(myAnnotation.message());
            }
    }

二,切面日志

2.0 传统的方法

2.1 定义切面类

package com.yk.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class LogAop {

//    定义切入点
//    @Pointcut("execution(* com.yk.controller.*Controller.*(..))")
    @Pointcut("@annotation(com.yk.annotation.MyLog)")
    public void logger(){}

    @Around("logger()")
    public Object around(ProceedingJoinPoint point){
//     获得当前执行的方法名称
        Signature methodName = point.getSignature();
//     日志输出
        log.info(methodName+"方法执行");
        long l1 = System.currentTimeMillis();
//     让方法执行
        Object obj = null;
        try {
            obj = point.proceed(point.getArgs());
        } catch (Throwable e) {
            e.printStackTrace();
        }
//     日志输出
        log.info(methodName+"方法结束"+"\t耗时"+(System.currentTimeMillis()-l1));
        return obj;
    }

}

2.2 编写测试类

package com.yk.controller;


import com.yk.annotation.MyAnnotation;
import com.yk.annotation.MyLog;
import org.aspectj.bridge.Message;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {

    @MyLog
    @RequestMapping("/add")
    public String add(){
        return "yes";
    }

    @RequestMapping("/del")
    public String del(){
        return "yes";
    }

    @RequestMapping("/upd")
    public String upd(){
        return "yes";
    }

    @MyLog
    @RequestMapping("/list")
    public String list(){ return "yes"; }
}

测试:

2.3 现在利用注解来获取日志 定义一个专用切面的注解

package com.yk.annotation;

import java.lang.annotation.*;

/**
 * @author lgs
 */
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
}

2.4 定义注解切入点

2.5 在对应方法上注入

测试 :

三,前端响应

3.1 自定义注解

package com.yk.res;

import java.lang.annotation.*;

/**
 * @author hgh
 */
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD})
public @interface ResponseResult {

}

3.2 响应对象封装类

package com.yk.res;

import lombok.Data;

import java.io.Serializable;

/**
 * 响应对象封装类
 *
 * @author hgh
 */
@Data
public class Result<T> implements Serializable {

    private final int code;
    private final String message;
    private final T data;

    /**
     * 私有构造, 只允许通过static调用构造
     *
     * @param resultCode 结果枚举
     * @param data       响应数据
     */
    private Result(ResultCode resultCode, T data) {
        this.code = resultCode.getCode();
        this.message = resultCode.getMessage();
        this.data = data;
    }

    /**
     * 成功调用返回的结果(无数据携带)
     *
     * @return Result
     */
    public static Result success() {
        return success(null);
    }

    /**
     * 成功调用返回的结果(数据携带)
     *
     * @return Result
     */
    public static <T> Result success(T data) {
        return new Result(ResultCode.SUCCESS, data);
    }

    /**
     * 失败调用返回的结果(数据携带)
     *
     * @param resultCode 状态枚举
     * @param data       携带的数据
     * @return Result
     */
    public static <T> Result failure(ResultCode resultCode, T data) {
        return new Result(resultCode, data);
    }

    /**
     * 失败调用返回的结果(无数据携带)
     *
     * @param resultCode 状态枚举
     * @return Result
     */
    public static Result failure(ResultCode resultCode) {
        return failure(resultCode, null);
    }

}

3.3 响应结果码枚举

package com.yk.res;

import java.io.Serializable;

/**
 * 响应结果码枚举
 *
 * @author hgh
 */

public enum ResultCode implements Serializable {

    /* 正常状态 */
    SUCCESS(100, "成功"),
    FAILURE(101, "失败"),
    UNKNOWN(102, "未知响应"),
    /**
     * 用户code范围: 200~300;
     */
    USER_ACCOUNT_NOT_FIND(201, "用户名不存在"),
    USER_ACCOUNT_DISABLED(202, "该用户已被禁用"),
    USER_PASSWORD_NOT_MATCH(203, "该用户密码不一致"),
    USER_PERMISSION_ERROR(204, "该用户不具备访问权限"),
    USER_STATE_OFF_LINE(205, "该用户未登录");

    private final Integer code;
    private final String message;

    ResultCode(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    public static ResultCode queryCode(Integer code) {
        for (ResultCode value : values()) {
            if (code.equals(value.code)) {
                return value;
            }
        }
        return UNKNOWN;
    }

}

3.4 ResponseParse

package com.yk.res;

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

/**
 * @author hgh
 */
@RestControllerAdvice
public class ResponseParse implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        //返回值决定他是否需要进入beforeBodyWrite
        return methodParameter.getMethod().isAnnotationPresent(ResponseResult.class);
    }

    @Override
    public Object beforeBodyWrite(Object o, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        //更改返回值
        if (o == null) {
            return Result.success();
        }
        if (o instanceof Integer) {
            return Result.failure(ResultCode.queryCode((Integer) o));
        }
        if (o instanceof ResultCode) {
            return Result.failure((ResultCode) o);
        }
        if (o instanceof Result) {
            return o;
        }
        return null;
    }

}

3.5 测试以下几种不同情况

package com.yk.controller;


import com.yk.annotation.MyAnnotation;
import com.yk.annotation.MyLog;
import com.yk.res.ResponseResult;
import com.yk.res.Result;
import com.yk.res.ResultCode;
import org.aspectj.bridge.Message;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;


@RestController
public class TestController {

    @MyLog
    @RequestMapping("/add")
    @ResponseResult
    public Result add(){
        return Result.success();
    }

    @ResponseResult
    @RequestMapping("/del")
    public  Object del(){
        return 201;
    }

    @ResponseResult
    @RequestMapping("/upd")
    public  Object upd(){
        return ResultCode.USER_ACCOUNT_NOT_FIND;
        //用户名不存在
    }

    @ResponseResult
    @RequestMapping("/list")
    public  Object list(){
        return Result.success("yes");
    }

}

OK!到这就结束了,希望能帮到你!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

亣柒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值