java注解的实现机制_注解的实现原理

一、注解的底层实现原理

注解的底层也是使用反射实现的,我们可以自定义一个注解来体会下。注解和接口有点类似,不过申明注解类需要加上@interface,注解类里面,只支持基本类型、String及枚举类型,里面所有属性被定义成方法,并允许提供默认值。

@Target — —注解用于什么地方

ANNOTATION_TYPE,//给注解注解(这貌似把自己不当类来看)

ElementType.FIELD //注解作用于变量

ElementType.METHOD //注解作用于方法

ElementType.PARAMETER //注解作用于参数

ElementType.CONSTRUCTOR //注解作用于构造方法

ElementType.LOCAL_VARIABLE //注解作用于局部变量

ElementType.PACKAGE //注解作用于包

@Retention — —注解运行状态

SOURCE, //源码状态运行,

CLASS, //编译类文件时运行

RUNTIME //运行时运行

SOURCE:注解将编译器丢弃(该类型的注解信息只会保留在源码里,源码经过编译后,注解信息会被丢弃,不会保留在编译好的class文件里)

CLASS:注解在class中可用,但会被VM丢弃(该类型的注解信息会保留在源码里和class文件里,在执行的时候,不会加载到虚拟机中(JVM)中)

RUNTIME:VM将在运行期也保留注解信息,因此可以通过反射机制读取注解信息(源码、class文件和执行的时候都有注解的信息)

具体的区别如下:

运行时注解就是就是运行时运用反射,动态获取对象、属性、方法等,一般的IOC框架就是这样,可能会牺牲一点效率。

编译时注解就是在程序编译时根据注解进行一些额外的操作,大名鼎鼎的ButterKnife运用的就是编译时注解,ButterKnife在我们编译时,就根据注解,自动生成了一些辅助类。要玩编译时注解呢,你得先依赖apt,然后自己写一个类继承AbstractProcessor,重写process方法,在里面实现如何把配置或注解的信息变成所需要的类。

@Documented — — 生成说明文档,添加类的解释

表示注解会被包含在javaapi文档中

当一个注解被@Documented元注解所修饰时,那么无论在哪里使用这个注解,都会被Javadoc工具文档化。

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)public @interface Documented {

}

这个元注解呗@Documented修饰,表示它本身会被文档化。@Retention注解的值RetentionPolicy.RUNTIME表示@Documented这个注解能保留在运行时;@Target元注解的值ElementType.ANNOTATION_TYPE表示@Documented这个注解只能够来修饰注解类型

@Inherited — —允许子类继承父类中的注解

允许子类继承父类的注解。

用于描述某个被标注的类型可被继承的,如果一个使用了@Inherited修饰的annotation类型类型被用于一个class,则这个annotation将被用于该class类的子类。

表明被修饰的注解类型是自动继承的。如果你想让一个类和它的子类都包含某个注解,就可以使用@Inherited来修饰这个注解。也就是说,假设@Parent类是Child类的父类,那么我们若用被@Inherited元注解所修饰的某个注解对Parent类进行了修饰,则相当于Child类也被该注解所修饰了。这个元注解的定义如下:

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.ANNOTATION_TYPE)public @interface Inherited {

}

@Inherited

public @interface MyAnnotation {

}

@MyAnnotation

public class MySuperClass {

}

public class MySubClass extends MySuperClass {

}

上述代码的大致意思是使用@Inherited修饰注解MyAnnotation使用MyAnnotation注解MySuperClass实现类MySubclass继承自MySuperClass

当@Inherited annotation类型标注的annotation的Retention是RetentionPolicy.RUNTIME,则反射API增强了这种继承性。如果我们使用java.lang.reflect去查询一个@Inherited annotation类型的annotation时,反射代码检查将展开工作:检查class和其父类,直到发现指定的annotation类型被发现,或者到达类继承结构的顶层。

二、自定义实现运行时注解

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

public @interface TestAnnonation {

String name() default "";

int Id() default 0;

}

注解关键字时@interface,然后上面标注为元注解,表示只能修饰方法并且加载到虚拟机中,里面时这个注解所具有的属性,name, id,我们在给方法加注解的时候设置相应的值。

@TestAnnonation(name = "android" , Id = 1)

private void testAnno(){

}

上面我们在一个方法上面添加注解,然后我们通过下面的方法将这个注解打印出来

private void outputAnnoDetail(Class clazz){

Method [] methods = clazz.getDeclaredMethods();

for(Method method : methods) {

TestAnnonation testAnnonation = method.getAnnotation(TestAnnonation.class);

if (testAnnonation != null) {

Log.d("anonation", "name------>" + testAnnonation.name() + "------>Id------>" + testAnnonation.Id());

}

}

}

三、标准注解/内建注解

1、@Override注解

@Override注解用来修饰对父类进行重写的方法。如果一个并非重写父类的方法使用这个注解,编译器将提示错误。

public class MySuperClass {

public void doTheThing() {

System.out.println("Do the thing");

}

}

public class MySubClass extends MySuperClass{

@Override

public void doTheThing() {

System.out.println("Do it differently");

}

}

2、@ Deprecated

@Deprecate 标记类、方法、属性,如果上述三种元素不再使用,使用@Deprecated注解,建议用户不再使用。

@Deprecated

public class MyComponent {

}

3、@SuppressWarnings

@SuppressWarnings 用来抑制编译器生成警告信息。可以修饰的元素为类,方法,方法参数,属性,局部变量。

@SuppressWarnings

public void methodWithWarning() {

}

四、Android注解框架 ButterKnife

ButterKnife注解框架是大家常用的注解框架,它主要作用是绑定View并且绑定View常用的监听事件,下面是其中一个注解

@Retention(CLASS)

@Target(FIELD)

public @interface BindView {

/** View ID to which the field will be bound. */

@IdRes int value();

}

通过上面的代码可以看出,ButterKnife的注解保留方式为CLASS模式,也就是会保留到class中但是不会背加载到虚拟机中,这个时候我们就要看下它的AbstractProcessor,一般标注为Class的都会重写AbstractProcessor类,这样在虚拟机进行编译的时候就会做相应的处理。

主要看一下几个方法:

@Override

public Set getSupportedAnnotationTypes() {

Set types = new LinkedHashSet<>();

for (Class extends Annotation> annotation : getSupportedAnnotations()) {

types.add(annotation.getCanonicalName());

}

return types;

}

private Set> getSupportedAnnotations() {

Set> annotations = new LinkedHashSet<>();

annotations.add(BindArray.class);

annotations.add(BindBitmap.class);

annotations.add(BindBool.class);

annotations.add(BindColor.class);

annotations.add(BindDimen.class);

annotations.add(BindDrawable.class);

annotations.add(BindFloat.class);

annotations.add(BindInt.class);

annotations.add(BindString.class);

annotations.add(BindView.class);

annotations.add(BindViews.class);

annotations.addAll(LISTENERS);

return annotations;

}

上面的方法主要表明会处理哪些注解

@Override

public boolean process(Set extends TypeElement> elements, RoundEnvironment env) {

Map bindingMap = findAndParseTargets(env);

for (Map.Entry entry : bindingMap.entrySet()) {

TypeElement typeElement = entry.getKey();

BindingSet binding = entry.getValue();

JavaFile javaFile = binding.brewJava(sdk);

try {

javaFile.writeTo(filer);

} catch (IOException e) {

error(typeElement, "Unable to write binding for type %s: %s", typeElement, e.getMessage());

}

}

return true;

}

然后就是在编译的时候的具体处理过程,这个过程主要时先找到并解析注解,然后生成java文件,这样在虚拟机真正执行的时候就不用去查找和解析,也就不会耗时了。

EventBus

EventBus是使用运行时注解,主要的作用是在运行的时候会去查找所有被注解的方法,然后再去解析注解。运行时注解会影响程序的性能,毕竟在运行的时候有一个查找的过程,所以运行时注解的作用一般是标记一个作用区。

五、注解基础知识思维导图

a692150d625c

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值