BeanFactory bf = new XmlBeanFactory(new ClassPathResource("application.xml")); ①
TestBean testBean = (TestBean)bf.getBean("testBean"); ②
①: spring bean 配置文件的读取
new ClassPathResource("application.xml")
返回一个 Resouce 的接口
Resource 接口抽象了Spring内部使用到的底层资源,File,Url,ClassPath等- 首先 定义了3个判断当前资源窗台的方法,存在性 exists() , 可读性 isReadable() , 是否处于打开状态 isOpen()
- 获取属性 isModified(), getFilename()
- 对应不同来源的资源有不同的实现: ByteArrayResource,DescriptiveResource,FileSystemResource,ClassRelativeContextResource,UrlResource,InputStreamResource 。
XmlRBeanFactory( Resource resource) 构造方法接收 resource 对象,使用XmlBeanDefinitionReader来加载Bean
准备工作(
XmlBeanDefinitionReader
)- 封装资源文件
- 获取输入流。从Resource中获取对应的InputStream 并构造InputSource
通过构造的InputSource 实例和 Resource实例调用函数 doLoadBeanDefinitions
- 由于xml文件编码问题,第一步首先是是对资源文件进行编码处理 EncodeResource 构造出一个 EncodedResource 已编码的 Resource
return doLoadBeanDefinitions(inputSource, encodedResource.getResource())
(核心方法) 通过InputSource 和 EncodedResource 来加载bean是读取配置文件真正的核心部分了。第一步 是通过[ inputSource,getEntityResolver(), errorHandler, validationMode, isNamespaceAware()]获取 Document 对象。(
DocumentLoader
)DocumentBuilderFactory factory = createDocumentBuilderFactory(validationMode, namespaceAware);
通过 validationMode 和 namespaceAware 获得一个 DocumentBuilderFactory对象DocumentBuilder builder = createDocumentBuilder(factory, entityResolver, errorHandler);
通过factory,entityResolver,errorHandler 获取一个 DocumentBuilder 对象。- 通过
builder.parse(inputSource);
得到document
第二步
registerBeanDefinitions(doc, resource);
返回本次加载bean的数量documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
主要关注这个方法。 方法内从doc的root节点开始遍历文档读取配置。doRegisterBeanDefinitions(root);
的源码:
PROFILE_ATTRIBUTE
中间部分主要在校验这个属性,xml配置中 profile 属性可以在生产环境和开发环境中配置2套不同的配置,这样方便随时切换,spring在这里是先读取的这个判断做出校验。
preProcessXml(root);
和postProcessXml(root);
的代码均为空,应该是使用了模板模式,让子类可以扩展。
parseBeanDefinitions(root, this.delegate);
的源码:
parseBeanDefinitions 中 会先判断delegate.isDefaultNamespace(ele)
是否是默认命名空间(根据 element 的 BEANS_NAMESPACE_URI ),如果是就使用spring默认的读取规则,否则就使用BeanDefinitionParserDelegate
的转换方法。2个parseDefaultElement
是完全不同的逻辑
- BeanDefinion 是一个接口,在Spring中存在三种实现: RootBeanDefinition,ChildBeanDefinition,GenericBeanDefinition. 三种均继承自 AbstractBeanDefinition。BeanDefinition是配置文件
<bean>
元素标签在容器中的内部表现形式。<bean>
中的 class, scope,lazy-init 对应 BeanDefinition中的 RootBeanDefinition是常用的实现类。GenericBeanDefinition是一站式服务类。<bean>
元素可以定义父类和子类,一般父类是 RootBeanDefinition 子类是 ChildBeanDefinition。 BeanDefinitionRegistry类相当于spring的bean的内存数据库,以map形式保存bean的信息
DefaultBeanDefinitionDocumentReader 的
parseDefaultElement
方法
方法对应 IMPORT_ELEMENT,ALIAS_ELEMENT, BEAN_ELEMENT, NESTED_BEANS_ELEMENT 这4种标签
BEAN_ELEMENT
BeanDefinitionHolder
是一个bean定义的持有对象,有private final BeanDefinition beanDefinition;
的成员和 beanName , aliases[] 别名数组 。注册bean时需要这个对象delegate.parseBeanDefinitionElement(ele);
转换element 元素为 beanDefinitionHolder
- 获取 id 和 name 和 aliases 节点的值 ,并把id作为 beanName 的值
- 校验 beanName 唯一性
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
获取 BeanDefinition
- 获取类名,父类名
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
根据类名和父类名创建一个 BeanDefinition。具体方法:BeanDefinitionReaderUtils.createBeanDefinition(
parentName, className, this.readerContext.getBeanClassLoader());
方法内通过ClassUtils.forName(className, classLoader)
获取bean的类,返回GenericBeanDefinition- 在
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
中设置BeanDefine各类基本详细属性 parseMetaElements(ele, bd);
这些根据方法名都很好理解
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
如果没有检测到 beanName 使用默认规则为此Bean 设置 BeanName
- 最后根据获得的 bd 返回 BeanDefinitionHolder