众所周知,Spring以其强大而又灵活的IoC管理功能著称。IoC本质上是通过依赖注入DI的方式实现组件之间的解耦。其实在DI模式下,一个关键性的角色是装配器(assembler)。应用程序的组件A在引用别的组件B的时候不用通过new来创建,而是依赖一个第三方的装配器创建好B,然后再通过setter或者constructor来注入给A,这样A只管用B的接口,而不需要知道B的存在,从而实现了A与B的解耦。也实现了面向抽象(接口)编程。
Spring框架里面谁来充当这个assembler的角色呢?是BeanFactory。Spring提供了品种繁多的BeanFactory,共用户在不同场合下使用。我们通常更多的是使用它的子接口ApplicationContext。大致上分了三类:Java Application,Web Application 和 Portlet Application。
本文通过一个简单的例子(
http://svn.springbyexample.org/core/basic-dependency-injection/)来看看ClassPathXmlApplicationContext的调用和执行顺序图(用MaintainJ生成的):
上图的init代表执行构造函数。上图表达了下面这句话:
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/application-context.xml");
再看看类图:
再细致的看下去,分解StandardEnvironment的构建过程(可以看出是解析各种属性的过程):
属性都load好了,环境也构建好了,那么Bean的实例在哪里创建的呢?关键得看DefaultListableBeanFactory:
这里面关键是看registerSingleton函数,它负责把bean缓存到singletonObjects (private final Map<String, Object> singletonObjects = new ConcurrentHashMap<String, Object>();)
public void registerSingleton(String beanName, Object singletonObject) throws IllegalStateException {
Assert.notNull(beanName, "'beanName' must not be null");
synchronized (this.singletonObjects) {
Object oldObject = this.singletonObjects.get(beanName);
if (oldObject != null) {
throw new IllegalStateException("Could not register object [" + singletonObject +
"] under bean name '" + beanName + "': there is already object [" + oldObject + "] bound");
}
addSingleton(beanName, singletonObject);
}
}
/**
* Add the given singleton object to the singleton cache of this factory.
* <p>To be called for eager registration of singletons.
* @param beanName the name of the bean
* @param singletonObject the singleton object
*/
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, (singletonObject != null ? singletonObject : NULL_OBJECT));
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
从上面的逻辑上可以看出,同名的类是不能被缓存进去的,会抛出异常。
好了,后的程序想getBean的时候只需要从这个缓存中获取就好了。