Java自定义注解开发

目录

一、JAVA注解

1、java注解的定义

2、java注解的分类

                 2.1 JDK基本注解

  2.2 JDK元注解

    2.3 自定义注解

3、注解分类

        3.1标记Annotation: 

   3.2 元数据Annotation: 

二、自定义注解开发

1、自定义注解的定义

2、实例

三、用自定义注解完成切面日志操作

        一、传统的切面日志操作

                 1、先定义一个切面类:

  2、定义一个测试类:

二、自定义注解完成切面日志操作

                   1、定义一个专用于切面的注解类

   2、在切面类中改变注解成专用于

3、在对应方法上注入注解

  三、自定义注解和传统切面日志操作对比:

四、自定义注解完成前端响应返回

           1、定义好四个自定义帮助类:

 2、测试实现


一、JAVA注解

1、java注解的定义

 Java注解是附加在代码中的一些元信息,用于一些工具在编译、
   运行时进行解析和使用,起到说明配置的功能。

   注解相关类都包含在java.lang.annotation包中。

2、java注解的分类

                 2.1 JDK基本注解

 2.1.1 @Override
      重写
  2.1.2 @Deprecated
      已过时 
  2.1.3 @SuppressWarnings(value = "unchecked") 
      压制编辑器警告

  2.2 JDK元注解

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

 2.2 .2 @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}),也就是此注解可以在方法和类上面使用
  
 2.2 .3 @Inherited:指定被修饰的Annotation将具有继承性 

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

    2.3 自定义注解

(第二个大点讲到 2.2 .4 @Documented:指定被修饰的该Annotation可以被javadoc工具提取成文档.)

3、注解分类

         根据Annotation是否包含成员变量,可以把Annotation分为两类: 

        3.1标记Annotation: 

没有成员变量的Annotation; 这种Annotation仅利用自身的存在与否来提供信息      

   3.2 元数据Annotation: 

包含成员变量的Annotation;他们可以接受(或提供)更多的元数据。

二、自定义注解开发

1、自定义注解的定义

使用@interface关键字, 其定义过程与定义接口非常类似, 需要注意的是:Annotation的成员变量在Annotation定义中是以无参的方法形式来声明的, 其方法名和返回值类型定义了该成员变量的名字和类型, 而且我们还可以使用default关键字为这个成员变量设定默认值

2、实例

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

package com.zxy.annotation;


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

import java.lang.annotation.*;

@Documented
@Target({ElementType.TYPE,ElementType.METHOD})//该注解只能写在类上
@Retention(RetentionPolicy.RUNTIME)//停留在源码阶段
@Inherited//继承
public @interface  MyAnnotation {
    String value() default "";

    String message() default  "aaa";
}

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

2、@Target({ElementType.TYPE,ElementType.METHOD})是指这个注解可以用在方法上面

在对应枚举中有相关属性介绍:

public enum ElementType {
    /** Class, interface (including annotation type), or enum declaration */
    TYPE,类

    /** Field declaration (includes enum constants) */
    FIELD,属性

    /** Method declaration */
    METHOD,

    /** Formal parameter declaration */
    PARAMETER,

    /** Constructor declaration */
    CONSTRUCTOR,

    /** Local variable declaration */
    LOCAL_VARIABLE,

    /** Annotation type declaration */
    ANNOTATION_TYPE,

    /** Package declaration */
    PACKAGE,

    /**
     * Type parameter declaration
     *
     * @since 1.8
     */
    TYPE_PARAMETER,

    /**
     * Use of a type
     *
     * @since 1.8
     */
    TYPE_USE
}

3、@Inherited:指定被修饰的Annotation具有继承性。

4@Retention注解的情况:写一个测试类进行测试。

  4.1、@Retention(RetentionPolicy.SOURCE) 这个只保存在源码中

MyAnnotation:

package com.yk.annotation;
 
import java.lang.annotation.*;
 
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Inherited
@Retention(RetentionPolicy.SOURCE)
public @interface MyAnnotation {
    String value() default "hello";
 
    String message() default "aaa";
}

 TestController:

package com.zxy.controller;
 
 
import com.zxy.annotation.MyAnnotation;
import org.aspectj.bridge.Message;
import org.springframework.stereotype.Controller;
 
@Controller
@MyAnnotation(message = "bbb")
public class TestController {
    public TestController(){
    }
 
 
    @MyAnnotation
    public void aa(){
 
    }
 
 
 
}
Test
package com.zxy.controller;
 
import com.zxy.annotation.MyAnnotation;
import org.springframework.test.context.TestContext;
 
import java.lang.annotation.Annotation;
 
public class Test {
    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 a = TestController.class.getAnnotation(MyAnnotation.class);
        if(a!=null){
            System.out.println(a.message());
 
        }
    }
}

测试结果:没有出现 MyAnnotation注解,原因是在@Retention(RetentionPolicy.SOURCE)当运行时,只存在源码中。

Java自定义注解开发_第1张图片

target中TestController没有出现MyAnnotation注解

Java自定义注解开发_第2张图片

 4.2@Retention(RetentionPolicy.CLASS)     

Java自定义注解开发_第3张图片

运行结果中没有出现MyAnnotation注解,但是在target中出现了注解。

Java自定义注解开发_第4张图片

 4.3@Retention(RetentionPolicy.RUNTIME)  

当运行结果出来时,会出现MyAnnotation注解:

Java自定义注解开发_第5张图片

三、用自定义注解完成切面日志操作

        一、传统的切面日志操作

                 1、先定义一个切面类:

package com.zxy.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+"bye"+"\t耗時 "+(System.currentTimeMillis()-l1));
//记录一个耗时时间,将证明日志通知
return obj;
    }
 
}

代码分析

1、

/*

* 这是一个切入点

* */

@Pointcut("execution(* com.yk.controller.*Controller.*(..))")

这是读取controller中的以controller结尾的类中的所有方法

2、导航方法

这是写了环绕通知后出现的导航方法

  @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+"bye"+"\t耗時 "+(System.currentTimeMillis()-l1));
return obj;

    }

Java自定义注解开发_第6张图片

  2、定义一个测试类:

package com.zxy.controller;
 
 
import com.zxy.annotation.MyLog;
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 "addyes";
    }
 
    @RequestMapping("/del")
    public  String del(){
 
 
        return "delyes";
    }
 
 
    @RequestMapping("/upd")
    public  String upd(){
 
        return "updyes";
    }
 
 
    @RequestMapping("/list")
    public  String list(){
        return "listyes";
    }
 
 
 
 
 
 
}

运行结果: 

Java自定义注解开发_第7张图片

二、自定义注解完成切面日志操作

                   1、定义一个专用于切面的注解类

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

   2、在切面类中改变注解成专用于

 @Pointcut("execution(* com.yk.controller.*Controller.*(..))")
        
  改变一下这个@Pointcut("@annotation(com.yk.annotation.MyLog)")

3、在对应方法上注入注解

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

运行结果:Java自定义注解开发_第8张图片

  三、自定义注解和传统切面日志操作对比:

自定义注解和传统最大特点的对比是,自定义注解灵活性比传统方式要高

重点突出在一下两行代码:

          @Pointcut("execution(* com.yk.controller.*Controller.*(..))")
          @Pointcut("@annotation(com.yk.annotation.MyLog)")
以及自定义注解的方式:在方法上方注入注解即可

四、自定义注解完成前端响应返回

           1、定义好四个自定义帮助类:

自定义注解类:ResponseResult

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

 Result:响应对象封装类

package com.yk.response;
 
import lombok.Data;
 
import java.io.Serializable;
 
/**
 * 响应对象封装类
 *
 */
@Data
public class Result 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  Result success(T data) {
        return new Result(ResultCode.SUCCESS, data);
    }
 
    /**
     * 失败调用返回的结果(数据携带)
     *
     * @param resultCode 状态枚举
     * @param data       携带的数据
     * @return Result
     */
    public static  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:响应结果码枚举
package com.yk.response;
 
import java.io.Serializable;
 
/**
 * 响应结果码枚举
 *
 *
 */
 
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;
    }
 
}
ResponseParse
 
package com.yk.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;
 
@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;
    }
 
}

 2、测试实现

以下四种情况分别对应着ResponseParse方法中的四种情况

package com.yk.controller;
 
 
import com.yk.annotation.MyLog;
import com.yk.response.ResponseResult;
import com.yk.response.Result;
import com.yk.response.ResultCode;
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");
    }
 
 
 
 
 
}

四种不同情况方法运行结果:

add:

Java自定义注解开发_第9张图片

 del:

Java自定义注解开发_第10张图片

 对应的编码翻译:

USER_ACCOUNT_NOT_FIND(201, "用户名不存在"),

upd:

Java自定义注解开发_第11张图片

list:

Java自定义注解开发_第12张图片

你可能感兴趣的:(java,开发语言,后端)