好多看源码的小伙伴,像是在玩一个没有主线指引的游戏,在诺大的地图上到处碰壁,时不时钻进死胡同,一次次消磨掉了耐心,失去了再玩下去的勇气;这次呢,我来做你的引导npc;
其实坚持不下去,真的不怪你。
首先:spring已经发展很多年了,你不是从第一行代码看起。
其次:先有了设计思想,才有代码实现。而反过来从代码去找思想,就是反人类的做法
再者:代码量繁多,你不需要看完所有的代码。只需要看懂关键部分,看懂关键部分之后,其余的细枝末节需要用的时候再去看(借助文档等等工具),你会很轻松的看懂他们
首先最宏观来看spring就是一个容器,容器里面可以装很多个bean,每个bean之间有着互相有关系,可以互相引用。这个容器被称为ioc容器;
从宏观上它做了以下的这些事情;
约定:咱们这里就以ClassPathXmlApplicationContext这个ioc容器的实现来进行讲解,其余的触类旁通;而且细节不做深入研究,点到即止。具体细节读者知道思想后再去看源码也不会困难
知识点扫盲:
什么是beanDefinition?
beanDefinition就是spring用来封装bean定义信息的一个模型。把原始的bean元数据读取出来解析后通过该模型进行管理,和使用。好处当然是统一了bean定义信息。站在核心容器的视角:隔离了多种bean定义方式的复杂解析逻辑(比如xml,注解,properties等等,你甚至可以自定义)。核心容器只管取统一的beanDefinition来使用即可;
今天就来看读取bean配置信息的内容:先上图
也就是下图中的1,2,3标号,如何从xml转换成beanDefinition的
classPathXmlApplicationContent是通过创建一个XmlBeanDefinitionReader的读取器来对xml进行读取翻译成BeanDefinition的。
这里面几个定义你需要了解:
1.ClassPathXmlApplicationContent: 就是一个容器的实现
2.XmlBeanDefinitionReader: 用来读取解析xml中的bean配置的类
3.BeanDefinition: 存储bean定义信息一个包装类,提供bean的配置信息
!!!那么关键就是去找到这个XmlBeanDefinitionReader在哪里创建和使用的;
跟着路标来找代码,没有标路标的部分可以暂时不看:
路标1:构造器
构造ioc容器
//构造ioc容器
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
路标2:另一个构造器
调用另一个参数较多的构造器
public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
//调用另一个构造器来进行构造
this(new String[] {configLocation}, true, null);
}
路标3:refresh()
然后又通过找到refresh();刚启动肯定要强制刷新容器。买了新房子你还得装修呢不是?
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
//构造器的第一行先调用父级一个参数的构造函数
super(parent);
//把配置路径解析存到configLocations属性上
setConfigLocations(configLocations);
if (refresh) {
//刷新容器这里是真正干活的地方;
refresh();
}
}
路标4:obtainFreshBeanFactory
到这里方法开始变得复杂了,但是不要走错路,跟着路标走;这里面的东西你扫一眼就好了,主要看到obtainFreshBeanFactory()这个位置;这里面就是实例化一个具体容器,把配置加载出来的地方;(如果你懂装饰者模式的话可能会有些感触,不懂也没关系)
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
//准备进行容器刷新
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//告诉具体的子类去刷新包含的bean factory,这里其实是用的包装者模式,包装者中包含一个引用,然后返回该beanFactory容器对象
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//为容器做一些准备,设置一些它需要的东西和配置等等
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//允许子类去实现一些后置处理逻辑
postProcessBeanFactory(beanFactory);
// Invoke factory processors registered as beans in the context.
//执行容器中注册的BeanFactoryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册BeanPostProcessor,在创建bean的前后等执行
registerBeanPostProcessors(beanFactory);
// Initialize message source for this context.
//国际化相关,感兴趣自行了解
initMessageSource();
// Initialize event multicaster for this context.
//注册事件广播器
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
//子类重写这个方法,在容器刷新的时候可以自定义逻辑,默认什么都不做
onRefresh();
// Check for listener beans and register them.
//检查监听器bean并且注册,监听的就是上面广播器发出的各种不同类型的消息
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
//初始化所有剩下的非懒加载的单例bean!!!!!
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
//最后一步:发布相应的事件,比如容器刷新完成事件
finishRefresh();
}
catch (BeansException ex) {
logger.warn("Exception encountered during context initialization - cancelling refresh attempt", ex);
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
}
}
路标5:refreshBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //刷新beanFactory
refreshBeanFactory();
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (logger.isDebugEnabled()) {
logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
}
return beanFactory;
}
路标6:loadBeanDefinitions()
是不是看见曙光了。这里就是加载我们配置的xml等等信息的地方
protected final void refreshBeanFactory() throws BeansException {
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
路标7:找到对应的加载逻辑
类路径为
org.springframework.context.support.AbstractXmlApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)
我们可以看到ClassPathXmlApplicationContext就是这个类的子类;
这里主要逻辑就是构造一个bean定义信息读取器
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
// Create a new XmlBeanDefinitionReader for the given BeanFactory.
//创建一个类型为XmlBeanDefinitionReader的解析器来读取xml的内容
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// Configure the bean definition reader with this context's
// resource loading environment.
beanDefinitionReader.setEnvironment(this.getEnvironment());
//容器本身就是一个资源加载器的实现
beanDefinitionReader.setResourceLoader(this);
//设置一个实体的处理器
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// Allow a subclass to provide custom initialization of the reader,
// then proceed with actually loading the bean definitions.
//初始化redaer
initBeanDefinitionReader(beanDefinitionReader);
//进行读取
loadBeanDefinitions(beanDefinitionReader);
}
路标8:进到loadBeanDefinitions()
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
//加载Redource类型的资源
Resource[] configResources = getConfigResources();
if (configResources != null) {
reader.loadBeanDefinitions(configResources);
}
//加载字符串路径类型的资源最终还是会转换成Resource类型
String[] configLocations = getConfigLocations();
if (configLocations != null) {
reader.loadBeanDefinitions(configLocations);
}
}
到这里点到即止,再往下如何解析xml等等部分的内容,最终解析出beanDefinitionr然后通过org.springframework.beans.factory.support.BeanDefinitionRegistry#registerBeanDefinition方法把bean定义信息注册到BeanDefinitionRegistry中,
因为ClassPathXmlApplicationContext 本身就是一个BeanDefinitionRegistry的子类DefaultListableBeanFactory的一个装饰的装饰者,所以也可以说就是把定义信息注册到它自己中;
如何解析这部分内容不做深入,有兴趣自己看一下。
第一步做的事情:
ClassPathXmlApplicationContext 通过创建XmlBeanDefinitionReader 去把xml中bean定义信息转换成BeanDefinition加载到它自己中;为后续创建bean准备好了图纸
ClassPathXmlApplicationContext 在这个过程中拥有两个能力:
1.是一个ResourceLoader,资源加载器,能读取xml资源
2.是一个BeanDefinitionRegistry,是一个注册管理器,能管理bean定义信息
为什么说它拥有这两个能力呢,
第一个其实是因为ClassPathXmlApplicationContext 是ResourceLoader的简介子类
第二个呢,线索在refreshBeanFactory();其实是一个装饰者模式的体现,ClassPathXmlApplicationContext 其实是DefaultListableBeanFactory实现的一个装饰;而DefaultListableBeanFactory又是一个BeanDefinitionRegistry的实现,所以ClassPathXmlApplicationContext 可以使用BeanDefinitionRegistry管理器的逻辑;
你可以自己试试从源码中找到这段代码来验证:
protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
好了第一阶段bean信息加载到此结束了。bean的图纸画好了。但是这里的beanDefinition并不是真正的bean。只是一个bean定义信息而已;切记