Spring框架的核心就是IoC,而Spring中IoC的实现是依靠容器的,Spring提供了两种IoC容器的实现,BeanFactory和ApplicationContext。
前者是一个基础的IoC容器,提供了完整的IoC服务支持,默认采用延迟初始化策略(当用户访问时才初始化)。而后者是在前者的基础上实现的,ApplicationContext对BeanFactory进行了很多的扩充,而使用起来也更加方。便,所以在一般的应用中,ApplicationContext应该是更好的选择。
但这并不是完全否认了BeanFactory,毕竟ApplicationContext也必须实现这个接口。而且在一些资源有限,对功能要求不是很严格的场景,BeanFactory还是比较合适的选择。所以在学习Spring的时候可以先从BeanFactory入手,一步步的了解Spring的IoC容器。
我们知道,在Spring中,交由Spring管理的pojo会被配置成一个bean,而BeanFactory从字面理解就是一个bean的工厂。我们可以去查看BeanFactory接口的源码:
public interface BeanFactory {
String FACTORY_BEAN_PREFIX = "&"; Object getBean(String name) throws BeansException;
Object getBean(String name, Class requiredType) throws BeansException;
/**
* @since 2.5
*/
Object getBean(String name, Object[] args) throws BeansException;
boolean containsBean(String name);
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* @since 2.0.3
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* @since 2.0.1
*/
boolean isTypeMatch(String name, Class targetType) throws NoSuchBeanDefinitionException;
Class getType(String name) throws NoSuchBeanDefinitionException;
String[] getAliases(String name);
}
其中最主要的方法就是getBean(String,Class),显而易见,我们可以通过这个方法从工厂中得到一个Bean来初始化我们的对象。而其他方法也可以通过名字知道其作用,都是得到或判断Bean的属性。
Ok,了解了BeanFactory方法之后,我们就可以使用它完成所谓的IoC,就是说对象的成员由BeanFactory为我们推送过来。
假设有以下一个pojo:
package com.zrabbit.production;
import java.util.List;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import com.zrabbit.production.part.Engine;
import com.zrabbit.production.part.Wheel;
@SuppressWarnings(deprecation)
public class Car
{
private Engine engine;
private List wheels;
public Engine getEngine()
{
return engine;
}
public void setEngine(final Engine engine)
{
this.engine = engine;
}
public List getWheels()
{
return wheels;
}
public void setWheels(final List wheels)
{
this.wheels = wheels;
}
public void showInfo()
{
engine.showInfo();
for (final Wheel wheel : wheels)
{
wheel.showInfo();
}
}
public void launch()
{
engine.launch();
}
}
内部的Engine和Wheel可以随意实现,然后在工程的classpath下有一个名字为beans.xml的配置文件,内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<bean id="myCar" class="com.zrabbit.production.Car">
<property name="engine" ref="engineOne" />
<property name="wheels" >
<list>
<ref bean="wheelNumOne" />
<ref bean="wheelNumOne" />
<ref bean="wheelNumOne" />
<ref bean="wheelNumOne" />
</list>
</property>
</bean>
<bean id="engineOne" class="com.zrabbit.production.part.EngineOne" />
<bean id="wheelNumOne" class="com.zrabbit.production.part.WheelNumOne" />
</beans>
pojo和配置文件都有了,现在就需要使用BeanFactory将两者联系到一起完成IoC。我们可以找到BeanFactory的一个实现类:XmlBeanFactory,但当使用的时候会发现这个实现类已经废弃了,没关系,至少比没有强。
同时,XmlBeanFactory的构造器需要一个Resource参数(当然有两个构造器,另一个需要Resource和parentBeanFactory,这里我们不必关心parentBeanFactory),这个参数用于读取xml配置文件,我们可以通过其实现类ClassPathResource初始化这样一个对象。
现在,我们可以在Car类中写一个main方法来运行下这个简单的Spring程序:
public static void main(final String[] args)
{
final Resource res = new ClassPathResource("beans.xml");
final BeanFactory bf = new XmlBeanFactory(res);
final Car myCar = (Car) bf.getBean("myCar");
myCar.showInfo();
}
很简单吧,通过代码我们也可以很清楚的看出BeanFactory的构建与使用过程,首先通过Resource类将配置文件的路径告诉给XmlBeanFactory,然后XmlBeanFactory在这个文件中寻找到id为myCar的bean,根据bean内的信息初始化出我们要的对象。
那么在XmlBeanFactory中做了些什么呢,我们可以去查看这个类的源码。我们发现,XmlBeanFactory继承自DefaultListableBeanFactory,这个父类是一个很古老(2001年就存在了),并且很重要(ApplicationContext中也会用到它)的IoC容器实现,BeanFactory的IoC功能主要来源于这个类,有兴趣可以去看下。
我们还是主要关注XmlBeanFactory中的东东,这个类中有一个成员叫做XmlBeanDefinitionReader,通过名字就可以看出这货是用来读我们的XML文件的,它的构造器将XmlBeanFactory本身传入(this),其实这正需要的是其父类DefaultListableBeanFactory,这里就多说两句吧,DefaultListableBeanFactory间接地实现了BeanFactory接口,并且还实现了BeanDefinitionRegistry接口,该接口是在BeanFactory的实现中担当Bean注册管理的角色(说简单点就是让你写在XML中的bean真正的成为一个bean并保存下来)。XmlBeanFactory的构造方法也非常简单,就是直接用上面说到的reader去加载传入的Resource。
Ok,说了这么多,也应该清楚了BeanFactory的执行过程了,我们可以自己尝试实现一个BeanFactory:
1. 创建IoC抽象资源,就是那个Resource类
2. 创建一个DefaultListableBeanFactory,因为需要它完成Bean注册及BeanFactory主要功能。
3. 创建一个XmlBeanDefinitionReader,将DefaultListableBeanFactory传入。
4. 用XmlBeanDefinitionReader对象读取Resource。