白话Spring源码(六):BeanDefinition的注册过程

上一篇博客讲了bean的创建过程。这次跟大家分享BeanDefinition的注册过程。

一、什么是BeanDefinition

BeanDefinition:就是bean的定义信息,比如bean的名称,对应的class,bean的属性值,bean是否是单列等等,一般是通过xml来定义的,如下所示:

	<bean id="test" class="org.springframework.beans.TestBean">
		<property name="name"><value>custom</value></property>
		<property name="age"><value>25</value></property>
	</bean>

二、怎么注册BeanDefinition

我们知道bean的实例是根据BeanDefinition的信息来创建的,而BeanDefinition是通过xml或者Properties形式的配置文件定义的,注册bean主要解决下面几个问题:

1.BeanDefinition定义的文件存在哪,怎么去加载?

2.怎么去解析xml或者其他格式的文件成BeanDefinition的实例?

3.BeanDefinition是怎么在工厂里存放和获取的?

下面我们来一条条解答:

1.BeanDefinition定义的文件存在哪,怎么去加载?

spring专门定义了InputStreamSource和Resource来解决这个问题

public interface InputStreamSource {

	/**
	 * 返回流
	 * @return
	 * @throws IOException
	 */
	InputStream getInputStream() throws IOException;

}

Resource接口继承InputStreamSource,做了扩展

public interface Resource extends InputStreamSource {

	/**
	 * Return whether this resource actually exists in physical form.
	 */
	boolean exists();

	/**
	 * Return whether this resource represents a handle with an open
	 * stream. If true, the InputStream cannot be read multiple times,
	 * and must be read and closed to avoid resource leaks.
	 * <p>Will be false for all usual resource descriptors.
	 */
	boolean isOpen();

	/**
	 * Return a URL handle for this resource.
	 * @throws IOException if the resource cannot be resolved as URL,
	 * i.e. if the resource is not available as descriptor
	 */
	URL getURL() throws IOException;

	/**
	 * Return a File handle for this resource.
	 * @throws IOException if the resource cannot be resolved as absolute
	 * file path, i.e. if the resource is not available in a file system
	 */
	File getFile() throws IOException;

	/**
	 * Return a description for this resource,
	 * to be used for error output when working with the resource.
	 * <p>Implementations are also encouraged to return this value
	 * from their toString method.
	 * @see java.lang.Object#toString
	 */
	String getDescription();

}

我们知道配置文件可能存在磁盘某个目录下,或者类classpath目录下,甚至来自网络。所以Resource有如下几个实现类:


ClassPathResource    通过类路径获取资源文件
FileSystemResource    通过文件系统获取资源
UrlResource    通过URL地址获取资源
ByteArrayResource    获取字节数组封装的资源
ServletContextResource    获取ServletContext环境下的资源
InputStreamResource    获取输入流封装的资源

2.怎么去解析xml或者其他格式的文件成BeanDefinition的实例?

一般bean是通过xml或者Properties配置文件类定义的,所以我们要解析xml或者Properties来生成bean的定义信息,Spring是通过AbstractBeanDefinitionReader的解析的,AbstractBeanDefinitionReader有两个实现类:

PropertiesBeanDefinitionReader 通过Properties配置文件方式解析

XmlBeanDefinitionReader 通过xml方式解析

3.BeanDefinition是怎么在工厂里存放和获取的?

首先我们看一下工厂的BeanDefinition注册的接口:

public interface BeanDefinitionRegistry {

	/**
	 * 获取bean定义的数量
	 * @return
	 */
	int getBeanDefinitionCount();

	/**
	 * 获取bean定义的名称
	 * @return
	 */
	String[] getBeanDefinitionNames();

	/**
	 * 通过名称判断是否包含bean的定义
	 * @param name
	 * @return
	 */
	boolean containsBeanDefinition(String name);

	/**
	 * 通过名称获取bean的定义
	 * @param name
	 * @return
	 * @throws BeansException
	 */
	BeanDefinition getBeanDefinition(String name) throws BeansException;

	/**
	 * 注册bean的定义
	 * @param name
	 * @param beanDefinition
	 * @throws BeansException
	 */
	void registerBeanDefinition(String name, BeanDefinition beanDefinition)
			throws BeansException;

	/**
	 * 果果名称获取别名
	 * @param name
	 * @return
	 * @throws NoSuchBeanDefinitionException
	 */
	String[] getAliases(String name) throws NoSuchBeanDefinitionException;

	/**
	 * 注册别名
	 * @param name
	 * @param alias
	 * @throws BeansException
	 */
	void registerAlias(String name, String alias) throws BeansException;

}

该接口的实现类是DefaultListableBeanFactory,DefaultListableBeanFactory里有个map专门存放BeanDefinition

	/** Map of bean definition objects, keyed by bean name */
	private Map beanDefinitionMap = new HashMap();

注册的过程其实就是讲BeanDefinition放到map里,name作为key,BeanDefinition是value。

XmlBeanFactory继承DefaultListableBeanFactory,注册通过xml的定义的bean

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 InputStream,
	 * which must be parsable using DOM.
	 * <p>It's preferable to use a Resource argument instead of an
	 * InputStream, to retain location information. This constructor
	 * is mainly kept for backward compatibility.
	 * @param is XML InputStream to load bean definitions from
	 * @throws BeansException in case of loading or parsing errors
	 * @see #XmlBeanFactory(Resource)
	 */
	public XmlBeanFactory(InputStream is) throws BeansException {
		this(new InputStreamResource(is, "(no description)"), 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);
	}

}

其实源码很简单,创建一个bean的解析器,这里当然是xml的解析器,Resource(获取xml配置文件)通过构造方法传入,调用reader.loadBeanDefinitions(resource)来注册BeanDefinition

三、设计理念:正交性

阅读源码我们不但要学习里面的逻辑和模式,更要学习里面的设计理念,这里先给大家介绍一个重要的设计理念:正交性。

“正交”在数学上指的是线性无关,最常见的例子就是坐标系下的x 轴和y轴,对于一个点来讲,它的x值的变化不会影响到y,  y值得变化不会影响到x ,即x和y是正交的。

正交的威力在于互不影响,扩展方便,单用一个坐标轴可以表示一个直线上的所有的点, 再加一个y 轴就能表示平面上的所有的点, 再加一个z轴 3维空间中的所有点都能表示出来了!

软件编程也是如此,无论你使用何种方法, 就是让程序形成互不影响、可以独立变化的多个维度, 这样的程序不仅优雅漂亮,更是容易理解,容易维护。

BeanDefinition的Resource和AbstractBeanDefinitionReader体现的正交性,bean定义获取和解析可以独立变化,相互不影响。

猜你喜欢

转载自blog.csdn.net/haoxin963/article/details/85057544