Spring IOC的深入理解(三)
引言
从第三章开始,我们从源码的角度,对spring IOC进行分析
核心类的了解

第一个要了解的类是DefaultListtableBeanFactory.Spring Bean的创建是典型的工厂模式,上面的工厂也是一个个不同的IOC容器.其中他们的父类接口为BeanFactory.定义了容器的基本功能规范.然后它有三个子类ListableBeanFactory,HierachicaBeanFactory,AutoWireCapableBeanFactory.最终实现了全部的功能的容器为DefaultListableBeanFactory.这就是第一个核心的类.DefaultListableBeanFactory是Spring注册以及加载的核心实现.然后就是XmlBeanFactory,主要是读取Xml配置文件中的BeanDefinition,然后bean的注册和获取是通过他的父类DefaultListableBeanFactory实现,唯一不同的就是增加了XmlBeanDefinitionReader的reader属性.XmlBeanDefinition主要是通过reader属性对资源文件进行读取和注册.
第二个核心类就是XMLBeanDefinitionReader,我们可以通过这个类和其中的功能对资源文件的解析,加载,注册进行梳理
//它不是XMLBeanDefinitionReader类中的属性,而是它父类中的属性
private ResourceLoader resourceLoader;
//它继承了这个接口,也就是这个接口完成了从资源文件的读取并实现转化成BeanDefinition的操作
public interface BeanDefinitionReader
public interface EnvironmentCapable
//将资源文件转化成Document的操作
private DocumentLoader documentLoader = new DefaultDocumentLoader();
BeanDefinitionDocumentReader
public class BeanDefinitionParserDelegate
我们大致来说一下Xml配置文件读取的大概步骤
- 通过继承AbstractBeanDefinitionReader中ResourceLoader,使用它将资源文件路径转化为Resource文件.
- 将Resource文件通过 DocumentLoader 转化为Document文件
- 通过BeanDefinitionDocumentReader读取Document并注册BeanDefinition
-
BeanDefinitionParserDelegate来对Element进行读取
我们来画一个图解释一下
图片.png
容器XMLBeanFactory的初始化
我们先来看这样的一段代码
XMLBeanFactory beanFactory=new XMLBeanFactory(new ClassPathResource("text.xml"));

我们首先来看看ClassPathResource是如何将text.xml转化成Resource的.
配置文件的封装
ClassPathResource是实现了Resource接口的,抽象了所有Spring内部用到的资源,File,URL,ClassPath下的资源和Byte Array.它只有一个方法的定义
getInputStream().对于不同的资源文件都有不同的实现,ClassPathResource是对ClassPath资源进行读取的.
如下的代码
Resource rs=new ClassPathResource("text.xml");
InputStream in=rs.getInputStream();
我们了解了Resource,然后我们再来了解一下ClassPathResource,它是通过class或者classloader提供的底层方法进行读取的
public InputStream getInputStream() throws IOException {
InputStream is;
if (this.clazz != null) {
is = this.clazz.getResourceAsStream(this.path);
}
else if (this.classLoader != null) {
is = this.classLoader.getResourceAsStream(this.path);
}
else {
is = ClassLoader.getSystemResourceAsStream(this.path);
}
if (is == null) {
throw new FileNotFoundException(getDescription() + " cannot be opened because it does not exist");
}
return is;
}
看上面的clazz和ClassLoader.
这个时候我们完成了配置文件到Resource文件转化.我们在回到我们的测试代码
XMLBeanFactory beanFactory=new XMLBeanFactory(new ClassPathResource("text.xml"));
下面执行的就是XMLBeanFactory的构造方法
public XmlBeanFactory(Resource resource) throws BeansException {
this(resource, null);
}
//其中实现的构造方法是
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
this.reader.loadBeanDefinitions(resource);就是核心的资源加载的核心体现.但是我们也注意到super(parentBeanFactory);它实际我们只要ctrl+鼠标一直找到它真正的实现.
public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory)
throws BeansException {
super(parentBeanFactory);
this.reader.loadBeanDefinitions(resource);
}
public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
super(parentBeanFactory);
}
public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
this();
setParentBeanFactory(parentBeanFactory);
}
public AbstractAutowireCapableBeanFactory() {
super();
ignoreDependencyInterface(BeanNameAware.class);
ignoreDependencyInterface(BeanFactoryAware.class);
ignoreDependencyInterface(BeanClassLoaderAware.class);
}
我们来看一下ignoreDependencyInterface接口的含义,忽略接口的自动装配能力,大概是A中有属性B,加载A是会默认初始化B,这就是自动装配,但是当B实现了BeanNameAware的接口时,就无法实现.
将无关紧要的属性分析完之后,我们就可以分析核心代码了this.reader.loadBeanDefinitions(resource),首先我们先分析一下this.reader是XmlBeanDefinitionReader的reader.注意:
private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);this就是将工厂自己传给reader
这个代码是整个资源加载的切入点,也是IOC容器初始化的切入点
首先我们reader将读取的Resource文件进行封装,通过EncodedResource进行封装
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
在reader中体现了EncodedResource的主要逻辑,如果有编码,就把有编码的InputStream返回回去,如果没有编码,就直接返回对应的InputStream
public Reader getReader() throws IOException {
if (this.charset != null) {
return new InputStreamReader(this.resource.getInputStream(), this.charset);
}
else if (this.encoding != null) {
return new InputStreamReader(this.resource.getInputStream(), this.encoding);
}
else {
return new InputStreamReader(this.resource.getInputStream());
}
}
然后我们来看看loadBeanDefinitions的方法
public int loadBeanDefinitions(EncodedResource encodedResource)
throws BeanDefinitionStoreException {
...
//从encodedResource中获取Resource对象,然后在获取inputStream
InputStream inputStream = encodedResource.getResource().getInputStream();
...
//最后进入核心部分
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
然后我们来看一下doLoadBeanDefinitions的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
....
}
- 获取Xml文件的验证模式
- 加载XMl文件,并得到对应的document
- 返回document并注册Bean信息
然后继续分析,对于将Resource文件转化成为document的文件,这件事情是交给DocumentLoader来做的.DocumentLoader是个接口,实际上实现的是它的实现类
Document doc = doLoadDocument(inputSource, resource);
->>>>>>
return this.documentLoader.loadDocument(inputSource, getEntityResolver()
, this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
->>>>>>
@Override
public Document loadDocument(InputSource inputSource, EntityResolver entityResolver,
ErrorHandler errorHandler, int validationMode, boolean namespaceAware) throws Exception {
DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
if (logger.isTraceEnabled()) {
logger.trace("Using JAXP provider [" + factory.getClass().getName() + "]");
}
DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
return builder.parse(inputSource);
}
这里并非是重点方法,这里就是告诉你DTD在哪儿,由程序来寻找DTD,避免了通过网络寻找.
其中将传入InputSource,然后通过DocumentBuilderFactory来解析 inputSource从而返回Document对象.
其中的EntityResolver值得我们思考,它是如何传入的?它是如何起作用的?
对于它是如何传入的,我们返回前几层的代码,看到
return this.documentLoader.loadDocument(inputSource, getEntityResolver(), this.errorHandler,
getValidationModeForResource(resource), isNamespaceAware());
其中的getEntityResolver()方法就是用来获得getEntityResolver的,我们在进入这个方法看一下
protected EntityResolver getEntityResolver() {
if (this.entityResolver == null) {
// Determine default EntityResolver to use.
ResourceLoader resourceLoader = getResourceLoader();
if (resourceLoader != null) {
this.entityResolver = new ResourceEntityResolver(resourceLoader);
}
else {
this.entityResolver = new DelegatingEntityResolver(getBeanClassLoader());
}
}
return this.entityResolver;
}
然后就是解析并注册BeanDefinition了,回到上面的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
....
}
然后就是去执行registerBeanDefinitions,这个时候我们有了document的对象
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
这个方法的核心类就是BeanDefinitionDocumentReader了,它是通过createBeanDefinitionDocumentReader创建的,创建的实际上是它的实现类
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
doRegisterBeanDefinitions(doc.getDocumentElement());就是注册BeanDefinition的核心,这个时候就开始解析和注册了
protected void doRegisterBeanDefinitions(Element root) {
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);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("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;
}
处理profile属性
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
我们先来聊一下profile属性的作用:切换开发和部署环境
parseBeanDefinitions(root, this.delegate);
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
//root的意思就是对应的beans属性
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
//两种处理方式1
parseDefaultElement(ele, delegate);
}
else {
//两种处理方式2
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
其中一类是默认的parseDefaultElement(ele, delegate);,令一类是自定义的
delegate.parseCustomElement(ele);