当前位置 博文首页 > 刘博仁:spring源码学习笔记之容器的基本实现(一)

    刘博仁:spring源码学习笔记之容器的基本实现(一)

    作者:刘博仁 时间:2021-02-05 12:27

    前言

    最近学习了<<Spring源码深度解析>>受益匪浅,本博客是对学习内容的一个总结、分享,方便日后自己复习或与一同学习的小伙伴一起探讨之用.

    建议与源码配合使用,效果更嘉,使用的spring版本为5.0.x: 官方源码下载 添加中文注解版源码

    下面正文开始.

    1. 容器的实现

    本文要分享的内容就是从读取配置文件到注册BeanDefinition的过程中spring到底做了怎样的处理.

    下面是一个具体的代码示例,首先创建一个对象

    public class MyTestBean {
        private String testStr = "testStr";
    
        public String getTestStr() {
            return testStr;
        }
    
        public void setTestStr(String testStr) {
            this.testStr = testStr;
        }
    }
    

    第二步创建配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    <bean  >
    
    </beans>
    

    第三步创建测试用例

    
    public class BeanFactoryTest (
        @Test
        public void testSirnpleLoad() {
          BeanFactory bf = new XmlBeanFactory (new ClassPathResource ("beanFactoryTest.xml"));
          MyTestBean bean=(MyTestBean) bf.getBean("myTestBean");
          System.out.println(bean.getTestStr());
          }
    }
    

    执行后会正确输出testStr.

    本篇博客的重点就是关注下列代码到底做了什么

    BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
    

    2. 源码分析

    BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
    

    第一步通过ClassPathResource将xml文件转换为Resource对象.

    new ClassPathResource("beanFactoryTest.xml")
    

    这里不再赘述,直接进入XmlBeanFactory这个类.

    public class XmlBeanFactory extends DefaultListableBeanFactory {
    
    	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    
    
    	/**
    	 * Create a new XmlBeanFactory with the given resource,
    	 * which must be parsable using DOM.
    	 * @param resource XML resource to load bean definitions from
    	 * @throws BeansException in case of loading or parsing errors
    	 */
    	public XmlBeanFactory(Resource resource) throws BeansException {
    		this(resource, null);
    	}
    
    	/**
    	 * Create a new XmlBeanFactory with the given input stream,
    	 * which must be parsable using DOM.
    	 * @param resource XML resource to load bean definitions from
    	 * @param parentBeanFactory parent bean factory
    	 * @throws BeansException in case of loading or parsing errors
    	 */
    	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    		super(parentBeanFactory);
    		this.reader.loadBeanDefinitions(resource);
    	}
    
    }
    

    这个类很简单继承了DefaultListableBeanFactory,有一个私有的成员变量XmlBeanDefinitionReader 和两个构造方法.

    可以看出XmlBeanFactory类本身并没有复写父类的方法,XmlBeanFactory与其父类的区别就在于使用私有成员变量XmlBeanDefinitionReader去读取资源文件.

    进入XmlBeanFactory的构造方法

    /**
    	 * Create a new XmlBeanFactory with the given input stream,
    	 * which must be parsable using DOM.
    	 * @param resource XML resource to load bean definitions from
    	 * @param parentBeanFactory parent bean factory
    	 * @throws BeansException in case of loading or parsing errors
    	 */
    	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    		super(parentBeanFactory);
    		this.reader.loadBeanDefinitions(resource);
    	}
    

    有一点需要关注一下,就是在执行this.reader.loadBeanDefinitions(resource)前先调用了super(parentBeanFactory);

    追踪这行代码进入了XmlBeanFactory的父类DefaultListableBeanFactory

    /**
    	 * Create a new DefaultListableBeanFactory with the given parent.
    	 * @param parentBeanFactory the parent BeanFactory
    	 */
    	public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    		super(parentBeanFactory);
    	}
    

    发现又是一个super(parentBeanFactory),继续追,又进入了DefaultListableBeanFactory的父类AbstractAutowireCapableBeanFactory

           /**
    	 * Create a new AbstractAutowireCapableBeanFactory with the given parent.
    	 * @param parentBeanFactory parent bean factory, or {@code null} if none
    	 */
    	public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    		this();
    		setParentBeanFactory(parentBeanFactory);
    	}
    
    

    现在追进去this()方法,我们需要关注的东西就是里面的ignoreDependencyInterface()方法

    /**
    	 * Create a new AbstractAutowireCapableBeanFactory.
    	 */
    	public AbstractAutowireCapableBeanFactory() {
    		super();
    		ignoreDependencyInterface(BeanNameAware.class);
    		ignoreDependencyInterface(BeanFactoryAware.class);
    		ignoreDependencyInterface(BeanClassLoaderAware.class);
    	}
    

    那么这个方法是做什么用的呢?

    2.1 ignoreDependencyInterface()

    首先先明确一下这个方法实现的功能是什么,然后我们再追踪一下代码

    2.1.1 ignoreDependencyInterface()实现的功能

    ignoreDependencyInterface的主要功能是忽略给定接口的自动装配功能.

    举例来说当A中有属性B.那么当Spring在获取A的Bean的时候如果其属性B还没有初始化,那么Spring 会自动初始化B,这也是Spring中提供的一个重要特性。但是,某些情况
    下,B不会被初始化,其中的一种情况就是B实现了BeanNameAware接口

    2.1.2 源码部分

    接下来看看源码部分是如何实现上述功能的

    实现分为三个步骤:

    1. 存入(将需要过滤的接口存入集合)
    2. 过滤Bean的所有属性(只要Bean的属性实现了第一步存入的接口进行剔除,返回一个过滤后的属性的集合)
    3. 对过滤后的属性进行初始化

    第一步存入源码,直接追踪ignoreDependencyInterface()

    
    /**
    	 * Dependency interfaces to ignore on dependency check and autowire, as Set of
    	 * Class objects. By default, only the BeanFactory interface is ignored.
    	 */
            // set 专门存入需要需要过滤的类
    	private final Set<Class<?>> ignoredDependencyInterfaces = new HashSet<>();
    
           /**
    	 * Ignore the given dependency interface for autowiring.
    	 * <p>This will typically be used by application contexts to register
    	 * dependencies that are resolved in other ways, like BeanFactory through
    	 * BeanFactoryAware or ApplicationContext through ApplicationContextAware.
    	 * <p>By default, only the BeanFactoryAware interface is ignored.
    	 * For further types to ignore, invoke this method for each type.
    	 * @see org.springframework.beans.factory.BeanFactoryAware
    	 * @see org.springframework.context.ApplicationContextAware
    	 */
    	public void ignoreDependencyInterface(Class<?> ifc) {
                    //放入集合
    		this.ignoredDependencyInterfaces.add(ifc);
    	}
    

    将传入的值方法一个set集合(对就是ignoredDependencyInterfaces这个集合).

    第二步过滤部分的源码

    	/**
    	 * Return an array of non-simple bean properties that are unsatisfied.
    	 * These are probably unsatisfied references to other beans in the
    	 * factory. Does not include simple properties like primitives or Strings.
    	 * @param mbd the merged bean definition the bean was created with
    	 * @param bw the BeanWrapper the bean was created with
    	 * @return an array of bean property names
    	 * @see org.springframework.beans.BeanUtils#isSimpleProperty
    	 */
    	protected String[] unsatisfiedNonSimpleProperties(AbstractBeanDefinition mbd, BeanWrapper bw) {
    		// 方法的第一个参数AbstractBeanDefinition mbd 可以理解为2.1.1举的例子类A
    
    		// 声明了一个集合
    		Set<String> result = new TreeSet<>();
    		// 拿到beanDefinition的所有属性
    		PropertyValues pvs = mbd.getPropertyValues();
    		// 得到属性的所有描述
    		PropertyDescriptor[] pds = bw.getPropertyDescriptors();
    		for (PropertyDescriptor pd : pds) {
    			// 这里遍历所有属性,可以理解为2.1.1举的例子属性B
                            // 过滤属性的方法在!isExcludedFromDependencyCheck(pd)
    			if (pd.getWriteMethod() != null && !isExcludedFromDependencyCheck(pd) && !pvs.contains(pd.getName()) &&
    					!BeanUtils.isSimpleProperty(pd.getPropertyType())) {
    				result.add(pd.getName());
    			}
    		}
    		// 最后返回过滤后的属性
    		return StringUtils.toStringArray(result);
    	}
    

    通过源码我们知道了具体过滤的逻辑 !isExcludedFromDependencyCheck(pd) 在这个方法中,继续追踪

    /**
    	 * Determine whether the given bean property is excluded from dependency checks.
    	 * <p>This implementation excludes properties defined by CGLIB and
    	 * properties whose type matches an ignored dependency type or which
    	 * are defined by an ignored dependency interface.
    	 * @param pd the PropertyDescriptor of the bean property
    	 * @return whether the bean property is excluded
    	 * @see #ignoreDependencyType(Class)
    	 * @see #ignoreDependencyInterface(Class)
    	 */
    	protected boolean isExcludedFromDependencyCheck(PropertyDescriptor pd) {
    		return (AutowireUtils.isExcludedFromDependencyCheck(pd) ||
    				this.ignoredDependencyTypes.contains(pd.getPropertyType()) ||
                                    // 这里使用了第一步存入的集合
    				AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces));
    	}
    

    看条件 AutowireUtils.isSetterDefinedInInterface(pd, this.ignoredDependencyInterfaces),这里使用了我们第一步存入的集合,接近真相了,继续追踪

    /**
    	 * Return whether the setter method of the given bean property is defined
    	 * in any of the given interfaces.
    	 * @param pd the PropertyDescriptor of the bean property  pd就是某个对象的一个属性
    	 * @param interfaces the Set of interfaces (Class objects)
    	 * @return whether the setter method is defined by an interface
    	 */
    	public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> interfaces) {
    		// 这里得到B的set方法
    		Method setter = pd.getWriteMethod();
    		// 如果set方法不为空
    		if (setter != null) {
    			/*
    			* getDeclaringClass
    			* 该方法返回一个Class对象,返回当前class对象的声明对象class,一般针对内部类的情况,比如A类有内部类B,那么通过B.class.getDeclaringClass()方法将获取到A的Class对象.
    			* 在使用反射对象时比如Method和Field的getDeclaringClass方法将获取到所属类对象
    			* */
    			// 所以这个targetClass 为所属类对象
    			// 这里得到了属性B的class
    			Class<?> targetClass = setter.getDeclaringClass();
    			for (Class<?> ifc : interfaces) {
    				// 判断 ifc 是否是属性B的父类(这里的ifc 则是第一步存入的接口)
    				// 如果 属性B继承了第一步存入的接口  并且 存入的接口也有相同的set方法,就会被过滤
    				if (ifc.isAssignableFrom(targetClass) &&
    						// 判断类是否有指定的public方法;
    						ClassUtils.hasMethod(ifc, setter.getName(), setter.getParameterTypes())) {
    					return true;
    				}
    			}
    		}
    		return false;
    	}
    

    看到上面的代码我们明白了过滤属性的步骤:

    1. 得到属性B的class
    2. 遍历第一步存入的接口的集合
    3. 如果属性B继承了第一步存入的接口,并且存入的接口也有相同的set方法,就会被过滤
      第二部分忽略的逻辑就完了

    最后是第三部分使用,使用过滤后的属性进行初始化

    protected void autowireByType(
    			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    
    		TypeConverter converter = getCustomTypeConverter();
    		if (converter == null) {
    			converter = bw;
    		}
    
    		Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
                    // 过滤后的属性是在这里使用的
    		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    		for (String propertyName : propertyNames) {
    			try {
    				PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
    				// Don't try autowiring by type for type Object: never makes sense,
    				// even if it technically is a unsatisfied, non-simple property.
    				if (Object.class != pd.getPropertyType()) {
    					MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
    					// Do not allow eager init for type matching in case of a prioritized post-processor.
    					boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
    					DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
    					Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
    					if (autowiredArgument != null) {
    						pvs.add(propertyName, autowiredArgument);
    					}
    					for (String autowiredBeanName : autowiredBeanNames) {
    						registerDependentBean(autowiredBeanName, beanName);
    						if (logger.isDebugEnabled()) {
    							logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
    									propertyName + "' to bean named '" + autowiredBeanName + "'");
    						}
    					}
    					autowiredBeanNames.clear();
    				}
    			}
    			catch (BeansException ex) {
    				throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
    			}
    		}
    	}
    
    protected void autowireByName(
    			String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
                    // 过滤后的属性是在这里使用的
    		String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    		for (String propertyName : propertyNames) {
    			if (containsBean(propertyName)) {
    				Object bean = getBean(propertyName);
    				pvs.add(propertyName, bean);
    				registerDependentBean(propertyName, beanName);
    				if (logger.isDebugEnabled()) {
    					logger.debug("Added autowiring by name from bean name '" + beanName +
    							"' via property '" + propertyName + "' to bean named '" + propertyName + "'");
    				}
    			}
    			else {
    				if (logger.isTraceEnabled()) {
    					logger.trace("Not autowiring property '" + propertyName + "' of bean '" + beanName +
    							"' by name: no matching bean found");
    				}
    			}
    		}
    	}
    

    第二步过滤后的属性是用来注入属性的无论是通过name还是通过type注入. 经过一系列的追踪我们证实了2.1.1节的功能实现.

    2.2 XmlBeanDefinitionReader

    构造方法解释完毕了,进入正题,xmlBeanFactory使用私有成员变量XmlBeanDefinitionReader去加载资源

     /**
    	 * Create a new XmlBeanFactory with the given input stream,
    	 * which must be parsable using DOM.
    	 * @param resource XML resource to load bean definitions from
    	 * @param parentBeanFactory parent bean factory
    	 * @throws BeansException in case of loading or parsing errors
    	 */
    	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    		super(parentBeanFactory);
    		this.reader.loadBeanDefinitions(resource);
    	}
    

    进入this.reader.loadBeanDefinitions(),发现创建了一个EncodedResource对象,传入loadBeanDefinitions()方法

    /**
    	 * Load bean definitions from the specified XML file.
    	 * @param resource the resource descriptor for the XML file
    	 * @return the number of bean definitions found
    	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
    	 */
    	@Override
    	public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
    		return loadBeanDefinitions(new EncodedResource(resource));
    	}
    

    进入loadBeanDefinitions(new EncodedResource(resource))方法

    	/**
    	 * Load bean definitions from the specified XML file.
    	 * @param encodedResource the resource descriptor for the XML file,
    	 * allowing to specify an encoding to use for parsing the file
    	 * @return the number of bean definitions found
    	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
    	 */
    	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);
    		}
    
    		// 获得当前正在加载的资源
    		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());
    				}
    				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();
    			}
    		}
    	}
    

    这个方法主要做了两个操作: 1. 检测资源是否循环加载,如果是则抛出异常 2. 对inputSource设置编码.

    继续往下追踪,进入doLoadBeanDefinitions(inputSource, encodedResource.getResource())方法.

    /**
    	 * Actually load bean definitions from the specified XML file.
    	 * @param inputSource the SAX InputSource to read from
    	 * @param resource the resource descriptor for the XML file
    	 * @return the number of bean definitions found
    	 * @throws BeanDefinitionStoreException in case of loading or parsing errors
    	 * @see #doLoadDocument
    	 * @see #registerBeanDefinitions
    	 */
    	protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
    			throws BeanDefinitionStoreException {
    		try {
                            // 通过inputSource, resource获得文档对象
    			Document doc = doLoadDocument(inputSource, resource);
    			// 获取到document后注册BeanDefinition
    			return registerBeanDefinitions(doc, resource);
    		}
    		catch (BeanDefinitionStoreException ex) {
    			throw ex;
    		}
    		catch (SAXParseException ex) {
    			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    					"Line " + ex.getLineNumber() + " in XML document from " + resource + " is invalid", ex);
    		}
    		catch (SAXException ex) {
    			throw new XmlBeanDefinitionStoreException(resource.getDescription(),
    					"XML document from " + resource + " is invalid", ex);
    		}
    		catch (ParserConfigurationException ex) {
    			throw new BeanDefinitionStoreException(resource.getDescription(),
    					"Parser configuration exception parsing XML from " + resource, ex);
    		}
    		catch (IOException ex) {
    			throw new BeanDefinitionStoreException(resource.getDescription(),
    					"IOException parsing XML document from " + resource, ex);
    		}
    		catch (Throwable ex) {
    			throw new BeanDefinitionStoreException(resource.getDescription(),
    					"Unexpected exception parsing XML document from " + resource, ex);
    		}
    	}
    

    doLoadBeanDefinitions(InputSource inputSource, Resource resource)方法通过inputSource、 resource获得了Document这个方法就不展开解释了,继续往下追踪进入了registerBeanDefinitions(doc, resource)

    /**
    	 * Register the bean definitions contained in the given DOM document.
    	 * Called by {@code loadBeanDefinitions}.
    	 * <p>Creates a new instance of the parser class and invokes
    	 * {@code registerBeanDefinitions} on it.
    	 * @param doc the DOM document
    	 * @param resource the resource descriptor (for context information)
    	 * @return the number of bean definitions found
    	 * @throws BeanDefinitionStoreException in case of parsing errors
    	 * @see #loadBeanDefinitions
    	 * @see #setDocumentReaderClass
    	 * @see BeanDefinitionDocumentReader#registerBeanDefinitions
    	 */
    	public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
                    //1. 生成BeanDefinitionDocumentReader
    		BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
    		// 返回注册列表中注册的bean的数量
    		int countBefore = getRegistry().getBeanDefinitionCount();
                    //2. 通过生成的BeanDefinitionDocumentReader注册beanDefinition
    		documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    		// 这里返回的是新注册的bean的数量
                    //3. 返回最新的注册的bean的数量
    		return getRegistry().getBeanDefinitionCount() - countBefore;
    	}
    

    registerBeanDefinitions(Document doc, Resource resource)主要做了三件事:

    1. 生成BeanDefinitionDocumentReader
    private Class<? extends BeanDefinitionDocumentReader> documentReaderClass =
    			DefaultBeanDefinitionDocumentReader.class;
    
    
    /**
    	 * Create the {@link BeanDefinitionDocumentReader} to use for actually
    	 * reading bean definitions from an XML document.
    	 * <p>The default implementation instantiates the specified "documentReaderClass".
    	 * @see #setDocumentReaderClass
    	 */
    	protected BeanDefinitionDocumentReader createBeanDefinitionDocumentReader() {
    		return BeanUtils.instantiateClass(this.documentReaderClass);
    	}
    

    可以看出这里生成的BeanDefinitionDocumentReader实际上是DefaultBeanDefinitionDocumentReader类型的

    1. 通过生成的BeanDefinitionDocumentReader注册beanDefinition(也就是我们下一节要分析的主角)
    documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
    

    这里要留意一下createReaderContext(resource)方法

    /**
    	 * Create the {@link XmlReaderContext} to pass over to the document reader.
    	 */
    	public XmlReaderContext createReaderContext(Resource resource) {
    		return new XmlReaderContext(resource, this.problemReporter, this.eventListener,
    				this.sourceExtractor, this, getNamespaceHandlerResolver());
    	}
    

    注意第五个参数this,相当于把自己(XmlBeanDefinitionReader)也传递给了BeanDefinitionDocumentReader,方便后续调用

    1. 返回最新的注册的bean的数量
    return getRegistry().getBeanDefinitionCount() - countBefore;
    

    这里的getRegistry()是哪里来的,当时有点懵,找了一会才明白... 是这么来的

    public class XmlBeanFactory extends DefaultListableBeanFactory {
            // 看XmlBeanDefinitionReader构造方法的参数this
    	private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
    
    
    	/**
    	 * Create a new XmlBeanFactory with the given resource,
    	 * which must be parsable using DOM.
    	 * @param resource XML resource to load bean definitions from
    	 * @throws BeansException in case of loading or parsing errors
    	 */
    	public XmlBeanFactory(Resource resource) throws BeansException {
    		this(resource, null);
    	}
    
    	/**
    	 * Create a new XmlBeanFactory with the given input stream,
    	 * which must be parsable using DOM.
    	 * @param resource XML resource to load bean definitions from
    	 * @param parentBeanFactory parent bean factory
    	 * @throws BeansException in case of loading or parsing errors
    	 */
    	public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
    		super(parentBeanFactory);
    		this.reader.loadBeanDefinitions(resource);
    	}
    
    }
    

    嗯... 答案就在XmlBeanDefinitionReader的构造方法中

    	/**
    	 * Create new XmlBeanDefinitionReader for the given bean factory.
    	 * @param registry the BeanFactory to load bean definitions into,
    	 * in the form of a BeanDefinitionRegistry
    	 */
    	public XmlBeanDefinitionReader(BeanDefinitionRegistry registry) {
    		super(registry);
    	}
    

    这里划重点,因为后续得到的BeanDefinition还需要使用registry(实际上是XmlBeanFactory)去注册BeanDefinition

    XmlBeanDefinitionReader实现的功能到这里告一段落了, 进入下一个类DefaultBeanDefinitionDocumentReader

    2.3 DefaultBeanDefinitionDocumentReader

    /**
    	 * This implementation parses bean definitions according to the "spring-beans" XSD
    	 * (or DTD, historically).
    	 * <p>Opens a DOM Document; then initializes the default settings
    	 * specified at the {@code <beans/>} level; then parses the contained bean definitions.
    	 */
    	@Override
    	public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
    		this.readerContext = readerContext;
    		logger.debug("Loading bean definitions");
    		Element root = doc.getDocumentElement();
    		doRegisterBeanDefinitions(root);
    	}
    

    这个很简单没什么说的,通过document得到了Element,然后调用doRegisterBeanDefinitions(root)

    
    /**
    	 * Register each bean definition within the given root {@code <beans/>} element.
    	 */
    	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 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);
                            // 主要逻辑就是判断这个配置文件是否是允许存在,不存在则 return,不再浪费时间解析了
    				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);
    		/*解析bean的定义*/
    		parseBeanDefinitions(root, this.delegate);
    		/*解析后处理,该方法为空实现*/
    		postProcessXml(root);
    
    		this.delegate = parent;
    	}
    
    

    上述代码做了三件事:

    1. 方法中生成了一个BeanDefinitionParserDelegate,这个delegate有大用处,主要用来将Element解析成BeanDefinition(什么是BeanDefinition? 可以理解为将xml中的 bean标签 解析成一个对象,bean标签的属性与BeanDefinition的属性一一对应).
    2. 对配置文件的profile属性的校验
      举个例子,有多个profile(多个环境)的情况,如果 param-value标签和profile的值能对应的情况才往下解析,否则就return了.
    <!-- 开发环境配置文件 -->
      <beans profile="development">
        <context:property-placeholder location="/WEB-INF/test-orm.properties" />
      </beans>
     
      <!-- 本地环境配置文件 -->
      <beans profile="test">
        <context:property-placeholder location="/WEB-INF/local-orm.properties" />
      </beans>
    
    
    <!--在web.xml中添加-->
    <context-param>
       <param-name>spring.profiles.default</param-name>
       <param-value>development</param-value>
    </context-param>
    
    1. 有三个方法解析前处理、解析bean的定义、解析后处理,其中解析前处理和解析后处理两个方法为空实现,如果需要做一些特殊的操作可以自行实现.

    重点关注解析bean的定义parseBeanDefinitions(root, this.delegate)方法

    /**
    	 * Parse the elements at the root level in the document:
    	 * "import", "alias", "bean".
    	 *
    	 * @param root the DOM root element of the document
    	 */
    	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);
    				if (node instanceof Element) {
    					Element ele = (Element) node;
    					if (delegate.isDefaultNamespace(ele)) {
    						// 解析默认标签
    						parseDefaultElement(ele, delegate);
    					} else {
    						// 解析自定义标签
    						delegate.parseCustomElement(ele);
    					}
    				}
    			}
    		} else {
    			delegate.parseCustomElement(root);
    		}
    	}
    

    进入解析bean的定义parseBeanDefinitions(root, this.delegate)方法,可以看到这里进行了一个判断,判断这个element究竟是自定义标签还是默认标签,并且如果是默认标签的情况还会进行遍历,再次判断是自定义标签还是默认标签,说明了默认标签中有可能包含自定义标签.

    那什么是默认标签? 什么是自定义标签?

    默认标签是spring自己定义的,比如:

    <bean  >
    

    自定义标签:

    <tx:annotation-driven>
    

    这两种标签的解析方式有很大的不同. 我们先追踪解析默认标签的方法

    	private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
                     // import 标签
    		if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
    			importBeanDefinitionResource(ele);
    		} 
                      // alias标签
                      else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
    			processAliasRegistration(ele);
    		} 
                      //bean标签
                      else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
    			processBeanDefinition(ele, delegate);
    		} 
                      //beans标签
                      else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
    			// recurse
    			doRegisterBeanDefinitions(ele);
    		}
    	}
    

    可以看到有四种默认标签,分别是: import标签(导入其他配置文件)、alias标签(设置别名)、bean标签、beans标签(嵌套的bean). 限于篇幅及重要程度,我们重点关注processBeanDefinition(ele, delegate)方法,理解了bean标签的解析相信其他三类标签也可以触类旁通.

    进入processBeanDefinition(ele, delegate)方法

    /**
    	 * Process the given bean element, parsing the bean definition
    	 * and registering it with the registry.
    	 */
    	protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    		/*
    		* 首先委托BeanDefinitionDelegate 类的parseBeanDefinitionElement 方法进行元素解析,
    			返回BeanDefinitionHolder 类型的实例bdHolder ,经过这个方法后,bdHolder 实例已经包含我
    			们配置文件中配置的各种属性了,例如class 、name、id 、alias 之类的属性
    		* */
    		BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    		if (bdHolder != null) {
    			/*
    			* 当返回的bdHolder 不为空的情况下若存在默认标签的子节点下再有自定义属性,还需要
    				再次对自定义标签进行解析
    			* */
    			bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
    			try {
    				// Register the final decorated instance.
    				/*
    				* 解析完成后,需要对解析后的bdHolder 进行注册,同样,注册操作委托给了
    				* BeanDefinitionReaderUtils的registerBeanDefinition方法
    				* */
    				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));
    		}
    	}
    
    
    下一篇:没有了