SpringIOC源码之XmlBeanFactory
一、概述
1、说明
XmlBeanFactory是实现ioc方式之一,是那种比较屌丝的,功能对比ApplicationContext来讲差太多了。SpringIOC主要是如下三步骤
- 定位:说白了就是找到我们传递进去的xml文件。
- 加载:找到我们的xml文件位置后,将其解析成BeanDefinition对象。
- 注册:将bean对象放到ioc的工厂里(其实就是个hashmap)。
二、源码
1、伪代码
//根据Xml配置文件创建Resource资源对象,属于定位
ClassPathResource resource = new ClassPathResource("beans.xml");
//创建DefaultListableBeanFactory工厂,是BeanFactory最核心的实现类
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
//创建XmlBeanDefinitionReader读取器,用于载入BeanDefinition。之所以需要BeanFactory作为参数,是因为会将读取的信息回调配置给factory
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
//XmlBeanDefinitionReader执行载入BeanDefinition的方法,最后会完成Bean的载入和注册。完成后Bean就成功的放置到IOC容器当中,以后我们就可以从中取得Bean来使用
reader.loadBeanDefinitions(resource);
2、源码
2.1、定位
ClassPathResource resource = new ClassPathResource("beans.xml");
// 定位
public ClassPathResource(String path) {
this(path, (ClassLoader) null);
}
/**
* path:beans.xml
* classLoader:null
*/
public ClassPathResource(String path, ClassLoader classLoader) {
Assert.notNull(path, "Path must not be null");
String pathToUse = StringUtils.cleanPath(path);
// 若path是以/开头,则去掉/
if (pathToUse.startsWith("/")) {
pathToUse = pathToUse.substring(1);
}
// 将path赋值给ClassPathResource的path变量
this.path = pathToUse;
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
}
其实就是将资源文件路径传递给ClassPathResource的path变量
2.2、加载
2.2.1、创建工厂
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
2.2.2、创建读取器
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
/**
* registry:DefaultListableBeanFactory
*/
protected AbstractBeanDefinitionReader(BeanDefinitionRegistry registry) {
Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
// 将AbstractBeanDefinitionReader的registry赋值成DefaultListableBeanFactory
this.registry = registry;
// Determine ResourceLoader to use.
if (this.registry instanceof ResourceLoader) {
this.resourceLoader = (ResourceLoader) this.registry;
}
else {
// 设置resourceLoader为PathMatchingResourcePatternResolver
this.resourceLoader = new PathMatchingResourcePatternResolver();
}
// Inherit Environment if possible
if (this.registry instanceof EnvironmentCapable) {
this.environment = ((EnvironmentCapable)this.registry).getEnvironment();
}
else {
this.environment = new StandardEnvironment();
}
}
主要干了三件事
- 将AbstractBeanDefinitionReader的registry设置为将AbstractBeanDefinitionReader的registry赋值成DefaultListableBeanFactory
- resourceLoader = new PathMatchingResourcePatternResolver();
- this.environment = new StandardEnvironment();
2.2.3、加载(核心)
reader.loadBeanDefinitions(resource);
// 啥也没干,就是给resource包了一层
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
public EncodedResource(Resource resource) {
this(resource, null);
}
public EncodedResource(Resource resource, String encoding) {
Assert.notNull(resource, "Resource must not be null");
this.resource = resource;
this.encoding = encoding;
}
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
try {
// xml文件流
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
// do代表真正做事的方法
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
}
根据beans.xml获取InputStream且传递给doLoadBeanDefinitions。
这里说下spring中do开头的都是真正做事的方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {
try {
// 根据resource获取Document对象
Document doc = this.documentLoader.loadDocument(
inputSource, getEntityResolver(), this.errorHandler, validationMode, isNamespaceAware());
// 调用registerBeanDefinitions(doc, resource);
return registerBeanDefinitions(doc, resource);
}
}
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
// 创建BeanDefinitionDocumentReader对象
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
documentReader.setEnvironment(this.getEnvironment());
// 获取容器中已注册的bean的数量,只是统计用,我们这不关心
int countBefore = getRegistry().getBeanDefinitionCount();
// 核心来了,继续看
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
// 获取根节点(beans标签这种根元素)
Element root = doc.getDocumentElement();
// do开头的都是真正做事的方法,我看到do就兴奋。
doRegisterBeanDefinitions(root);
}
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
// 委托模式
this.delegate = createHelper(readerContext, root, parent);
// 模板设计模式,空方法,开发者可自定义实现
preProcessXml(root);
// 核心方法,让代理类BeanDefinitionParserDelegate负责将xml里的bean装配到BeanDefinition对象
parseBeanDefinitions(root, this.delegate);
// 模板设计模式,空方法,开发者可自定义实现
postProcessXml(root);
this.delegate = parent;
}
protected BeanDefinitionParserDelegate createHelper(XmlReaderContext readerContext, Element root, BeanDefinitionParserDelegate parentDelegate) {
// 创建BeanDefinitionParserDelegate,委托类。委托模式
BeanDefinitionParserDelegate delegate = new BeanDefinitionParserDelegate(readerContext, environment);
delegate.initDefaults(root, parentDelegate);
return delegate;
}
public void initDefaults(Element root, BeanDefinitionParserDelegate parent) {
// 设置默认节点属性,这个方法在BeanDefinitionParserDelegate类里,主要就是一些默认属性
// 比如 default-lazy-init等
populateDefaults(this.defaults, (parent != null ? parent.defaults : null), root);
this.readerContext.fireDefaultsRegistered(this.defaults);
}
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
// 这里有个细节,就是instanceof Element了,比如xml中的换行也算一个元素的,也会多for一次,但是他不属于Element,所以不会进入逻辑
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
// 核心
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
// import
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
// 负责解析import标签
importBeanDefinitionResource(ele);
}
// alias
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
// 负责解析alias标签
processAliasRegistration(ele);
}
// bean
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
// 负责解析bean标签,我这里主要分析这个
processBeanDefinition(ele, delegate);
}
// beans
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// 递归算法。重新走遍解析bean的整个流程doRegisterBeanDefinitions
doRegisterBeanDefinitions(ele);
}
}
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
}
}
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
return parseBeanDefinitionElement(ele, null);
}
// 大致上就是获取bean标签的id和name和alias和class属性,
// 放到BeanDefinition,且将放到BeanDefinition放到BeanDefinitionHolder
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, BeanDefinition containingBean) {
String id = ele.getAttribute(ID_ATTRIBUTE);
String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);
String beanName = id;
// 获取bean标签的class属性的value
AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
String[] aliasesArray = StringUtils.toStringArray(aliases);
return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
return null;
}
public AbstractBeanDefinition parseBeanDefinitionElement(
Element ele, String beanName, BeanDefinition containingBean) {
this.parseState.push(new BeanEntry(beanName));
String className = null;
// class属性
if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
}
try {
String parent = null;
// parent
if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
parent = ele.getAttribute(PARENT_ATTRIBUTE);
}
// 创建BeanDefinition对象
AbstractBeanDefinition bd = createBeanDefinition(className, parent);
// 将lazy-init等属性装配到BeanDefinition对象中
parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
parseMetaElements(ele, bd);
parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
parseConstructorArgElements(ele, bd);
parsePropertyElements(ele, bd);
parseQualifierElements(ele, bd);
bd.setResource(this.readerContext.getResource());
bd.setSource(extractSource(ele));
return bd;
}
}
// 创建BeanDefinition对象
public static AbstractBeanDefinition createBeanDefinition(
String parentName, String className, ClassLoader classLoader) throws ClassNotFoundException {
GenericBeanDefinition bd = new GenericBeanDefinition();
bd.setParentName(parentName);
if (className != null) {
// classLoader为null
if (classLoader != null) {
bd.setBeanClass(ClassUtils.forName(className, classLoader));
}
else {
bd.setBeanClassName(className);
}
}
return bd;
}
// 到这一步终于TMD返回了BeanDefinition对象,终于回到了起点方法processBeanDefinition(),那么下一步就是注册了。注册就简单太多了。
2.3、注册
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
// 加载的过程,我们已经在上面讲解了,这里返回了bean的定义,里面包含bean的全部信息
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
// 装饰者模式,这里不关心,用处不大。
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// 重点是这个,负责注册bean
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// 获取beanName,就是bean标签定义的id或name
String beanName = definitionHolder.getBeanName();
// 注册bean到ioc容器中
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// 获取别名
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String aliase : aliases) {
// 将别名与beanName绑定
registry.registerAlias(beanName, aliase);
}
}
}
// 简化版就是如下,就是直接往hashmap里扔了一下
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {
synchronized (this.beanDefinitionMap) {
this.beanDefinitionNames.add(beanName);
this.frozenBeanDefinitionNames = null;
// 放入ioc容器
this.beanDefinitionMap.put(beanName, beanDefinition);
}
}
3、小结
完结了,我在贴源码过程中删减了一些不必要的代码,只是贴了核心代码,可以发现我们只是将bean信息放到了ioc,并没有真正的实例化出来。实例化是DI的工作,IOC只是容器,DI后续文章会出。
三、总结
1.定义好Spring的配置文件
2.通过Resource对象将Spring配置文件进行抽象,抽象成一个Resource对象
3.定义好Bean工厂
4.定义好XmlBeanDefinitionReader对象,并将工厂作为参数传递进去供后续回调使用
5.通过XmlBeanDefinitionReader对象读取之前抽象出的Resource对象(包含了xml文件的解析过程)
6.本质上,xml文件的解析是由XmlBeanDefinitionReader对象交由BeanDefinitionParserDelegate委托完成的,实质上这里使用到了委托模式
7.解析完的xml文件信息都存放到BeanDefinition对象中,spring还采取了BeanDefinitionHolder来持有BeanDefinition对象的引用。
7.IOC容器创建完毕,用户可以通过容器获取到所需要的对象信息。(获取的时候其实就是DI依赖注入的过程,因为我们现在并没有实例化bean,只是将bean信息放到了ioc容器【map】中),key-》value 为 bean的name-》BeanDefinition
设计模式主要采取了工厂和委托。
四、建议
debug!debug!debug!按照我给的思路和流程和中文提示,自己写个helloworld去反复debug!
五、广告
QQ群:458430385
微信公众号