- 为什么要引入注解?
在我们访问网址时通常都会带有参数,例如:http://www.haoduosc.com/home/company?bpid=102&rn=%E7%BE%8E%E7%9A%84,app在访问网络的时候,需要根据输入的参数bpid和rn拼接完成上叙网址。对于通用的网络访问框架,在输入参数“102”的时候必须指定参数名“bpid”,否则网络访问框架是无法完成网址拼接的,此时注解就被发明出来。注解是一种标准的描述元数据的方式,通过反射读取注解中的数据,我们可以根据业务需求,把它设置成键值名或者是初始值等。
- 2、注解的定义
注解只支持基本类型、String及枚举类型。注释中所有的属性被定义成方法,并允许提供默认值。
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@interface Todo {
public enum Priority {LOW, MEDIUM, HIGH}
public enum Status {STARTED, NOT_STARTED}
String author() default “Yash”;
Priority priority() default Priority.LOW;
Status status() default Status.NOT_STARTED;
}
下面的例子演示了如何使用上面的注解。
@Todo(priority = Todo.Priority.MEDIUM, author = “Yashwant”, status = Todo.Status.STARTED)
public void incompleteMethod1() {
//Some business logic is written
//But it’s not complete yet
}
如果注解中只有一个属性,可以直接命名为“value”,使用时无需再标明属性名。
@interface Author{
String value();
}
@Author(“Yashwant”)
public void someMethod() {
}
但目前为止一切看起来都还不错。我们定义了自己的注解并将其应用在业务逻辑的方法上。现在我们需要写一个用户程序调用我们的注解。这里我们需要使用反射机制。如果你熟悉反射代码,就会知道反射可以提供类名、方法和实例变量对象。所有这些对象都有getAnnotation()这个方法用来返回注解信息。我们需要把这个对象转换为我们自定义的注释(使用 instanceOf()检查之后),同时也可以调用自定义注释里面的方法。看看以下的实例代码,使用了上面的注解:
Class businessLogicClass = BusinessLogic.class;
for(Method method : businessLogicClass.getMethods()) {
Todo todoAnnotation = (Todo)method.getAnnotation(Todo.class);
if(todoAnnotation != null) {
System.out.println(" Method Name : " + method.getName());
System.out.println(" Author : " + todoAnnotation.author());
System.out.println(" Priority : " + todoAnnotation.priority());
System.out.println(" Status : " + todoAnnotation.status());
}
}
-
3、注解的元数据
用来描述数据都数据通常称为“元数据”,注解就是一种典型的元数据。有的注解数据是编译阶段需要,例如@Override告诉编译器这个方法是一个重写方法(描述方法的元数据),如果父类中不存在该方法,编译器便会报错,提示该方法没有重写父类中的方法。有的注解是运行的时候才需要,例如方法: Product(@Query(“url”) String url, @Query(“bpid”) String bpid, @Query(“rn”) String rn),在执行Product方法都时候会读取注解@Query(“bpid”)中的元数据"bpid"做为键值名,然后把后面的 String bpid做为值进行网址拼接,最后形成[http://www.haoduosc.com/home/company?bpid=102&rn=%E7%BE%8E%E7%9A%84。编译器需要一个参数来说明注解的作用周期,这个参数也被设计成注解,由于是说明注解的注解又被称为元注解,通常包括下面几类:
@Documented–一个简单的Annotations标记注解,表示是否将注解信息添加在java文档中。
@Retention– 定义该注解的生命周期。
RetentionPolicy.SOURCE – 在编译阶段丢弃。这些注解在编译结束之后就不再有任何意义,所以它们不会写入字节码。@Override, @SuppressWarnings都属于这类注解。
RetentionPolicy.CLASS – 在类加载的时候丢弃。在字节码文件的处理中有用。注解默认使用这种方式。
RetentionPolicy.RUNTIME– 始终不会丢弃,运行期也保留该注解,因此可以使用反射机制读取该注解的信息。我们自定义的注解通常使用这种方式。
@Target – 表示该注解用于什么地方。如果不明确指出,该注解可以放在任何地方。以下是一些可用的参数。需要说明的是:属性的注解是兼容的,如果你想给7个属性都添加注解,仅仅排除一个属性,那么你需要在定义target包含所有的属性。
ElementType.TYPE:用于描述类、接口或enum声明
ElementType.FIELD:用于描述实例变量
ElementType.METHOD
ElementType.PARAMETER
ElementType.CONSTRUCTOR
ElementType.LOCAL_VARIABLE
ElementType.ANNOTATION_TYPE 另一个注释
ElementType.PACKAGE 用于记录java文件的package信息
@Inherited – 定义该注释和子类的关系
- 4、注解和特性的区别
注解和特性就如同一篇文章被翻译为不同的语言,内容和理念完全一样。由于android推荐使用网络解析json访问数据,而解析json用注解进行数据说明是常用方法,所以学C#可以不懂特性,而学android则必须要懂注解。
公共语言运行时使你能够添加类似于关键字的描述性声明(称为特性),以便批注编程元素(如类型、字段、方法和属性)。
编译运行时的代码时,它将被转换为 Microsoft 中间语言 (MSIL),并和编译器生成的元数据一起放置在可移植可执行 (PE)
文件内。 特性使你能够将额外的描述性信息放到可使用运行时反射服务提取的元数据中。
以上的这段c#特性解释同样适用于android的注解。
反射提供描述程序集、模块和类型的对象(Type类型)。
可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型,然后调用其方法或访问器字段和属性。
如果代码中使用了特性,可以利用反射来访问它们。
以上的这段c#反射解释同样适用于android的反射,都是通过反射读取程序集里面的元数据