再说注解

来源:互联网 发布:司马懿 新旧知乎 编辑:程序博客网 时间:2024/05/21 06:34
    当今的各种框架:Hibernate、Struts、Spring等,包括EJB,都支持注解形式,注解相比XML配置而言确实更为简洁,先来说说关于注解"what is"的问题。

注解

    从形式上看,注解是类似注释,它为代码提供了一种新的标识方式,可以在编译器先定义、使用,等到运行时再去解析这些注解对应的含义。在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之间的关系。


4 0
原创粉丝点击