注解
从形式上看,注解是类似凝视,它为代码提供了一种新的标识方式,能够在编译器先定义、使用,等到执行时再去解析这些注解相应的含义。在JDK1.5中引入,如今的JDK已经把注解的地位提升到和二进制码同样,当然假设你略微关注过注解,你应该知道注解解析过程的基础是反射原理。
它为什么能够使用反射原理?对照类载入使用反射可知,虚拟机在载入class文件时,也会为注解分配空间并解析,终于还会为注解和相应的二进制码建立关联,这就为使用反射提供了基础。
注解不过用于标注,并不会主动执行,也不会影响主代码的逻辑,只起到辅助性的作用,但其对代码的说明能力,结合反射技术已经给了我们非常大的想象空间。
从宏观上看,注解的运行共分为3部分:
- 定义注解
- 使用注解
- 解析注解
定义注解
说到定义注解须要先说一下元注解,即定义注解的注解,共同拥有四种:@Retention @Target @Document @Inherited:
- @Retention:注解的生命时长:编译期、执行期……
- @Target:应用位置:字段、方法、类……
- @Documented:是否被包括在javadoc中
- @Inherited:子类能否够继承父类该注解
有关这几个元注解的说明不再细说,有兴趣能够查看元注解的源代码,位于java.lang.annotation下,另有部分注解位于javax.annotation下。
使用注解
使用注解的方式非经常见也非常简答,如@MyAnnotation(ElementType.RealNew),将此注解加到类、字段、方法等上即表明此注解关联到该类的指定项上。
解析注解
这三步中,事实上这是最后也是最关键的一步,上面定义、使用再好,没有一个专门解析注解的类,前面都是白写,解析注解实例会在以下说明,解析的核心步骤是:
- 得到使用注解的类
- 使用反射得到类中的字段、方法等
- 得到使用了指定注解的方法、字段等,及其注解的值
- 编写一个函数,依据注解类型及注解值进行指定操作
实例
依据以上所说,编写了一个注解实例,实例内容为:当检測到House属性中有我们自己定义的注解时,向House注入一个Dog。
文件说明
- NewMePolicy:定义一个枚举,指定注解能够使用的參数
- NewMeAnnotation:定义一个注解
- Dog:辅助使用注解的类
- House:使用我们自己定义注解的类
- TestAnnotation:解析注解的类
NewMePolicy
package annotation; public enum NewMePolicy { //使用单例模式获取对象 Singleton, //创建新对象 RealNew, //忽视此注解 Ignore }
NewMeAnnotation
package annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Documented @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface NewMeAnnotation { NewMePolicy category(); public String className(); }
House
package annotation; public class House { @NewMeAnnotation(category=NewMePolicy.RealNew,className="annotation.Dog") private Class<?> cat; private String other; // 省略getter和setter方法 }
TestAnnotation
package annotation; import java.lang.annotation.Annotation; import java.lang.reflect.Field; import java.util.HashMap; public class TestAnnotation { public static void main(String [] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException { //定义一个暂时的HashMap,用于保存全部涉及到的用户自己定义类实例 HashMap<String, Object> temp=new HashMap<String, Object>(); //定义另外一个HashMap,用于保存处理过的用户自己定义类实例 HashMap<String, Object> objMap=new HashMap<String, Object>(); //载入House类 Class<?> uaClass=Class.forName("annotation.House"); //利用反射得到其全部的属性 Field[] fields=uaClass.getDeclaredFields(); //遍历得到的属性 for (Field field:fields) { //该字段是否使用了我们自己定义的注解 boolean hasAnnotation=field.isAnnotationPresent((Class<? extends Annotation>) NewMeAnnotation.class); if (hasAnnotation) { //得到有NewMeAnnotation注解的字段 NewMeAnnotation annotations=field.getAnnotation(NewMeAnnotation.class); //打印查看注解实例化的策略 System.out.println("注入的策略为: "+annotations.category()); //打印要注入的内容 System.out.println("注入的类为: "+annotations.className()); if (NewMePolicy.RealNew.equals(annotations.category())) { //找到相应的类,实例化 Class<?> cat=Class.forName(annotations.className().toString()); House house= (House) uaClass.newInstance(); //将两个类的实例保存到temp中 temp.put(uaClass.toString(), house); temp.put(Dog.class.toString(), cat); //注入实例 house.setCat(cat); //保存定义好的UseAnnotation实例 objMap.put(uaClass.toString(), house); } System.out.println("类: "+uaClass+"已经完毕初始化"); } else { System.out.println("字段:"+field+" 没有NewMeAnnotation注解!"); } } } }
控制台输出
注入的策略为: RealNew 注入的类为: annotation.Dog 类: class annotation.House已经完毕初始化 字段:private java.lang.String annotation.House.other 没有NewMeAnnotation注解!
能够看到,我们能够使用反射获取到字段,及字段的注解,依据注解内容,我们能够动态的将注解规定的类Dog注入到House中,当然这个样例在解析的时候还不是非常全,比方没有解析假设注解的category为singleton、Ignore时怎样处理,可是作为一个解释注解的样例,我觉得足够了。
总结
这个注解的样例是使用setter将一个bean注入到另外一个bean中,有没有认为有些眼熟,对Spring,稍后的文章会解释Spring是怎样依据注解管理bean之间的关系。