【Java】基础增强之注解

什么是注解

同 class 和 interface 一样 注解属于一种类型 它在Java1.5版本中开始引入的概念

注解的定义

注解通过 @interface 关键字进行定义 它的形式跟接口类似 ( IEAD创建注解如下图 )

元注解

元注解是可以注解到注解上的注解 

很绕口 简单的理解就是 在我们自定义注解上使用的Java API注解

元注解有 @Retention ,@Document ,@Target ,@Inherited ,@Repeatable

1.  @Retention  (常用)

Retention的英文意为保留期 当 @Retention 应用到一个注解上的时候,它解释说明这个注解的存活时间 其取值范围如下

RetentionPolicy.SOURCE    注解只在源码阶段保留,在编译器进行编译时它将被丢弃
RetentionPolicy.CLASS    注解只被保留到编译进行的时候,它并不会被加载到Jvm中    
RetentionPolicy.RUNTIME    注解可以保留到程序运行的时候 它会被加载到Jvm中 所以程序运行时获取到它

 

2. @Documented

它的作用是能够将注解的元素包含到JavaDoc中

3. @Target (常用)

指定注解可以运用到那些地方 

ElementType.ANNOTATION_TYPE    可以给一个注解进行注解
ElementType.CONSTRUCTOR    可以给构造方法进行注解
ElementType.FIELD    可以给属性进行注解
ElementType.LOCAL_VARIABLE    可以给局部变量进行注解
ElementType.METHOD    可以给方法进行注解
ElementType.PACKAGE    可以给一个包进行注解
ElementType.PARAMETER    可以给一个方法内的参数进行注
ElementType.TYPE    可以给一个类型进行注解 比如类 接口 枚举

4. @Inherited

是继承的意思 但它并不是说注解本身可以继承 ,而是说如果一个超类被@Inherited注解过的注解进行注解的话那么它的子类没有任何注解应用的话,那么这个子类就继承了超类的注解

5. @Repeatable

@Repeatable 是Java 1.8才有的 是重复的意思  我觉得这篇文章写得简单易懂 传送门

注解的属性

注解的属性也叫成员变量,注解只能有成员变量,没有方法,注解的成员变量在注解的定义中以为  无参数的方法 形式来声明其方法名定义了该成员变量的名字,其发挥值定义了该成员变量的类型

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log{
	int id();
	String msg();
}

Log这个注解中有 id 和 msg 两个属性 赋值方式参见如下代码

@Log(id = 3,msg = "hello")
public class Test{

}

需要注意的是,在注解中定义属性时它的类型必须是8种基本数据类型外加类、接口、注解 及它们的数组

注解中的默认值可以使用 defaault 指定 参见如下代码

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log{
    public int id() default -1;

    public String msg() default "requestMessage";
}

注解的使用与获取

首先我们定义一个Log注解

package com.it520.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {

    // 定义注解的msg()属性 默认值为requestMessage
    String msg() default "requestMessage";

}

然后我们去使用这个注解

package com.it520.annotation;

import org.junit.jupiter.api.Test;


@Log // 因为该注解有默认值 所以我们在此处不需要指定其值
public class LogTest {

    @Test
    public void TEST_01() {

        // 首先判断这个类上是否包含注解
        boolean hasAnnotation = LogTest.class.isAnnotationPresent(Log.class);

        if (hasAnnotation) { // 如果有注解

            // 通过反射获取注解
            Log log = LogTest.class.getAnnotation(Log.class);

            System.out.println("msg:" + log.msg());
        }

    }
}

打印结果为 msg:requestMessage

getAnnotation()源码简要剖析

    /**
     * @throws NullPointerException {@inheritDoc}
     * @since 1.5
     */
    @SuppressWarnings("unchecked")
    public <A extends Annotation> A getAnnotation(Class<A> annotationClass) {
        
        // 如果该注解类不存在 则抛出空指针异常
        Objects.requireNonNull(annotationClass);

        // 主要是这段代码
        return (A) annotationData().annotations.get(annotationClass);
    }

(A) annotationData().annotations.get(annotationClass)

① annotationData()  其实就是通过反射去获取一个AnnotationData对象

② AnnotationData 是Class类的一个静态内部类 该类从Java1.5以后存在 (源码部分我加了适当中文注解 这个类很简单 就三个属性 一个构造方法)

    private static class AnnotationData {
        // 这是一个map集合 装载了类中的所有注解 并且该属性使用了final修饰 不可修改其值
        final Map<Class<? extends Annotation>, Annotation> annotations;
        // 同上
        final Map<Class<? extends Annotation>, Annotation> declaredAnnotations;

        // Value of classRedefinedCount when we created this AnnotationData instance
        // Class类中给出其默认值为 0 
        final int redefinedCount;

        // 这是该类的构造方法
        AnnotationData(Map<Class<? extends Annotation>, Annotation> annotations,
                       Map<Class<? extends Annotation>, Annotation> declaredAnnotations,
                       int redefinedCount) {
            this.annotations = annotations;
            this.declaredAnnotations = declaredAnnotations;
            this.redefinedCount = redefinedCount;
        }
    }

所以 annotationData().annotations 其实返回的是一个装载了该类所有注解的 map 集合

③ 最后通过 map 集合的 get 方法去获取这个注解类

④ 拿到这个注解类后 我们就可以获取里面的注解属性 与Java类是一样的

注解在SpringBoot工程中的使用

1. 首先我们创建一个SpringBoot工程 导入如下依赖

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>

    <dependencies>
        
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>


    </dependencies>

2. 在application.yml中配置端口号

server:
  port: 8099

 

3. 自定义一个注解 

package com.it520.annotation.csutom;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * @author 玛丽莲梦明
 * @描述 自定义注解
 */
// 指定注解在运行时获取
@Retention(RetentionPolicy.RUNTIME)
// 声明注解可以使用在类上 可以使用在方法上
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface RequestLog {

    // 请求的方法名
    String requestMethod() default "";

    // 请求方式类型
    String requestType() default "";
}

 4. 创建Controller

package com.it520.annotation.csutom;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.lang.reflect.Method;

/**
 * @author 玛丽莲梦明
 * @描述 注解测试 获取方法上注解的内容 获取方法上的注解内容
 */
@RestController
@RequestMapping("annotation")
public class LogController {


    @GetMapping("/log")
    @RequestLog(requestMethod = "annotationTest", requestType = "GET")
    public String annotationTest() {

        // 通过反射获取类注解上的内容
        RequestLog log = LogController.class.getAnnotation(RequestLog.class);
        System.out.println(log.requestMethod());
        System.out.println(log.requestType());


        // 通过反射获取到方法注解上的内容
        Method[] methods = LogController.class.getMethods();
        for (Method method : methods) {

            if (method.getName().equals("annotationTest")) {
                RequestLog ann = method.getAnnotation(RequestLog.class);
                System.out.println(ann.requestType());
                System.out.println(ann.requestMethod());
            }
        }

        return "RequestLog";
    }


}

5.启动工程 并通过浏览器访问 localhost:8099/annotation/log 

打印结果如下 可以看到我们已经获取到类上和方法上的注解属性值了

很多同学想问 获取到了有什么用啊

在实际工作中注解的获取一般不会在Controller中,我这里是为了演示方便

举个栗子 我们的Log注解 就可以在 HandlerInterceptor 处获取并记录用户访问日志

以上 感谢~!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值