Java Annotation原理分析(四) - 实现原理分析

来源:互联网 发布:java的helloworld代码 编辑:程序博客网 时间:2024/06/12 02:45

转载:http://blog.csdn.net/blueheart20/article/details/18810693

7.1 实现综述

   在我们了解完了Annotation诸多的基础知识之后,不禁会想,Annotation是如何在代码和系统中被处理和执行的?

   首先,我们来看一下,Annotation的RetentionPolicy支持三种不同的类型:

  •    SourceCode

                写在源代码中,在编译之时,就被会抛弃掉。

  •    Class

               写在源代码中,在编译过程中,被保留到class文件中,具体按照什么样的格式被保存到class文件之内,这里就没有深究了。

               在代码运行过程中,VM(Java virtual machine)就不再使用Annotation信息。 这里仅在编译阶段中使用。

  •    Runtime

               信息被写入class文件中,在系统运行过程中,被VM提取。通过Reflective API来读取相应的信息。

      基于上面的三种策略,就会有相应的三种对应的解析方法:

  •    Source Code

                主要解析: 代码编译器,从代码中生成文档,读取java源码文件等等,特点是Annotation信息仅在源代码中存在。

  •    Class

                 基于java代码编译之后的字节码,直接读取Class中的信息。 常用的工具有:asm, javaasist,cglib等。

  •    Runtime

                 基于Java提供反射接口,读取允许在Runtime读取的Annotation。

    我们把研究的重点放在如何利用上述的这些特点去完成相应的动作,进而起到简化编程,提高开发效率的目的。

    7.2 Runtime的处理

           Runtime的处理主要依赖于反射的接口,在字节码中寻找Annotation的接口和输入参数,提取其内容和数值。大部分的情况下是基于代理模式,动态生成相应的代理类实例,然后通过代理类,调用相应的InvocationHandler,在InvocationHandler之内完成Annotation所要执行的动作;然后再继续调用原来的方法,继续执行。

         这里以AnnotationListenerFor这个Annotation的实现机理为例,来讲解如何完成自定义的Annotation解析过程。这里示例是基于@ActionListenerFor动态给标注的方法,注册到按钮的事件监听器中。

       1.  定义Annotation

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Target(ElementType.METHOD)  
  2. //标注运行阶段  
  3. @Retention(RetentionPolicy.RUNTIME)  
  4. public @interface ActionListenerFor {  
  5.      String source();  //变量  
  6. }  
   该Annotation的作用,就是给基于标注的方法,以ActionListener的接口方式,注册方法到相应的按钮事件中。就是将按钮的点击事件处理,由该方法来完成。

     2.  使用Annotation的ButtonFrame

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class ButtonFrame extends JFrame {  
  2.      public ButtonFrame()  {  
  3.        ///.....省略代码  
  4.        ///调用Annotation处理类,  
  5.        ActionListenerInstaller.processAnnotations(this);    
  6.      }  
  7.   
  8.    @ActionListenerFor(source = "yellowButton")  
  9.    public void yellowBackground()  {  
  10.       panel.setBackground(Color.YELLOW);  
  11.    }  
  12. }  

   这个类就是使用Annotation的示例类, @ActionListenerFor标注方法。 具体的处理类的调用放到了构造函数里,在实际的代码中,在何处调用Annotation的处理逻辑,按照实际情况而定,不一而足。

    3.  Annotation逻辑处理类ActionListenerInstaller

          首先,我们来看一下processAnnotation()这个方法,以下是这个方法的主要内容,入口参数是obj.

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static void processAnnotations(Object obj) {  
  2.                  Class<?> cl = obj.getClass();  
  3.              for (Method m : cl.getDeclaredMethods())  //遍历方法  
  4.              {    
  5.                 ActionListenerFor a = m.getAnnotation(ActionListenerFor.class);//获取Annotation  
  6.                 if (a != null)  //判断是否存在  
  7.                 {  
  8.                        //例如,这里会是yellowButton  
  9.                        Field f = cl.getDeclaredField(a.source());  
  10.                    f.setAccessible(true); //如果是私有,可以将私有变量,设置为可以访问.  
  11.                    addListener(f.get(obj), obj, m);  
  12.                 }  
  13.              }  
  14.   }  
    这里就是遍历方法,找到相应的Annotation,读取source值,并执行addListener的操作。

     下面我们来看一下addListener是如何实现动态的行为注册的。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static void addListener(Object source, final Object param, final Method m)  
  2.          throws ReflectiveOperationException  
  3.    {     
  4.       InvocationHandler handler = new InvocationHandler() {//Annotation的Handler,这里操作没有定义,作为示例  
  5.             public Object invoke(Object proxy, Method mm, Object[] args) throws Throwable {  
  6.                return m.invoke(param);  
  7.             }  
  8.          };  
  9.              //定义ActionListener的代理类,并将代理操作委托给handler  
  10.              Object listener = Proxy.newProxyInstance(null,new Class[] { java.awt.event.ActionListener.class }, handler);  
  11.       Method adder = source.getClass().getMethod("addActionListener", ActionListener.class);  
  12.       adder.invoke(source, listener); //将ActionListener的监听器绑定到yelloButton之类的对象里,调用addActionListener完成。  
  13.    }  
     这里首先定义了一个InvocationHandler,在handler里,可以定义在调用实际的方法之前,要做的处理。  接下基于Proxy创建代理的实例对象,然后将代理对象,绑定要最终的对象上去,调用addActionListener来完成。

    完成的示例,可以参考github上的项目: https://github.com/bladestone01/SampleCode.

   7.3 基于Processor对源代码中的Annotation进行处理

     在Java 6之中,集成了AbstractProcessor/Processor的接口来方便用户使用,以便对代码中的Annotation进行处理。

     首先,定义一个Annotation。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Documented  
  2. @Target(ElementType.METHOD)  
  3. @Retention(RetentionPolicy.SOURCE)  //仅使用于源代码.  
  4. public @interface Property {  
  5.    String editor() default "";   
  6. }  
    定义Processor,扩展点有两个,Processor或者AbstractProcessor,这里使用后者。
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv)  
  3.    {  
  4.      /// 代码处理。  
  5.    }  
     这里仅仅列出方法体,具体的代码处理过程,就不再赘述。在这个processor之内用户可以根据自己需要,比如,生成新的类,或者做检验等等

     在github的项目代码中,给出了创建BeanInfo的例子,比较简单,感兴趣的读者,可以自己读读看。

     在创建完成之后,如何来使用呢?这里给命令行的使用方法,至于代码级别的自动化执行,读者可自行完成。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. javac BeanInfoAnnotationProcessor.java  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. javac -processor BeanInfoAnnotationProcessor ChartBean.java  

    7.4  基于字节码动态修改class

        可以借助第三方的类库来进行动态修改class文件的动作,这里不再详细讲述。

         可用的第三方类库: asm, cglib, javaassit.

   7,5  总结

      虽然Java语言层面上,支持Annotation,但是用户在定义Annotation,并实现相应的处理Handler还是需要一定工作量,总体而言,还不是非常的简便。

     这里将核心步骤总结一下,主要针对Runtime之时的Annotation解析器。

     1.  定义Annotation

     2.  定义Annotation的处理器类

     3.  创建InvocationHandler予以处理具体的逻辑

     4.  基于Proxy.newProxyInstance()创建代理,并将代理实例绑定到Invocationhandler的实例上

     5.  选择合适的调用Annotation的时机和切入点。

说明: 上述的代码示例是参照CoreJava Volume II(9th)之第10章的内容。 感兴趣的读者,可以自行阅读书中相关内容。

参考文档:

0.  Core Java Volume II, 9th,  10 chapter. 

1.  http://www.zdnet.com/writing-and-processing-custom-annotations-part-3-2039362483/

2.  http://yourmitra.wordpress.com/2008/02/15/java-custom-annotations-runtime-and-compile-build-time-processing/

3.  http://dyutiman.wordpress.com/2012/08/24/custom-annotation-processing-for-web-application/

4.  http://www.angelikalanger.com/Conferences/Slides/JavaAnnotationProcessing-JSpring-2008.pdf

5.  http://deors.wordpress.com/2011/10/08/annotation-processors/

0 0
原创粉丝点击