Java的注解机制

注解(Annotation)

Java注解在jdk1.5被引入,是一种注释机制。在类、方法、变量、参数和包等都可以被标注。注意,Java中的注解只能有数据,不能有行为。实际开发中注解做配置要比XML效率高,但是耦合度也是要高于XML的。

注解的实现原理

Java中的注解有三个主干类
第一个,Annotation类

package java.lang.annotation;
public interface Annotation {

    boolean equals(Object obj);

    int hashCode();

    String toString();

    Class<? extends Annotation> annotationType();
}

这个Annotation是所有注解的父接口,证明一下。

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

@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAn {
    int value() default 8;
}

在这里插入图片描述

可标记位置的枚举类

package java.lang.annotation;

public enum ElementType {
    TYPE,               /* 类、接口(包括注释类型)或枚举声明  */

    FIELD,              /* 字段声明(包括枚举常量)  */

    METHOD,             /* 方法声明  */

    PARAMETER,          /* 参数声明  */

    CONSTRUCTOR,        /* 构造方法声明  */

    LOCAL_VARIABLE,     /* 局部变量声明  */

    ANNOTATION_TYPE,    /* 注释类型声明  */

    PACKAGE             /* 包声明  */
}

注解生命周期的枚举

package java.lang.annotation;
public enum RetentionPolicy {
    SOURCE,            /* Annotation信息仅存在于编译器处理期间,编译器处理完之后就没有该Annotation信息了  */

    CLASS,             /* 编译器将Annotation存储于类对应的.class文件中。默认行为  */

    RUNTIME            /* 编译器将Annotation存储于class文件中,并且可由JVM读入 */
}

生命周期:Source<Class<Runtime
使用哪个看具体需求。

那么注解到底是怎么实现的呢,它又是什么呢?

@TestAn
public class Test {
    public static void main(String[] args) {
        Annotation annotation = Test.class.getAnnotation(TestAn.class);
        System.out.println(annotation.value());
    }
}

在这里插入图片描述
可以看到,invokeinterface,说明注解是接口。下一个问题随之而来,我们得到了一个Annotation类型的实例,那么这个他是怎么被实例化出来的呢?

在这里插入图片描述
可以看到,这是一个代理类,有一个AnnotationInvocationHandler类型的成员,这个类实现了InvocationHandler,用来封装解析注解的逻辑。

到了这里实例我们应该已经知道是怎么创建的了,在调用getAnnotation方法时,jdk使用动态代理的方式创建了一个$Proxy对象,使用Proxy对象的newProxyInstance方法,最后返回一个实例。

总结 :注解本质是一个继承了Annotation 的特殊接口,其具体实现类是Java 运行时生成的动态代理类。而我们通过反射获取注解时,返回的是Java 运行时生成的动态代理对象。通过代理对象调用自定义注解(接口)的方法,会最终调用AnnotationInvocationHandler 的invoke 方法。该方法会从memberValues 这个Map 中索引出对应的值。而memberValues 的来源是Java 常量池。

以下是一些jdk中常用注解
1、@Deprecated – 所标注内容不再被建议使用;
2、@Override – 只能标注方法,表示该方法覆盖父类中的方法;
3、@Documented --所标注内容可以出现在javadoc中;
4、@Inherited – 只能被用来标注“Annotation类型”,它所标注的Annotation具有继承性;
5、@Retention – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的RetentionPolicy属性;
6、@Target – 只能被用来标注“Annotation类型”,而且它被用来指定Annotation的ElementType属性;
7、@SuppressWarnings – 所标注内容产生的警告,编译器会对这些警告保持静默;
8、@interface – 用于定义一个注解;

其中4,5,6,8常用于自定义注解
3,4,5,6这4个元注解都是在jdk的java.lang.annotation包下面,被称为元注解

注解的成员

  1. 成员类型是受限制的,合法的类型包括基本的数据类型以及String,Class,Annotation,Enumeration等。
  2. 如果注解只有一个成员,则成员名必须取名为value(),在使用时可以忽略成员名和赋值号(=)。
  3. 注解类可以没有成员,没有成员的注解称为标识注解。

@Inherited

类继承关系中,子类会继承父类使用的注解中被@Inherited修饰的注解

验证一下哈

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface TestInherited{
    int value() default 8;
}

@TestInherited
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Inherited

@interface InheritedInterface {

}

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

}

@TestInherited
@InheritedInterface
@NotInheritedInterface
class Base{

}

class Sub extends Base{

}

public class AnnotationUsage {
    public static void main(String[] args) {
        Annotation[] annotations = Sub.class.getAnnotations();
        System.out.println(Arrays.toString(annotations));
        System.out.println(Arrays.stream(annotations).allMatch(a -> a.annotationType().equals(InheritedInterface.class)));
        System.out.println(Arrays.stream(annotations).anyMatch(a -> a.annotationType().equals(InheritedInterface.class)));
        System.out.println(Arrays.stream(annotations).noneMatch(a -> a.annotationType().equals(InheritedInterface.class)));

        System.out.println(Arrays.stream(annotations).allMatch(a -> a.annotationType().equals(NotInheritedInterface.class)));
        System.out.println(Arrays.stream(annotations).anyMatch(a -> a.annotationType().equals(NotInheritedInterface.class)));
        System.out.println(Arrays.stream(annotations).noneMatch(a -> a.annotationType().equals(NotInheritedInterface.class)));

System.out.println(Sub.class.getAnnotation(TestInherited.class).value());
    }
}
[@TestInherited(value=8), @InheritedInterface()]
false
true
false
false
false
true
8

去掉TestInherited注解上的Inherited注解,会报空指针。
从输出的annotation也能得到此结论。

[@InheritedInterface()]
true
true
false
false
false
true
Exception in thread "main" java.lang.NullPointerException
	at AnnotationUsage.main(AnnotationUsage.java:47)

Process finished with exit code 1

Annotation 的作用

Annotation 是一个辅助类,它在 Junit、Struts、Spring 等工具框架中被广泛使用。

  1. 生成文档,通过代码里标识的元数据生成javadoc文档。
  2. 编译检查,通过代码里标识的元数据让编译器在编译期间进行检查验证。
  3. 编译时动态处理,编译时通过代码里标识的元数据动态处理,例如动态生成代码。
  4. 运行时动态处理,运行时通过代码里标识的元数据动态处理,例如使用反射注入实例

试着实现一下4吧。写代码的时候想到了spring自动管理Bean,应该就是先扫描Field上的注解,然后创建的Bean,加入map中,当然理解可能很浅,这个以后还要探究一下。

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

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
@interface SemiAutowired{
    int value() default 8;
}

public class AnnotationAutowired {

    @SemiAutowired(100)
    int field;

    public static void main(String[] args) throws Exception {
        AnnotationAutowired autowired = new AnnotationAutowired();
        int value = autowired.getClass().getDeclaredField("field").getAnnotation(SemiAutowired.class).value();
        autowired.field = value;
        System.out.println(autowired.field);
    }
}

基本上完成,就先到这吧,有新知识再补充。
Created By lcy on 2029-09-22

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值