开发自己IoC容器

一 .IoC简介
在平时的开发中,当我们正在编写的某一个类需要用到另外的一个类(组件)的时候,我们都需要通过 new 的关键来创建该类的实例,那么有没有更好的方式,可以不在我们的代码中直接去 new 这个类,就可以得到该实例呢?IoC 的概念很好的帮助我们解决了这个问题,当我们需要在一个类中使用另外的类时,可以通过一些配置来得到该类的实现。IoC 是 Inversion of Control 简称,又称控制反转,一个类需要另外一个类的实例,可以通过某个容器获得,而不在类的内部创建,得到什么样的实例,由容器去决定,而不是由该类决定。搞清楚了控制反转的概念后,我们再来了解什么叫依赖注入。依赖注入(Dependency Injection)与控制反转是同一个概念,我们创建某个类的实例由 IoC 容器完成,如果该类需要使用另外的类实例,那么可以在 IoC 容器中向该类注入需要被调用的实例。无论是控制反转还是依赖注入,采用这种方式来创建类的实例,可以使得我们的代码更加清晰明了,将各个类之间的依赖关系反映到 IoC 容器中,可以更动态、灵活和透明的管理各个对象。在 J2EE 领域
中使用 IoC 的概念,可以将各个功能不同的组件统一放到 IoC 容器中,程序员只需要关注各个组件的实现,而不需要关注各个组件的依赖的关系。常用的 IoC 容器有 Spring 的 IoC 容器、webwork 的 IoC 容器、google-guice,apache 的 HiveMind 等。

二 .使用技术简介

2.1 Java 的反射机制
在 Java 运行时环境中,如果我们需要得到某一个类的具体信息,那么就可以使用 Java 的反射机制,该机制可以让我们动态的得到某个类型的属性、构造器和方法。Java 的反射机制可以在运行时构造某一个类的实例,在运行时调用任意一个对象的方法。

2.2 dom4j
dom4j 是一个常用的 XML 解析项目,该项目是一个易用的开源项目,它应用于 Java 平台,支持使用 Dom、SAX 来解析 XML 文件。Dom4j 的使用非常简单,只需要使用该项目所提供的解析类,就可以轻松的读取到相应的 XML 文件,并可以得到这些 xml 的相关内容。

三 确定配置文件内容、编写DTD
在编写 IoC 容器之前,我们需要编写 XML 文件,这些 XML 文件用来定义人们 IoC 容器的一些配置,例如声明我们需要创建哪些对象(bean),为这些对象(bean)提供名字和类名,让我们的 IoC 容器根据这些信息去创建相应的 bean。除了定义 bean 的名字和类名外,还需要定义一些其他的属性,例如该bean 是否为单态,是否需要延迟加载定。确定了配置文件的内容后,我们开始着手编写 DTD 文件。
3.1 声明bean
在整个 IoC 容器中,每一个 bean 代表具体的某个类,因此我们的根节点为 beans,beans 下有多个 bean,可以让我们配置具体的某个类。XML 文件的具体配置如下:


定义好了 bean 节点好,我们需要为 bean 节点指定一个名字和类名,表示这个 bean 所对应的名称与类型,这些我们的 IoC 容器得到这些配置后,就可以帮我们创建这些类的实例。具体的配置如下:

id=”myDate” class=”java.util.Date”>
以上的 bean 配置表示我们在 IoC 容器中创建了一个 myDate 的 bean,该 bean 的类型是java.util.Date。
3.2 声明单态的bean
定义一个 bean 除了需要显式声明该 bean 的名称和类型外,还需要告诉我们的 IoC 容器,该 bean是否为单态,如果该 bean 是单态的话,那么 IoC 容器在启动的时候,就只会为该 bean 创建一个实例,并将该实例缓存起来,如果该 bean 配置成非单态的话,那么就不进行缓存,每次请求这个 bean 的时候,就重新创建一个。我们为 bean 的配置添加一个 singleton 属性,用来声明 bean 是否为单态。

id=”myDate” class=”java.util.Date” singleton=”false”>
以上配置的黑体部分,我们配置了 myDate 这个 bean 为非单态,在一般的情况下,我们可以不指定 singleton 属性,让 singleton 属性在不显式声明的情况下的值为 true,给配置节点提供默认值
3.3 声明延迟加载
当 IoC 容器启动的时候,容器是否需要马上创建这个 bean,我们可以为配置文件提供一个属性,让容器知道我们在容器初始化的时候,是否需要创建。我们为 bean 提供一个 lazy-init 的属性:

id=” myDate” class=” java.util.Date” lazy-init=”true”>
以上的配置代码中,声明了 myDate 需要延迟加载,为 bean 节点加了这个属性后,beans 节点也可以提供一个 default-lazy-init 的属性,表示这份配置文件下的所有 bean,如果 lazy-init 属性的值为default 的时候,就使用 beans 节点(根节点)的 default-lazy-init 所配置的值:

default-lazy-init=”true”>
id=”myDate” class=”java.util.Date” lazy-init=”default”>
以上的配置,如果 myDate 的 bean 声明为 default 的话,那么就按照 beans 的 default-lazy-init 属性来决定是否需要延迟加载。如果对 bean 的 lazy-init 属性不指定值的话,那么就为这个 lazy-init 属性指定为 default,表示这个 bean 是否延迟加载取决于 beans 的配置,而 beans 的 default-lazy-init 属性不显式指定的话,可以使用 false 作为默认值。
3.4 声明设值注入到bean的属性
IoC容器除了可以帮我们创建bean之外,还可以帮助我们将对应的属性设置到该bean的实例里面,我们需要为每个 bean 注入一些属性,让该 bean 的实例得到这些属性,这些属性有可能是一些普通的值,也有可能是另外的定义的 bean。在这里我们使用设值注入,设值注入,就是创建了 bean 的实例后,再获得该 bean 配置的一些属性,通过该 bean 里面的 setter 方法,设值到该 bean 的实例中。

id=”student” class=”org.crazyit.Student”>
name=”school”>
bean=”school”>
id=”school” class=”org.crazyit.School”>
以上的配置中的黑体部分,我们为 student 的 bean 注入了一个 school 的 bean,这样,school 和student 这两个 bean 就产生了依赖关系,这样的注入我们就叫依赖注入,以上的注入方式是依赖注入中的设值注入。那么我们的配置文件中需要为 bean 加入一个 property 的子节点,该节点里面有一个 name属性,property 节点下面有一个 ref 子节点,ref 子节点指向某一个容器中的 bean。除了注入另外定义
的 bean 外,还可以向 bean 中注入一些普通的属性:

id=”student” class=”org.crazyit.Student”>
name=”age”>
type=”java.lang.Integer”>18
以上的配置表示向 student 这个 bean 中注入了一个 Integer 的值,对应 Student 的 age 属性。因此,property 节点下面可以出现 ref 节点和 value 节点,但是,每一个 property 只能出现一次 ref 节点或者value 节点,因为我们一个 property 只会设置一个属性。
3.5 声明构造注入到bean的属性
3.4 中,我们定义配置文件中为 bean 提供 property 节点进行设值注入,本小节我们定义构造注入的节点。构造注入,也就是通过 bean 中定义的构造参数,在创建这个 bean 的实例时,就将这些参数通过调用该 bean 的构造器,将这些配置的属性传递给该 bean。

id=”student” class=”org.crazyit.Student”>
bean=”school”>
以上配置的黑体部分,声明了创建 student 这个 bean 的时候,我们需要为 Student 类提供一个构造器,构造器的参数为 School 类型,那么 IoC 容器在创建时,就会根据 constructor-arg 声明的属性,去调用 Student 的构造器。与 14.3.4 中的设值注入一样,除了可以提供 ref 元素外,还可以提供 value元素,构造注入一些另外的普通属性。

id=”student” class=”org.crazyit.Student”>
type=”java.lang.Integer”>20
这样的配置,我们就可以为 Student 进行构造注入,并在创建 bean 的实例时,提供一个 Integer类型的构造参数。
3.6 自动装配
自动装配,就是不需要指定 bean 的属性,从 IoC 容器中查找 bean 所需要的属性,将这些查找到的 bean 以设值注入的方式依赖注入到目标的 bean 中。

id=”student” class=”org.crazyit.Student” autowire=”byName”>
id=”school” class=”org.carayit.School”>
以上的 student 提供了 autowire 属性,该属性值为 byName,表示根据 bean 的名称自动注入到student 中,如果 Student 类中有一个 setSchool 的 setter 方法,并且参数为 Schoold 对象,那么容器就需要自动的将 School 的 bean 通过设值注入设置到 Student 中。与延迟加载一样,也可以为 beans节点提供一个 default-autowire 属性,声明该 beans 根节点下面所有的 bean 节点,如果 autowire 的值为 default,那么就可以进行自动装配。

autowire=”byName”>
id=”student” class=”org.crazyit.Student” autowire=”default”>
id=”school” class=”org.carayit.School”>
在本例中 autowire 的值我们定义为只允许为 no 或者 byName。如果 autowire 的值为 no 的话,表示不需要自动装配。bean 的autowire 属性默认值为 default。确定了 IoC 的配置文件需要配置的内容,那么接下来,我们就可以根据上述的配置内容,编写相关的约束文件(DTD)。

。。。。待续