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信息我们后续章节在看。