分享

超级迷你的javabean容器,实现annotation DI

 will_lau 2014-01-06

这是一个解释javabean的容器原理的例子,有像spring容器的意思, 实现了annotation在setter方法上注入的效果,我没有看过spring或其他ejb容器的代码,自己猜测javabean容器应该就是这样实现的,所谓的DI或者叫ioc的就是这样做到的,利用xml配置文件和用annotation方法没有太大区别,两者都是为了描述注入点和注入对象。

 

先写一个被注入的类:

  1. package com.red.beans;  
  2.   
  3. public class SourceBean {  
  4.   
  5.     public void hello(){  
  6.         System.out.println("I am injected");  
  7.     }  
  8. }  

没什么好说的

 

再写一个接受注入的类

  1. package com.red.beans;  
  2.   
  3. import com.red.annotations.MyDI;  
  4.   
  5. public class DestinationBean {  
  6.   
  7.       
  8.     private SourceBean  sb;  
  9.       
  10.     public SourceBean getSb() {  
  11.         return sb;  
  12.     }  
  13.       
  14.     @MyDI  
  15.     public void setSb(SourceBean sb) {  
  16.         this.sb = sb;  
  17.     }  
  18.   
  19.     public void callSB(){  
  20.         sb.hello();  
  21.     }  
  22. }  

这个类有一个SourceBean成员和对应的setter方法,spring比较提倡通过setter方法注入的,我这边也实现了setter方法注入。

注意到有一个自定义的@MyDI annotation,这里的作用就是为了标记这里是个注入点,在利用反射初始化DestinationBean时,要在这里把SourceBean注入进来。

看一下这个@MyDI:

  1. package com.red.annotations;  
  2.   
  3. import java.lang.annotation.Retention;  
  4. import java.lang.annotation.Target;  
  5. import java.lang.annotation.ElementType;  
  6. import java.lang.annotation.RetentionPolicy;  
  7.   
  8. @Retention(RetentionPolicy.RUNTIME)      
  9. @Target(ElementType.METHOD)  
  10. public @interface MyDI {  
  11.   
  12. }  

这个annotation只能用于方法上,且保留时间到runtime,这样才能在运行时利用反射获取。

 

好了bean都有了,看看container是怎么做注入的吧:

  1. package com.red.beans;  
  2.   
  3. import java.lang.reflect.InvocationTargetException;  
  4. import java.lang.reflect.Method;  
  5.   
  6. import com.red.annotations.MyDI;  
  7.   
  8. public class Container {  
  9.   
  10.     private static Container container=new Container();  
  11.       
  12.     public static Container getContainer(){  
  13.         return container;  
  14.     }  
  15.       
  16.     /** 
  17.      * @param args 
  18.      */  
  19.     public static void main(String[] args) {  
  20.           
  21.         try {  
  22.             DestinationBean db=(DestinationBean) container.getInstance(DestinationBean.class);  
  23.             db.callSB();  
  24.         } catch (InstantiationException e) {  
  25.             // TODO Auto-generated catch block  
  26.             e.printStackTrace();  
  27.         } catch (IllegalAccessException e) {  
  28.             // TODO Auto-generated catch block  
  29.             e.printStackTrace();  
  30.         } catch (ClassNotFoundException e) {  
  31.             // TODO Auto-generated catch block  
  32.             e.printStackTrace();  
  33.         } catch (IllegalArgumentException e) {  
  34.             // TODO Auto-generated catch block  
  35.             e.printStackTrace();  
  36.         } catch (InvocationTargetException e) {  
  37.             // TODO Auto-generated catch block  
  38.             e.printStackTrace();  
  39.         }  
  40.           
  41.           
  42.   
  43.     }  
  44.       
  45.     public Object getInstance(Class<?> clazz) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException{  
  46.         Object obj = clazz.newInstance();  
  47.           
  48.         Method[] methods=clazz.getDeclaredMethods();//get all the methods  
  49.         for(int i=0;i<methods.length;i++){//iterate them  
  50.             if(methods[i].isAnnotationPresent(MyDI.class)&&methods[i].getName().startsWith("set")){//if annotated and is setter  
  51.                 methods[i].invoke(obj, container.getInstance(methods[i].getParameterTypes()[0]));//  
  52.             }  
  53.         }  
  54.         return obj;  
  55.     }  
  56.   
  57. }  

主函数中得到单例container,然后通过类名来初始化,而不是用new。

在初始化destinationBean时,遍历找出有@MyDI的setter方法,根据setter方法参数的类型来递归注入对象。

当主函数中得到destinationBean时,里面的SourceBean已经被注入进去了

 

输出结果:I am injected

 

其实spring利用xml配置文件也无非做了些说明工作,容器内有哪些类,注入的点在哪里,等等。。。只是spring容器总是在启动时就已经拥有了所有这些信息,当用户调用getbean之类的方法时可以自动完成上述类似的操作。

 

当然注入点可以有很多,JEE6中的注入方法有很多都是在成员上注入,比如@EJB,@Resource等等

我本来想用成员注入,但是发现这样做是可以办到的,但是有破坏类的封装性之嫌,如果一个成员是private的,在利用反射时需要把它改成public的,然后再赋值。

当然JEE的注入还可以用annotation里的成员,比如可以为@MyDI加一个name字段,这个值可以是一个类名,或者是一个jndi等等,在注入时,可以通过读取annotation中的信息来定制注入的内容。

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多