Spring Ioc 源码学习笔记
概述
Spring 里面最重要的特性就是 Ioc,可能你还会说 aop。其实 aop 的实现也是基于 ioc。Ioc (Inversion of Control),即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
下面具体看下spring如何加载bean,初始化bean的。
源码分析
我们先入口看一下:
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
super(parent);
//设置配置文件路径
setConfigLocations(configLocations);
//核心部分
if (refresh) {
refresh();
}
}
再具体看下refresh()的核心代码:
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
prepareRefresh();
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
prepareBeanFactory(beanFactory);
try {
postProcessBeanFactory(beanFactory);
invokeBeanFactoryPostProcessors(beanFactory);
registerBeanPostProcessors(beanFactory);
initMessageSource();
initApplicationEventMulticaster();
onRefresh();
registerListeners();
finishBeanFactoryInitialization(beanFactory);
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
下面具体分析一下:
prepareRefresh()
准备上下文的刷新,主要设置启动时间、激活和关闭的标志,以及应用事件监听集合的初始化,此处比较简单就不具体展开了,具体源码位置:AbstractApplicationContext—>prepareRefresh().
obtainFreshBeanFactory()
该方法的作用是:创建BeanFactory实例,并解析Spring的xml配置文件,放入beanDefinitionMap中,对于的key为beanName ,value 则是beanDefinition。
看看几个关键的函数:
refreshBeanFactory()
@Override protected final void refreshBeanFactory() throws BeansException { //销毁已经存在的Beans,并关掉BeanFactory if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创建beanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); //设置重复name覆盖和循环引用 customizeBeanFactory(beanFactory); //加载bean到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); } }
这里要注意的是customizeBeanFactorys设置覆盖的问题,就是在配置文件中定义 bean 时使用了相同的 id 或 name,默认情况下,allowBeanDefinitionOverriding 属性为 null,如果在同一配置文件中重复了,会抛错,但是如果不是同一配置文件中,会发生覆盖。
loadBeanDefinitions(beanFactory)
此处主要实例化一个XmlBeanDefinitionReader对象来为后面解析xml,装载bean做准备
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
//为beanFactory初始化一个XmlBeanDefinitionReader主要负责加载配置、解析。
//这里可以看出来ApplicationContext不负责解析xml,而是委托XmlBeanDefinitionReader来解析
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
// 设置beanDefinition参数
// 加载环境参数
beanDefinitionReader.setEnvironment(this.getEnvironment());
beanDefinitionReader.setResourceLoader(this);
beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
// 初始化beanDefinitionReader
initBeanDefinitionReader(beanDefinitionReader);
//beanDefinition读取
loadBeanDefinitions(beanDefinitionReader);
}
在具体看loadBeanDefinitions(beanDefinitionReader);之前,我们先看下BeanDefinition这个接口的一些方法
BeanDefinition.java
看完beanDefinition之后,我们继续看如何加载bean的定义loadBeanDefinitions(beanDefinitionReader);
注意一下,我这里省去了中间的一部分代码,这个看一下就好了,直接看核心部分。
还需要说明一下的是:这里的Resource其实是怎么来的,其实是从configLocations(这个大家要记得在最开始初始化的时候 setConfigLocations(configLocations)的时候设置的值),转成Resourced的,核心代码为:
Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
怕混淆,注明一下这个方法的路径:XmlBeanDefinitionReader–>loadBeanDefinitions(EncodedResource encodedResource)
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isInfoEnabled()) {
logger.info("Loading XML bean definitions from " + encodedResource.getResource());
}
Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
//前面都是一些变量的初始化,以及将encodedResource放入到resourcesCurrentlyBeingLoaded这个ThreadLocal变量中
//这里是具体的读取过程
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
继续往下看doLoadBeanDefinitions这个方法,省去了try{}catch
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
//将inputstream流读取到doc
Document doc = doLoadDocument(inputSource, resource);
//bean的注册
return registerBeanDefinitions(doc, resource);
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
//继续
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
//返回bean的数量
return getRegistry().getBeanDefinitionCount() - countBefore;
}
registerBeanDefinitions方法主要是获取Element元素,其主要是doRegisterBeanDefinitions方法
protected void doRegisterBeanDefinitions(Element root) {
// Any nested <beans> elements will cause recursion in this method. In
// order to propagate and preserve <beans> default-* attributes correctly,
// keep track of the current (parent) delegate, which may be null. Create
// the new (child) delegate with a reference to the parent for fallback purposes,
// then ultimately reset this.delegate back to its original (parent) reference.
// this behavior emulates a stack of delegates without actually necessitating one.
//BeanDefinitionParserDelegate 用于解析XML bean定义的有状态委托类。
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);
if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isInfoEnabled()) {
logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}
preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);
this.delegate = parent;
}
未完待续。。。