这是一个解释javabean的容器原理的例子,有像spring容器的意思, 实现了annotation在setter方法上注入的效果,我没有看过spring或其他ejb容器的代码,自己猜测javabean容器应该就是这样实现的,所谓的DI或者叫ioc的就是这样做到的,利用xml配置文件和用annotation方法没有太大区别,两者都是为了描述注入点和注入对象。
先写一个被注入的类:
- package com.red.beans;
-
- public class SourceBean {
-
- public void hello(){
- System.out.println("I am injected");
- }
- }
没什么好说的
再写一个接受注入的类
- package com.red.beans;
-
- import com.red.annotations.MyDI;
-
- public class DestinationBean {
-
-
- private SourceBean sb;
-
- public SourceBean getSb() {
- return sb;
- }
-
- @MyDI
- public void setSb(SourceBean sb) {
- this.sb = sb;
- }
-
- public void callSB(){
- sb.hello();
- }
- }
这个类有一个SourceBean成员和对应的setter方法,spring比较提倡通过setter方法注入的,我这边也实现了setter方法注入。
注意到有一个自定义的@MyDI annotation,这里的作用就是为了标记这里是个注入点,在利用反射初始化DestinationBean时,要在这里把SourceBean注入进来。
看一下这个@MyDI:
- package com.red.annotations;
-
- import java.lang.annotation.Retention;
- import java.lang.annotation.Target;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.RetentionPolicy;
-
- @Retention(RetentionPolicy.RUNTIME)
- @Target(ElementType.METHOD)
- public @interface MyDI {
-
- }
这个annotation只能用于方法上,且保留时间到runtime,这样才能在运行时利用反射获取。
好了bean都有了,看看container是怎么做注入的吧:
- package com.red.beans;
-
- import java.lang.reflect.InvocationTargetException;
- import java.lang.reflect.Method;
-
- import com.red.annotations.MyDI;
-
- public class Container {
-
- private static Container container=new Container();
-
- public static Container getContainer(){
- return container;
- }
-
- /**
- * @param args
- */
- public static void main(String[] args) {
-
- try {
- DestinationBean db=(DestinationBean) container.getInstance(DestinationBean.class);
- db.callSB();
- } catch (InstantiationException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (ClassNotFoundException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (IllegalArgumentException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- } catch (InvocationTargetException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
-
-
-
- }
-
- public Object getInstance(Class<?> clazz) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IllegalArgumentException, InvocationTargetException{
- Object obj = clazz.newInstance();
-
- Method[] methods=clazz.getDeclaredMethods();//get all the methods
- for(int i=0;i<methods.length;i++){//iterate them
- if(methods[i].isAnnotationPresent(MyDI.class)&&methods[i].getName().startsWith("set")){//if annotated and is setter
- methods[i].invoke(obj, container.getInstance(methods[i].getParameterTypes()[0]));//
- }
- }
- return obj;
- }
-
- }
主函数中得到单例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中的信息来定制注入的内容。
|