`
wenzongliang
  • 浏览: 466516 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Annotation 注解

 
阅读更多

http://www.trinea.cn/android/java-annotation-android-open-source-analysis/

不少开源库都用到了注解的方式来简化代码提高开发效率

本文简单介绍下 Annotation 示例、概念及作用、分类、自定义、解析,并对几个 Android 开源库 Annotation 原理进行简析。

PDF 版: Java Annotation.pdf, PPT 版:Java Annotation.pptx, Keynote 版:Java Annotation.key

 

一、Annotation 示例

Override Annotation

 

@Override

public void onCreate(Bundle savedInstanceState);

 

Retrofit Annotation

 

@GET("/users/{username}")

User getUser(@Path("username") String username);

 

Butter Knife Annotation

 

@InjectView(R.id.user) EditText username;

 

ActiveAndroid Annotation

 

@Column(name = “Name") public String name;

 

Retrofit 为符合 RESTful 规范的网络请求框架

Butter Knife 为 View 及事件等依赖注入框架

Active Android 为 ORM 框架

更多见:Android 开源项目汇总

 

二、Annotation 概念及作用

1 概念

An annotation is a form of metadata, that can be added to Java source code. Classes, methods, variables, parameters and packages may be annotated. Annotations have no direct effect on the operation of the code they annotate.

能够添加到 Java 源代码的语法元数据。类、方法、变量、参数、包都可以被注解,可用来将信息元数据与程序元素进行关联。Annotation 中文常译为“注解”

 

2 作用

a. 标记,用于告诉编译器一些信息

b. 编译时动态处理,如动态生成代码

c. 运行时动态处理,如得到注解信息

这里的三个作用实际对应着后面自定义 Annotation 时说的 @Retention 三种值分别表示的 Annotation

看看下面一段代码的运行结果是多少?

 

public class Person {

 

    private int    id;

    private String name;

 

    public Person(int id, String name) {

        this.id = id;

        this.name = name;

    }

 

    public boolean equals(Person person) {

        return person.id == id;

    }

 

    public int hashCode() {

        return id;

    }

 

    public static void main(String[] args) {

 

        Set<Person> set = new HashSet<Person>();

        for (int i = 0; i < 10; i++) {

            set.add(new Person(1, "Jim"));

        }

        System.out.println(set.size());

    }

}

 

答案见本文最后

 

三、Annotation 分类

1 标准 Annotation

包括Override, Deprecated, SuppressWarnings,标准 Annotation 是指 Java 自带的几个 Annotation,上面三个分别表示重写函数,函数已经被禁止使用,忽略某项 Warning

2 元 Annotation

@Retention, @Target, @Inherited, @Documented,元 Annotation 是指用来定义 Annotation 的 Annotation,在后面 Annotation 自定义部分会详细介绍含义

3 自定义 Annotation

自定义 Annotation 表示自己根据需要定义的 Annotation,定义时需要用到上面的元 Annotation

这里只是一种分类而已,也可以根据作用域分为源码时、编译时、运行时 Annotation,后面在自定义 Annotation 时会具体介绍

 

四、Annotation 自定义

1 调用

 

public class App {

 

    @MethodInfo(

        author = “trinea.cn+android@gmail.com”,

        date = "2014/02/14",

        version = 2)

    public String getAppName() {

        return "trinea";

    }

}

 

这里是调用自定义 Annotation——MethodInfo 的示例,MethodInfo Annotation 作用为给方法添加相关信息,包括 author、date、version

 

2 定义

 

@Documented

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

@Inherited

public @interface MethodInfo {

 

    String author() default "trinea@gmail.com";

 

    String date();

 

    int version() default 1;

}

 

这里是 MethodInfo 的实现部分

(1). 通过 @interface 定义,注解名即为自定义注解名

(2). 注解配置参数名为注解类的方法名,且:

a. 所有方法没有方法体,没有参数没有修饰符,实际只允许 public & abstract 修饰符,默认为 public ,不允许抛异常

b. 方法返回值只能是基本类型,String, Class, annotation, enumeration 或者是他们的一维数组

c. 若只有一个默认属性,可直接用 value() 函数。一个属性都没有表示该 Annotation 为 Mark Annotation

(3). 可以加 default 表示默认值

 

3 元 Annotation

@Documented 是否会保存到 Javadoc 文档中

@Retention 保留时间,可选值 SOURCE(源码时),CLASS(编译时),RUNTIME(运行时),默认为 CLASS,值为 SOURCE 大都为 Mark Annotation,这类 Annotation 大都用来校验,比如 Override, Deprecated, SuppressWarnings

@Target 可以用来修饰哪些程序元素,如 TYPE, METHOD, CONSTRUCTOR, FIELD, PARAMETER 等,未标注则表示可修饰所有

@Inherited 是否可以被继承,默认为 false

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

  注意:@Inherited annotation类型是被标注过的class的子类所继承。类并不从它所实现的接口继承annotation,方法并不从它所重载的方法继承annotation。

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

 

五、Annotation 解析

1 运行时 Annotation 解析

(1) 运行时 Annotation 指 @Retention 为 RUNTIME 的 Annotation,可手动调用下面常用 API 解析

 

method.getAnnotation(AnnotationName.class);

method.getAnnotations();

method.isAnnotationPresent(AnnotationName.class);

 

其他 @Target 如 Field,Class 方法类似

getAnnotation(AnnotationName.class) 表示得到该 Target 某个 Annotation 的信息,因为一个 Target 可以被多个 Annotation 修饰

getAnnotations() 则表示得到该 Target 所有 Annotation

isAnnotationPresent(AnnotationName.class) 表示该 Target 是否被某个 Annotation 修饰

(2) 解析示例如下:

 

public static void main(String[] args) {

    try {

        Class cls = Class.forName("cn.trinea.java.test.annotation.App");

        for (Method method : cls.getMethods()) {

            MethodInfo methodInfo = method.getAnnotation(

MethodInfo.class);

            if (methodInfo != null) {

                System.out.println("method name:" + method.getName());

                System.out.println("method author:" + methodInfo.author());

                System.out.println("method version:" + methodInfo.version());

                System.out.println("method date:" + methodInfo.date());

            }

        }

    } catch (ClassNotFoundException e) {

        e.printStackTrace();

    }

}

 

以之前自定义的 MethodInfo 为例,利用 Target(这里是 Method)getAnnotation 函数得到 Annotation 信息,然后就可以调用 Annotation 的方法得到响应属性值

 

2 编译时 Annotation 解析

(1) 编译时 Annotation 指 @Retention 为 CLASS 的 Annotation,甴 apt(Annotation Processing Tool) 解析自动解析。需要做的

a. 自定义类集成自 AbstractProcessor

b. 重写其中的 process 函数

这块很多同学不理解,实际是 apt(Annotation Processing Tool) 在编译时自动查找所有继承自 AbstractProcessor 的类,然后调用他们的 process 方法去处理

(2) 假设之前自定义的 MethodInfo 的 @Retention 为 CLASS,解析示例如下:

 

@SupportedAnnotationTypes({ "cn.trinea.java.test.annotation.MethodInfo" })

public class MethodInfoProcessor extends AbstractProcessor {

 

    @Override

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment env) {

        HashMap<String, String> map = new HashMap<String, String>();

        for (TypeElement te : annotations) {

            for (Element element : env.getElementsAnnotatedWith(te)) {

                MethodInfo methodInfo = element.getAnnotation(MethodInfo.class);

                map.put(element.getEnclosingElement().toString(), methodInfo.author());

            }

        }

        return false;

    }

}

 

SupportedAnnotationTypes 表示这个 Processor 要处理的 Annotation 名字。

process 函数中参数 annotations 表示待处理的 Annotations,参数 env 表示当前或是之前的运行环境

process 函数返回值表示这组 annotations 是否被这个 Processor 接受,如果接受后续子的 rocessor 不会再对这个 Annotations 进行处理

 

六、几个 Android 开源库 Annotation 原理简析

1 Annotation — Retrofit

(1) 调用

 

@GET("/users/{username}")

User getUser(@Path("username") String username);

 

(2) 定义

 

@Documented

@Target(METHOD)

@Retention(RUNTIME)

@RestMethod("GET")

public @interface GET {

  String value();

}

 

从定义可看出 Retrofit 的 Get Annotation 是运行时 Annotation,并且只能用于修饰 Method

(3) 原理

 

private void parseMethodAnnotations() {

    for (Annotation methodAnnotation : method.getAnnotations()) {

    Class<? extends Annotation> annotationType = methodAnnotation.annotationType();

    RestMethod methodInfo = null;

 

    for (Annotation innerAnnotation : annotationType.getAnnotations()) {

        if (RestMethod.class == innerAnnotation.annotationType()) {

            methodInfo = (RestMethod) innerAnnotation;

            break;

        }

    }

    ……

    }

}   

 

RestMethodInfo.java 的 parseMethodAnnotations 方法如上,会检查每个方法的每个 Annotation, 看是否被 RestMethod 这个 Annotation 修饰的 Annotation 修饰,这个有点绕,就是是否被 GET、DELETE、POST、PUT、HEAD、PATCH 这些 Annotation 修饰,然后得到 Annotation 信息,在对接口进行动态代理时会掉用到这些 Annotation 信息从而完成调用。 因为 Retrofit 原理设计到动态代理,这里只介绍 Annotation,具体原理分析请等待 Android 优秀开源项目实现原理解析 项目的完成

 

2 Annotation — Butter Knife

(1) 调用

 

@InjectView(R.id.user) 

EditText username;

 

(2) 定义

 

@Retention(CLASS) 

@Target(FIELD)

public @interface InjectView {

  int value();

}

 

可看出 Butter Knife 的 InjectView Annotation 是编译时 Annotation,并且只能用于修饰属性

(3) 原理

 

 

@Override 

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

    Map<TypeElement, ViewInjector> targetClassMap = findAndParseTargets(env);

 

    for (Map.Entry<TypeElement, ViewInjector> entry : targetClassMap.entrySet()) {

        TypeElement typeElement = entry.getKey();

        ViewInjector viewInjector = entry.getValue();

 

        try {

            JavaFileObject jfo = filer.createSourceFile(viewInjector.getFqcn(), typeElement);

            Writer writer = jfo.openWriter();

            writer.write(viewInjector.brewJava());

            writer.flush();

            writer.close();

        } catch (IOException e) {

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

        }

    }

 

    return true;

}

 

ButterKnifeProcessor.java 的 process 方法如上,编译时,在此方法中过滤 InjectView 这个 Annotation 到 targetClassMap 后,会根据 targetClassMap 中元素生成不同的 class 文件到最终的 APK 中,然后在运行时调用 ButterKnife.inject(x) 函数时会到之前编译时生成的类中去找。 这里只介绍 Annotation,具体原理分析请等待 Android 优秀开源项目实现原理解析 项目的完成

 

3 Annotation — ActiveAndroid

(1) 调用

 

@Column(name = “Name") 

public String name;

 

(2) 定义

 

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.RUNTIME)

public @interface Column {

  ……

}

 

可看出 ActiveAndroid 的 Column Annotation 是运行时 Annotation,并且只能用于修饰属性

(3) 原理

 

Field idField = getIdField(type);

mColumnNames.put(idField, mIdName);

 

List<Field> fields = new LinkedList<Field>(ReflectionUtils.getDeclaredColumnFields(type));

Collections.reverse(fields);

 

for (Field field : fields) {

    if (field.isAnnotationPresent(Column.class)) {

        final Column columnAnnotation = field.getAnnotation(Column.class);

        String columnName = columnAnnotation.name();

        if (TextUtils.isEmpty(columnName)) {

            columnName = field.getName();

        }

 

        mColumnNames.put(field, columnName);

    }

}

 

TableInfo.java 的构造函数如上,运行时,得到所有行信息并存储起来用来构件表信息。

这里原理都只介绍 Annotation,具体原理分析请等待 Android 优秀开源项目实现原理解析 项目的完成

 

最后留下一个小问题:如何判断一个 Annotation 是运行时还是编译时生效以及如何快速找到它的解析代码

 

前面的示例代码运行结果应该是 10 而不是 1,这个示例代码程序实际想说明的是标记型注解 Override 的作用,为 equals 方法加上 Override 注解就知道 equals 方法的重载是错误的,参数不对

分享到:
评论

相关推荐

    spring的Annotation注解.

    ### Spring框架中的Annotation注解详解 #### 一、Spring与Annotation的基本概念 Spring框架通过引入Annotation,极大地简化了Java开发中的依赖注入(Dependency Injection, DI)和面向切面编程(AOP)的过程。...

    Annotation注解的介绍和使用

    ### Annotation注解的深入解析与应用 #### 一、Annotation概览 ##### 1.1 何为元数据 元数据,在计算机科学中,是指用于描述数据的数据,它提供了关于数据本身的附加信息,有助于理解、解释和管理数据。在编程...

    Java Annotation注解技术

    Java Annotation注解技术是自Java SE 5.0版本引入的一种元编程机制,它允许程序员在源代码的各个层面(如类、方法、变量等)添加元数据,以供编译器、JVM或第三方工具在编译时或运行时进行处理。Annotation简化了...

    Struts2之Annotation注解配置使用案例struts013

    在Struts2中,Annotation注解的引入为开发者提供了更加灵活和便捷的配置方式,使得无需在XML配置文件中进行繁琐的设置,可以直接在类或方法上通过注解来进行配置。本文将深入探讨Struts2中的Annotation配置,以及...

    ssh2 Annotation注解 框架+oracle

    在"ssh2 Annotation注解 框架+oracle"的场景下,我们将深入探讨SSH2框架如何结合注解和Oracle数据库进行应用开发。 **Spring Annotation注解** Spring框架允许开发者使用注解来声明Bean、依赖注入、事务管理等。...

    hibernate _annotation 注解编程

    ### Hibernate Annotation注解编程知识点详解 #### 一、概述与设置环境 - **概述**:Hibernate 是一个流行的 Java 持久层框架,它提供了一种面向对象的方式来处理数据库操作。Hibernate 支持多种元数据定义方式,...

    Java.Annotation注解.part4

    Java.Annotation注解.part4

    spring annotation注解

    Spring Annotation 注解 Spring 框架中的注解是用于在 Java 类中添加元数据的,通过这些元数据,Spring 框架可以在运行时提供更多的功能。 Spring 框架提供了多种类型的注解,例如 @Autowired、@Resource、@...

    Java Annotation注解.doc

    Java Annotation,也称为注解,是Java编程语言中的一种元数据机制,用于向编译器、JVM或工具提供有关代码的附加信息。这些信息不直接影响代码的执行,但可以被编译器或运行时环境用来执行特定的操作,如代码分析、...

    HibernateAnnotation注解api

    HibernateAnnotation注解 api手册

    Java.Annotation注解.part3

    Java.Annotation注解.part3

    Java.Annotation注解.part2

    Java.Annotation注解.part2

    Java.Annotation注解.part1

    Java.Annotation注解.part1

    基于Struts2.18+Spring2.5+Hibernater3.3+Annotation注解开发的电子商务网站demo

    在本项目中,这些框架都结合了最新的版本,即Struts2.18、Spring2.5和Hibernate3.3,并且引入了Annotation注解来简化配置,提升开发效率。 Struts2是MVC(Model-View-Controller)设计模式的实现,主要负责处理用户...

    使用Spring的声明式事务----Annotation注解方式

    本篇文章将深入探讨如何使用Spring的声明式事务,特别是通过Annotation注解方式进行设置。 首先,我们需要理解什么是声明式事务。与编程式事务(即手动编写事务管理代码)不同,声明式事务是通过AOP(面向切面编程...

    Spring_Hibernate_JAVAX_Annotation注解

    ### Spring_Hibernate_JAVAX_Annotation 注解详解 #### 一、概述 本文将详细介绍与SSH(Spring+Struts+Hibernate)开发相关的注解。这些注解覆盖了多个领域,如AspectJ、Batch处理、Spring框架本身的功能(包括...

    基于Java的MVC架构Annotation注解实现源码分析

    该项目的核心是基于Java语言实现的MVC架构,采用Annotation注解技术进行实现。项目源码由69个文件组成,涵盖24个类文件、23个Java源文件、7个JSP页面文件、3个XML配置文件以及少量其他类型文件。该项目是jeecg第16期...

    自定义Annotation注解

    本教程将通过一个简单的自定义Annotation注解示例——`CustomAnnotationDemo`,帮助新手了解如何创建和使用自定义注解。 首先,让我们看看如何定义一个自定义注解。在Java中,注解的定义使用`@interface`关键字。...

Global site tag (gtag.js) - Google Analytics