一些Annotation学习的笔记

一、注解(Annotation)用途

注解本身不起直接作用,而是起到信息提示的作用

  • 提供信息给编译器/IDE工具
  • 可用于其他工具来产生额外的代码/配置文件等
  • 有些注解可在程序运行时访问,增加程序的动态性(在运行时通过反射来修改注解)

二、java预定义注解

1.普通注解

eg:

@Override	表示继承和复写
@SuppressWarnings	表示压制警告

2.元注解

修饰注解的注解

@Target	设置注解目标范围
@Retention	设置保持性
@Documented	文档
@Inherited 注解继承
@Repetable此注解可以重复修饰

2.1@Retention(保留)

用于修饰其他注解的存在范围

eg: @Retention(RetentionPolicy.RUNTIME)

  • RetentionPolicy.SOURCE 注解仅存在源码, 不在.class文件。通常用于IDE做源码质量分析和监控,eg:@Override

  • RetentionPolicy.CLASS (默认的注解保留策略) 注解存在于.class文件,但是不能被JVM加载

  • RetentionPolicy.RUNTIME 注解可以被JVM运行时访问到。通常情况下可以结合反射来做一些事

2.2@Inherited

普通注解没有继承功能,@Inherited让一个类和它的子类都包含某个注解

2.3@Repetable

自JDK1.8引入,表示被修饰的注解可以重复应用标注,需要定义注解和容器注解

eg:

//注解
@Repeatable(RepeatableAnnotations.class)
public @interface RepeatableAnnotation {
    int a() default 0;
    int b() default 0;
    int c() default 0;
}
//容器注解
public @interface RepeatableAnnotations {
    RepeatableAnnotation[] value();
}
public class RepeatableTest {

    @RepeatableAnnotation(a=1, b=2, c=3)
    @RepeatableAnnotation(a=5, b=2, c=4)
    public static void add(int a, int b, int c){
        if(a + b < c){
            System.out.println("a + b < c");
        }
    }
}

2.4@Documented

指明这个注解可以被javadoc工具解析,形成帮助文档

三、自定义注解

1.定义注解

注解可以包括的类型

  • 8种基本类型(int/short/long/float/double/byte/char/boolean)
  • String
  • Class
  • enum类型
  • 注解类型
  • 由以上类型组成的数组
import java.lang.annotation.*;

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
//注解接口
public @interface singleTest {
    //注解的成员属性 属性名value 缺省值""
    String value() default "";
}
public @interface BugReport
{
    enum Status{NOTBUG, FIXED, CONFIRMED, UNCONFIRMED};
    boolean showStopper() default true;
    Status status() default Status.UNCONFIRMED;
    String[] reportedBy();
}

2.使用注解

//定义
public @interface SingleValue{
    int value() default 0;
} 
//使用
@SingleValue
@SingleValue(5)
@SingleValue(value=5)
//定义
public @interface MutipleValues{
    int a() default 0;
    int b() default 0;
}
//使用
@MutipleValues
@MutipleValues(a=1)
@MutipleValues(a=1, b=2)
@MutipleValues(b=2, a=1)
//错误写法
@MutipleValues(1, 2)

3.注解使用的位置

eg: @Retention(RetentionPolicy.RUNTIME) //表示该注解会保留在class文件中,并能被JVM加载

​ @Target(ElementType.METHOD) //表示该注解只能用于方法

@Target可以限定位置

允许的位置

  • 接口

  • 方法

  • 成员变量

  • 构造器

  • 局部变量/形参变量/类型参数

4.自定义注解实例

Test.java 定义注解

package org.xll.HelloWorld.test;

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

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

Foo.java 使用注解

package org.xll.HelloWorld.test;

public class Foo {
    @Test
    public static void m1(){

    }
    public static void m2(){

    }
    @Test
    public static void m3(){
        throw new RuntimeException("Boom");
    }
    public static void m4(){

    }
    @Test
    public static void m5(){

    }
    public static void m6(){

    }
    @Test
    public static void m7(){
        throw new RuntimeException("Crash");
    }
    public static void m8(){

    }
}

Main.java 通过反射判断加注解的方法

package org.xll.HelloWorld.test;

import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception{
        int passed = 0, failed = 0;
        String className = "org.xll.HelloWorld.test.Foo";
        //通过反射获取 Foo 所有方法
        for(Method method: Class.forName(className).getMethods()){
            //如果方法标有 @Test 注解
            if(method.isAnnotationPresent(Test.class)) {
                try {
                    //调用此方法
                    method.invoke(null);
                    passed++;
                } catch (Throwable ex) {
                    System.out.println("Test " +  method.getName() + " failed: " + ex.getCause());
                    failed++;
                }
            }
        }
        System.out.printf("Passed: %d, Failed %d%n", passed, failed);
    }
}

运行结果

Test m3 failed: java.lang.RuntimeException: Boom
Test m7 failed: java.lang.RuntimeException: Crash
Passed: 2, Failed 2

四、RUNTIME注解的实现本质

eg:

Fruit.java 定义注解

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

@Retention(RetentionPolicy.RUNTIME)
public @interface Fruit {
    String name() default "";
}

RuntimeMain.java 使用注解

@Fruit(name = "Apple")
public class RuntimeMain {
    public static void main(String[] args) {
        Fruit fruit = RuntimeMain.class.getAnnotation(Fruit.class);
        System.out.println(fruit.name());
        //Apple
        System.out.println(fruit.getClass().getName());
        //com.sun.proxy.$Proxy1 (代理类型)
        System.out.println(fruit.getClass().getGenericInterfaces()[0]);
        //interface org.xll.HelloWorld.runtimeTest.Fruit
        InvocationHandler h = Proxy.getInvocationHandler(fruit);
        System.out.println(h.getClass().getName());
        //sun.reflect.annotation.AnnotationInvocationHandler
    }
}

将Fruit.class反编译,可以发现注解定义 @interface 在字节码中是以 interface 存在的

在这里插入图片描述

RUNTIME注解调用路线

  • 注解采用接口中的方法来表示变量
  • java为注解产生一个代理类。这个代理类包括一个AnnotationInvocationHandle成员变量
  • AnnotationInvocationHandle有一个Map的成员变量,用于存储所有的注解的属性赋值
  • 在程序中,调用注解接口的方法,将会被代理类接管,然后根据方法的名字,到Map中拿相应的Value并返回

RUNTIME注解设计思路

  • 传统接口中的成员变量,都是 public final static
  • 注解需要随意赋值
    • 注解方法表示变量
    • 采用代理类拦截注解方法访问
    • 所有的注解的赋值,都放在Map中,访问速度快
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值