在开发过程中有可能与其他业务系统进行对接开发,获取封装公共的API接口等等,有的时候为了开发的便捷性以及间接性,会需要开发一些自定义的注解,来实现一些业务。
下面就来说一下在SpringBoot里开发自定义注解。

 一、自定义注解的定义

1.自定义注解的规则:

1. Annotation型定义为@interface, 所有的Annotation会自动继承java.lang.Annotation这一接口,并且不能再去继承别的类或是接口.
2. 参数成员只能用public或默认(default)这两个访问权修饰
3. 参数成员只能用基本类型byte,short,char,int,long,float,double,boolean八种基本数据类型和String、Enum、Class、annotations等数据类型,以及这一些类型的数组.
4. 要获取类方法和字段的注解信息,必须通过Java的反射技术来获取 Annotation对象,因为你除此之外没有别的获取注解对象的方法
5. 注解也可以没有定义成员, 不过这样注解就没啥用了

2.如何自定义注解

对注解有了一个基本的认识:注解其实就是一种标记,可以在程序代码中的关键节点(类、方法、变量、参数、包)上打上这些标记,然后程序在编译时或运行时可以检测到这些标记从而执行一些特殊操作。因此可以得出自定义注解使用的基本流程:

  • 第一步,定义注解——相当于定义标记;
  • 第二步,配置注解——把标记打在需要用到的程序代码中;
  • 第三步,解析注解——在编译期或运行时检测到标记,并进行特殊操作。

 二、注解的分类

Java注解的分类

1.jdk基本注解

① @Override
      重写
②@Deprecated
      已过时 
③@SuppressWarnings(value = "unchecked") 
      压制编辑器警告

2. JDK元注解

 元注解用于修饰其他的注解(纪委:管干部的干部)
① @Retention:定义注解的保留策略
      @Retention(RetentionPolicy.SOURCE)             //注解仅存在于源码中,在class字节码文件中不包含
      @Retention(RetentionPolicy.CLASS)              //默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
      @Retention(RetentionPolicy.RUNTIME)            //注解会在class字节码文件中存在,在运行时可以通过反射获取到

② @Target:指定被修饰的Annotation可以放置的位置(被修饰的目标)
      @Target(ElementType.TYPE)                      //接口、类
      @Target(ElementType.FIELD)                     //属性
      @Target(ElementType.METHOD)                    //方法
      @Target(ElementType.PARAMETER)                 //方法参数
      @Target(ElementType.CONSTRUCTOR)               //构造函数
      @Target(ElementType.LOCAL_VARIABLE)            //局部变量
      @Target(ElementType.ANNOTATION_TYPE)           //注解
      @Target(ElementType.PACKAGE)                   //包 
     
      注:可以指定多个位置,例如:
@Target({ElementType.METHOD, ElementType.TYPE}),也就是此注解可以在方法和类上面使用
  
③ @Inherited:指定被修饰的Annotation将具有继承性 

④@Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.
 

3.自定义注解


三、自定义注解的开发

实例:

以一个实例来讲:创建一个注解叫做MyAnnotation,在类的上面有四个源注解。

①、枚举类:enum,指的是常量的集合

②、注解类

Ⅰ、演示@Retention(RetentionPolicy.SOURCE)注解:MyAnnotation.java

package com.xhy.Annotation;

import org.springframework.beans.factory.annotation.Autowired;

import java.lang.annotation.*;

/**
 * @author zjjt
 */
@Documented
@Target({ElementType.TYPE, ElementType.METHOD})//只能写在类上
@Inherited//可以被子类继承
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value() default "";

    String messsge() default "aaaaaaaa";
}

TestController.java:注意这引用了MyAnnotation注解

package com.xhy.controller;
 
import com.xhy.annotation.MyAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
 
@MyAnnotation
@Controller
public class TestController {
 
    @Autowired
    private String name;
 
    @MyAnnotation
    public void aa(){
 
    }
 
}

运行后target层注解消失:注解仅存在于源码中,在class字节码文件中不包含

Ⅱ、MyAnnotation注解为@Retention(RetentionPolicy.RUNTIME)时

        ——注解会在class字节码文件中存在,在运行时可以通过反射获取到

运行test.java:

package com.xhy.controller;
 
import java.lang.annotation.Annotation;
 
public class Test {
    public static void main(String[] args) {
//    反射
        for(Annotation a:TestController.class.getAnnotations()){
            System.out.println(a);
        }
 
    }
}

 Ⅲ、取注解里的属性值

 注解:MyAnnotation.java

String message() default "aaa";

拿值:

package com.xhy.controller;

import com.xhy.annotation.MyAnnotation;

import java.lang.annotation.Annotation;

public class Test {
    public static void main(String[] args) {
//    反射
        for(Annotation a:TestController.class.getAnnotations()){
            if(a instanceof MyAnnotation){
                System.out.println(((MyAnnotation) a).message());
            }
        }

    }
}

Ⅳ、判断在该类有无该注解

 测试:

package com.xhy.controller;
 
import com.xhy.annotation.MyAnnotation;
 
import java.lang.annotation.Annotation;
 
public class Test {
    public static void main(String[] args) {
//        直接将MyAnnotation这注解取出
        MyAnnotation myAnnotation=TestController.class.getAnnotation(MyAnnotation.class);
        if(myAnnotation !=null){
            System.out.println(myAnnotation.message());
        }
 
    }
}

三、完成切面日志操作

当我们在写增删改的时候,会有很多冗余的代码,后期修改很麻烦,如:

 @RequestMapping("/add")
    public String add(){
        System.out.println("xxx在增加");
        System.out.println("增加成功");
        return "yes";
    }

 新建切面:LogAop.java

package com.xhy.aop;
 
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;
 
@Aspect
//类不被识别,将类变成一个组件
@Component
@Slf4j
 
public class LogAop {
//    指定切入的规则,".."代表可有参可无参
    @Pointcut("execution(* com.lv.controller.*Controller.*(..))")
    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 throwable) {
            throwable.printStackTrace();
        }
        log.info(methodName+"走了"+"\t耗时"+(System.currentTimeMillis()-l1));
        return obj;
 
    }
}

使用jrebel运行:

package com.xhy.controller;
 
import com.xhy.annotation.MyAnnotation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@MyAnnotation
//直接返回json数据
@RestController
//返回页面跳转数据
//@Controller
public class TestController {
 
    @RequestMapping("/add")
    public String add(){
        return "yes";
    }
 
    @RequestMapping("/del")
    public String del(){
        return "yes";
    }
 
    @RequestMapping("/upd")
    public String upd(){
        return "yes";
    }
 
 
    @RequestMapping("/list")
    public String list(){
        return "yes";
    }
 
}

 

使用注解来开发aop日志:

新建注解类:MyLog.java

package com.xhy.annotation;
 
import java.lang.annotation.*;
 
@Inherited
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyLog {
 
}

 同样在切面类中,记得改变切入的规则

@Pointcut("@annotation(com.lv.annotation.MyLog)")

需要输出日志的方法就将新建的注解加上

 四、完成前端响应反应

传入四个文件:

ResponseParse.java:

package com.xhy.response;
 
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;
    }
 
}

  ResponseResult.java:

package com.xhy.response;
 
import java.lang.annotation.*;
 
/**
 * @author hgh
 */
@Retention(value = RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.METHOD})
public @interface ResponseResult {
 
}

Result.java:

package com.xhy.response;
 
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);
    }
 
}

ResultCode.java:

package com.xhy.response;
 
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;
    }
 
}

测试:

package com.xhy.controller;
 
import com.xhy.annotation.MyAnnotation;
import com.xhy.annotation.MyLog;
import com.xhy.response.ResponseResult;
import com.xhy.response.Result;
import com.xhy.response.ResultCode;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
@MyAnnotation
//直接返回json数据
@RestController
//返回页面跳转数据
//@Controller
public class TestController {
 
    @MyLog
    @ResponseResult
    @RequestMapping("/add")
    public Result add(){
        return Result.success("yes");
    }
 
    @RequestMapping("/del")
    @ResponseResult
    public Object del(){
        return 201;
    }
 
    @RequestMapping("/upd")
    @ResponseResult
    public Object upd(){
        return ResultCode.USER_ACCOUNT_NOT_FIND;
    }
 
 
    @RequestMapping("/list")
    @ResponseResult
    public Object list(){
        return Result.success("yes");
    }
 
}

增加:

 删除:

 

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐