spring源码系列02-DefaultListableBeanFactory的介绍和使用

通过上一篇文章,了解到ApplicationContext系列容器的内部会持有一个DefaultListableBeanFactory容器,bean都是放在这个容器里头的。

它是BeanFactory的实现类。这一次就来探索下这个类,看看直接把这个类作为spring容器时如何使用。

首先给出这个类的类图

一、DefaultListableBeanFactory作为容器使用

作为一个容器肯定是可以用来管理bean的,在使用ClassPathxmlApplicationContext时,我们需要指定一个配置文件,在其中给出bean的定义信息。最终容器启动时所有的bean都是放在内部的BeanFactory里边的。那么如何单独使用BeanFactory呢。

1.1 如何描述Bean的定义信息

有配置文件时,bean信息是在配置文件中配的,但实际上最终配置信息会被spring解析成接口BeanDefinition

来描述Bean的定义信息。所以单独使用beanFactory时,我们需要以BeanDefinition的形式来给容器指定Bean定义信息。当然这是个接口,实际使用的是其实现类

如下代码,使用RootBeanDefinition来描述了一个Bean

BeanDefinition bd = new RootBeanDefinition(Person.class);

1.2 如何给spring容器中指定bean定义

现在我们已经通过BeanDefinition描述了bean的信息,那如何添加到spring容器中呢?

观察上边的类图,DefaultListableBeanFactory实现了BeanDefinitionRegistry接口,从名字就可以看出这个接口中的方法可以用来给spring容器中注册bean,其中有一个方法

	void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
			throws BeanDefinitionStoreException;

这个方法就是用来给容器中注册bean的。

1.3 创建并使用容器

通过上边的分析我们已经可以描述一个bean了,接下来就真正使用下容器,可以分为这几个步骤:

创建容器,创建bean定义信息,注册bean定义信息,使用bean对象

public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        BeanDefinition bd = new RootBeanDefinition(Person.class);
        beanFactory.registerBeanDefinition("person",bd);
        System.out.println(beanFactory.getBean(Person.class));
}

上边的代码,就完成了把DefaultListableBeanFactory作为容器,向其中注册bean信息,获取bean这一个过程。

1.4 DefaultListableBeanFactory的懒加载

如果使用ApplicationContext类型的容器,当容器启动完成后所有的bean就已经被创建好了,而使用BeanFactory类型的容器,容器启动完成后并不会主动创建bean,只有主动从容器中getBean时才会真正的去创建该类型的bean。这就是懒加载。

验证: 可以给person类添加一个无参数的构造方法,在其中输出一句话,然后去掉上边的输出语句在运行,就会发现该构造方法并没有执行;加上输出语句后构造方法会执行。

二、DefaultListableBeanFactory中bean定义信息是如何存储的

上边完成了使用DefaultListableBeanFactory作为spring容器来管理bean,现在在思考一个问题,bean的定义信息在这个类中是如何存储的?

我们从该类的registerBeanDefinition方法入手

Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition){
...
操作 this.beanDefinitionMap
}

方法内容很多,但其实都是在操作this.beanDefinitionMap,所以bean的定义信息是放在这个类的这个成员中的。

/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);

上边是定义成员变量的地方,通过源码的注释也可以看出它的作用。

三、DefaultListableBeanFactory中是如何存储单例bean的

这里我们在延伸一下,把这个类作为spring容器时,创建好的bean是在哪里存储的?

这里只考虑单例bean,平时这种类型的bean使用的是最多的。

观察上边的类图,其中有一个类叫做DefaultSingletonBeanRegistry,这个类中有一个成员变量singletonObjects,它就是用来存放我们的单例bean的,所以它又被叫做单例池

/** Cache of singleton objects: bean name to bean instance. */
	private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);

可以通过在DefaultListableBeanFactory类中追踪getSingleton方法来定位到这个成员。这个方法是从DefaultSingletonBeanRegistry,中继承的。

四、如何加载xml配置文件

上边对beanFactory容器的使用,都是在手动注册bean信息,那么如何去读取配置文件呢,就像使用ClassPathxmlApplicationContext 一样,其实spring提供了一个XmlBeanDefinitionReader

ClassPathxmlApplicationContext这个容器内部也是使用这个reader来读取bean信息的

public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions("applicationContext.xml");
        System.out.println(beanFactory.getBean(Person.class));
    }

这样就可以直接从xml文件中读取bean定义信息,至于如何解析注解来读取bean信息我们后续章节在看。