Spring 5.x源码剖析-Beandefinition是如何创建的?

源码阅读传送门

  1. Spring IOC容器初始化源码剖析(一)

前言

上一章我们了解了IOC容器的相关概念、如何通过代码实例化容器以及容器初始化的核心方法refresh(),本章开始初探refresh(),在阅读源码前,我们有必要先了解什么是BeanFactory以及BeanDefinition,这将有利于我们对接来下源码的理解。

BeanFactory

BeanFactory顾名思义,Bean的工厂,用于创建与管理Bean。

关系图

从上图我们可以看到ApplicationContext继承了两个BeanFactory的子接口:

  • ListableBeanFactory:listable(可列举的),BeanFactory只能获取单个Bean,ListableBeanFactory可以获取多个Bean。
  • HierarchicalBeanFactory:hierarchical(分层),可以通过配置的方式设置父类的bean工厂。

BeanDefinition

Spring IOC容器管理一个或多个Bean,这些bean使用提供给容器的配置元数据创建的(例如通过xml定义的形式)。在容器本身,这些Bean表示为BeanDefinition对象。

BeanDefinition主要包含哪些信息呢?

  1. 包限定名,通常是定义bean的具体实现类。
  2. bean行为配置元素,比如:范围、生命周期回调等。
  3. 对bean执行工作所需的其它bean引用(依赖)。
  4. 要在新创建对象中设置的其它配置,比如数据库连接池bean的连接数大小等。

源码如下:

/**
 * BeanDefinition用于描述bean实例具有的属性值、构造函数参数值和具体实现等
 */
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {

	/**
	 * 单例作用域的作用域标识符
	 */
	String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;

	/**
	 * 原型范围的范围标识符
	 */
	String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;

	/**
	 * 角色分类-应用
	 */
	int ROLE_APPLICATION = 0;

	/**
	 * 角色分类-支持
	 */
	int ROLE_SUPPORT = 1;

	/**
	 * 角色分类-后台
	 */
	int ROLE_INFRASTRUCTURE = 2;
	
	// Modifiable attributes

	/**
	 * 设置该bean的父名称
	 */
	void setParentName(@Nullable String parentName);

	/**
	 * 获取···
	 */
	@Nullable
	String getParentName();

	/**
	 * 为该bean指定bean类名
	 */
	void setBeanClassName(@Nullable String beanClassName);

	/**
	 * 获取···
	 */
	@Nullable
	String getBeanClassName();

	/**
	 * 设置作用域
	 * @see #SCOPE_SINGLETON
	 * @see #SCOPE_PROTOTYPE
	 */
	void setScope(@Nullable String scope);

	/**
	 * 获取作用域
	 */
	@Nullable
	String getScope();

	/**
	 * 设置是否懒加载
	 */
	void setLazyInit(boolean lazyInit);

	/**
	 * 判断是否懒加载
	 */
	boolean isLazyInit();

	/**
	 * 设置此bean初始化所依赖的bean名称,
	 * bean工厂将保证首先初始化这些bean
	 */
	void setDependsOn(@Nullable String... dependsOn);

	/**
	 * 获取依赖的bean名称数组
	 */
	@Nullable
	String[] getDependsOn();

	/**
	 * 设置该bean是否可以自动注入
	 */
	void setAutowireCandidate(boolean autowireCandidate);

	/**
	 * 返回···
	 */
	boolean isAutowireCandidate();

	/**
	 * 设置该bean是否为主要候选bean
	 */
	void setPrimary(boolean primary);

	/**
	 * 返回···
	 */
	boolean isPrimary();

	/**
	 * 设置FactoryBean名称
	 */
	void setFactoryBeanName(@Nullable String factoryBeanName);

	/**
	 * 返回···
	 */
	@Nullable
	String getFactoryBeanName();

	/**
	 * 设置工厂方法名称,
	 * 如果有工厂方法,此方法将使用构造函数参数调用,如果没有指定参数则不使用。
	 */
	void setFactoryMethodName(@Nullable String factoryMethodName);

	/**
	 * 返回···
	 */
	@Nullable
	String getFactoryMethodName();

	/**
	 * 设置该bean指定构造函数参数值。
	 */
	ConstructorArgumentValues getConstructorArgumentValues();

	/**
	 * 返回···
	 */
	default boolean hasConstructorArgumentValues() {
		return !getConstructorArgumentValues().isEmpty();
	}

	/**
	 * 获取要应用于bean实例的属性值。
	 */
	MutablePropertyValues getPropertyValues();

	/**
	 * 返回···
	 */
	default boolean hasPropertyValues() {
		return !getPropertyValues().isEmpty();
	}

	/**
	 * 设置初始化方法
	 */
	void setInitMethodName(@Nullable String initMethodName);

	/**
	 * 返回···
	 */
	@Nullable
	String getInitMethodName();

	/**
	 * 设置销毁方法
	 */
	void setDestroyMethodName(@Nullable String destroyMethodName);

	/**
	 * 返回···
	 */
	@Nullable
	String getDestroyMethodName();

	/**
	 * 设置角色
	 */
	void setRole(int role);

	/**
	 * 获取···
	 */
	int getRole();

	/**
	 * 设置bean描述
	 */
	void setDescription(@Nullable String description);

	/**
	 * 返回bean描述
	 */
	@Nullable
	String getDescription();


	// Read-only attributes

	/**
	 * 返回此bean定义的可解析类型。
	 */
	ResolvableType getResolvableType();

	/**
	 * 是否单例
	 */
	boolean isSingleton();

	/**
	 * 是否原型
	 */
	boolean isPrototype();

	/**
	 * 是否抽象
	 */
	boolean isAbstract();

	/**
	 * 返回此bean定义来自的资源的描述
	 */
	@Nullable
	String getResourceDescription();

	/**
	 * 返回原始bean定义,如果没有则为空。
	 */
	@Nullable
	BeanDefinition getOriginatingBeanDefinition();

}

1. prepareRefresh()

prepareRefresh()refresh()的第一个方法,prepareRefresh()主要用于设置启动开始时间、活动标志位、初始化环境配置参数、校验参数等,来为初始化做好准备。

// AbstractApplicationContext#prepareRefresh()
protected void prepareRefresh() {
	// 记录当前时间戳,切换活动状态
	this.startupDate = System.currentTimeMillis();
	this.closed.set(false);
	this.active.set(true);

	// 部分省略日志打印

	// 初始化环境配置参数
	// 当前类方法本身do nothing,
	// 通过重写初始化环境属性信息。
	initPropertySources();

	// 获取initPropertySources中初始化的环境配置参数,
	// 来进行验证标记为required的属性
	getEnvironment().validateRequiredProperties();

	// 载入预刷新应用程序监听
	if (this.earlyApplicationListeners == null) {
		this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
	}
	else {
		// 将本地应用程序监听器重置为预刷新状态。
		this.applicationListeners.clear();
		this.applicationListeners.addAll(this.earlyApplicationListeners);
	}

	// 允许早期应用事件收集,
	// 当multicaster可用就会发布
	this.earlyApplicationEvents = new LinkedHashSet<>();
}
// AbstractApplicationContext#initPropertySources
protected void initPropertySources() {
	// For subclasses: do nothing by default.
}
// AbstractPropertyResolver#validateRequiredProperties
@Override
public void validateRequiredProperties() {
	MissingRequiredPropertiesException ex = new MissingRequiredPropertiesException();
	for (String key : this.requiredProperties) {
		// 判断必需的环境配置参数是否为空
		if (this.getProperty(key) == null) {
			ex.addMissingRequiredProperty(key);
		}
	}
	if (!ex.getMissingRequiredProperties().isEmpty()) {
		throw ex;
	}
}

prepareRefresh()大致流程如下:

  1. 记录当前时间戳并设置活动状态。
  2. 初始环境配置参数,校验必需的配置参数。(校验未通过则抛出MissingRequiredPropertiesException: The following properties were declared as required but could not be resolved: [缺失的配置参数数组]
  3. 初始化或清除并载入应用程序监听(ApplicationListener)。
  4. 初始化事件(ApplicationEvent)。

2. obtainFreshBeanFactory()

obtain(获得)fresh(最新的)BeanFactory,该方法中会解析配置元数据,生成BeanDefinition,并注册到BeanFactory中。

该方法链路比较长,最好结合源码一起看,接下来进入正题。

//AbstractApplicationContext#ConfigurableListableBeanFactory()
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
	// 配置加载
	refreshBeanFactory();
	// 获取BeanFactory并返回
	return getBeanFactory();
}
// AbstractRefreshableApplicationContext#refreshBeanFactory()
@Override
protected final void refreshBeanFactory() throws BeansException {
	// 判断BeanFactory是否已经创建
	if (hasBeanFactory()) {
		// 销毁所有已创建的bean
		destroyBeans();
		// 将BeanFactory置空
		closeBeanFactory();
	}
	try {
		// 默认实现创建DefaultListableBeanFactory,
		// 并将容器的父级内部bean工厂作为父bean工厂
		DefaultListableBeanFactory beanFactory = createBeanFactory();
		beanFactory.setSerializationId(getId());
		// 1.设置是否允许覆盖同名bean定义,自动替换前一个定义。
		// 2.设置是否允许bean之间循环引用。
		customizeBeanFactory(beanFactory);
		// 加载配置转换为BeanDefinition注册到beanFactory
		loadBeanDefinitions(beanFactory);
		this.beanFactory = beanFactory;
	}
	catch (IOException ex) {
		throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
	}
}

通过如上refreshBeanFactory()源码,我们可以了解到大致流程:

  1. 判断BeanFactory是否已经创建,若已经创建则销毁所有已创建的bean、并把beanFactory置空。

    // AbstractRefreshableApplicationContext#hasBeanFactory()
    protected final boolean hasBeanFactory() {
    	return (this.beanFactory != null);
    }
    
  2. 创建DefaultListableBeanFactory(该类为BeanFactory的一个默认实现),并将其设置为容器的parentBeanFactory
    继承关系

  3. customizeBeanFactory()设置是否允许覆盖bean定义及bean之间的循环引用。

    // AbstractRefreshableApplicationContext#customizeBeanFactory
    protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    	// 设置是否允许同名bean定义覆盖,true:自动替换前一个定义。
    	if (this.allowBeanDefinitionOverriding != null) {
    		beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    	}
    	// 设置是否允许bean之间循环引用。
    	if (this.allowCircularReferences != null) {
    		beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    	}
    }
    
  4. loadBeanDefinitions(beanFactory)读取配置元数据转换为BeanDefinition注册到beanFactory

IOC容器管理了一个或多个Bean。在容器本身,这些bean表示为BeanDefinition对象,容器启动过程中,会将Bean解析为BeanDefinition注册到beanFactory中。

loadBeanDefinitions(beanFactory)

开始套娃···

// AbstractXmlApplicationContext#loadBeanDefinitions
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
	XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
	// 配置beanDefinitionReader与context在同一环境中
	beanDefinitionReader.setEnvironment(this.getEnvironment());
	// 使用当前上下文对象作为beanDefinitionReader的ResourceLoader
	beanDefinitionReader.setResourceLoader(this);
	// 设置用于解析的SAX实体解析器,比如解析dtd、xsd文件
	beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
	// 初始化beanDefinitionReader
	// 默认开启XML验证
	initBeanDefinitionReader(beanDefinitionReader);
	// 通过beanDefinitionReader加载BeanDefinition
	// 加载配置转换为BeanDefinition注册到beanFactory
	loadBeanDefinitions(beanDefinitionReader);
}

loadBeanDefinitions(beanFactory)大致步骤:

  1. 实例化XmlBeanDefinitionReader(读取通过Xml文件中配置Bean定义的工具类)、属性配置。
  2. 加载配置转换为BeanDefinition注册到beanFactory。

loadBeanDefinitions(beanDefinitionReader)

// AbstractXmlApplicationContext#loadBeanDefinitions
protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
	// 可以通过子类重写getConfigResources的方式指定配置文件数组,
	// getConfigResources()默认返回null
	Resource[] configResources = getConfigResources();
	if (configResources != null) {
		// 从指定配置资源中加载Bean
		reader.loadBeanDefinitions(configResources);
	}
	// 获取本地配置文件
	String[] configLocations = getConfigLocations();
	if (configLocations != null) {
		// 从指定配置文件中加载Bean
		reader.loadBeanDefinitions(configLocations);
	}
}

loadBeanDefinitions(beanDefinitionReader)大致步骤如下:

  1. 在子类重写getConfigResources()方法的前提下,通过重写方法指定逻辑获取配置文件数组(默认返回null),从指定配置文件中加载Bean。
    // AbstractXmlApplicationContext#getConfigResources
    @Nullable
    protected Resource[] getConfigResources() {
    	return null;
    }
    
  2. 获取本地配置文件,从指定配置文件中解析并加载Bean。

reader.loadBeanDefinitions(configLocations)

// AbstractBeanDefinitionReader#loadBeanDefinitions
// 返回所有注册BeanDefinition的个数
@Override
public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException {
	Assert.notNull(locations, "Location array must not be null");
	int count = 0;
	for (String location : locations) {
		count += loadBeanDefinitions(location);
	}
	return count;
}

继续朝下追踪loadBeanDefinitions(location)的内部处理逻辑。

loadBeanDefinitions(location)

// AbstractBeanDefinitionReader#loadBeanDefinitions
@Override
public int loadBeanDefinitions(String location) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(location, null);
}
// AbstractBeanDefinitionReader#loadBeanDefinitions
 /**
  * 读取配置转换为BeanDefinition并注册到beanFactory
  * @param location			本地资源文件路径
  * @param actualResources	解析过程中的实际资源集合
  */
public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException {
	// 获取资源加载器
	ResourceLoader resourceLoader = getResourceLoader();
	// 部分省略
	if (resourceLoader instanceof ResourcePatternResolver) {
		try {
			// 使用资源解析器根据资源文件路径解析并加载
			Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location);
			// 解析当前资源中的bean定义,
			// 并返回注册BeanDefinition个数
			int count = loadBeanDefinitions(resources);
			// 若actualResources不为null,则把文件资源添加到集合中
			if (actualResources != null) {
				Collections.addAll(actualResources, resources);
			}
			// 部分省略
			// 返回注册个数
			return count;
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException("Could not resolve bean definition resource pattern [" + location + "]", ex);
		}
	}
	else {
		Resource resource = resourceLoader.getResource(location);
		int count = loadBeanDefinitions(resource);
		if (actualResources != null) {
			actualResources.add(resource);
		}
		// 部分省略
		return count;
	}
}

loadBeanDefinitions(location, null)大致流程如下:

  1. getResourceLoader()获得资源加载器。

    @Override
    @Nullable
    public ResourceLoader getResourceLoader() {
    	// this.resourceLoader即AbstractXmlApplicationContext。
    	// 前面源码中有讲解,
    	// 没有印象的可以再去回顾AbstractXmlApplicationContext#loadBeanDefinitions
    	return this.resourceLoader;
    }
    
  2. loader instanceof ResourcePatternResolver = true
    在这里插入图片描述

  3. 通过资源解析器根据资源文件路径解析并加载(该节点以下的源码,感兴趣可以看看,不感兴趣可以直接跳过)。

    // PathMatchingResourcePatternResolver#getResources
    // 根据指定配置文件路径获取文件资源数组
    @Override
    public Resource[] getResources(String locationPattern) throws IOException {
    	// 部分省略
    	// 判断文件路径是否以‘classpath*’开头
    	// CLASSPATH_ALL_URL_PREFIX:classpath*:
    	// “classpath*:”代表:该工程有多个classpath,同时寻找并加载多个classpath路径下的文件
    	// “classpath:” 代表: 与上反之
    	if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
    		// 判断截取“classpath*:”后的文件路径是否包含“*”或“?”或“{”或“}”
    		// 例如:配置为:“classpath*:config/*Config.xml”,则会寻找所有classpath路径config文件夹下所有以“Config.xml”为后缀的文件
    		if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
    			// 根据路径查找匹配资源文件
    			return findPathMatchingResources(locationPattern);
    		}
    		else {
    			// 若未包含以上字符,则寻找并加载所有classpath路径下的指定名称的配置文件。
    			return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
    		}
    	}
    	else {
    		int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
    				locationPattern.indexOf(':') + 1);
    		// 判断是否包含特殊字符
    		if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
    			return findPathMatchingResources(locationPattern);
    		}
    		else {
    			// 单文件根据名称加载
    			// 我们的demo中直接走这个分支
    			return new Resource[] {getResourceLoader().getResource(locationPattern)};
    		}
    	}
    }
    

    具体查找文件的方法就不继续延伸了,有兴趣的可以展开阅读。

  4. loadBeanDefinitions(resources)解析资源所有的bean定义,并返回注册成功BeanDefinition个数。

我们继续追踪loadBeanDefinitions(resources)的内部处理逻辑。

loadBeanDefinitions(resources)

// AbstractBeanDefinitionReader#loadBeanDefinitions
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
	Assert.notNull(resources, "Resource array must not be null");
	int count = 0;
	for (Resource resource : resources) {
		count += loadBeanDefinitions(resource);
	}
	return count;
}
// XmlBeanDefinitionReader#loadBeanDefinitions
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
	return loadBeanDefinitions(new EncodedResource(resource));
}

/**
 * 从指定文件中加载BeanDefinition并返回个数
 */
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
	// 部分省略
	Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
	if (!currentResources.add(encodedResource)) {
		throw new BeanDefinitionStoreException(
				"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
	}
	// 获取资源文件字节流
	try (InputStream inputStream = encodedResource.getResource().getInputStream()) {
		InputSource inputSource = new InputSource(inputStream);
		if (encodedResource.getEncoding() != null) {
			inputSource.setEncoding(encodedResource.getEncoding());
		}
		// 从XML资源中加载BeanDefinitions
		return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
	}
	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. 获取当前文件字节流,实例化InputSource
    	// InputSource
        public InputSource (InputStream byteStream){
            setByteStream(byteStream);
        }
        public void setByteStream (InputStream byteStream){
            this.byteStream = byteStream;
        }
    
  2. 设置资源编码格式,真正开始执行XML解析动作。

做了这么多准备动作,终于开始进入实际的加载,我们继续。

doLoadBeanDefinitions(inputSource, resource())

// XmlBeanDefinitionReader#doLoadBeanDefinitions
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
		throws BeanDefinitionStoreException {

	try {
		// 转换为Document对象
		Document doc = doLoadDocument(inputSource, resource);
		// 获取注册成功的BeanDefinition
		int count = registerBeanDefinitions(doc, resource);
		// 部分省略
		return count;
	}
	// 部分省略
}

如何完成注册呢?

// XmlBeanDefinitionReader#registerBeanDefinitions
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
	// 通过反射实例化并返回BeanDefinitionDocumentReader对象,
	// 该对象用于解析xml格式的bean定义
	BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
	// 获取当前已经注册的BeanDefinition个数
	int countBefore = getRegistry().getBeanDefinitionCount();
	// 注册BeanDefinition
	documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
	// 返回当前注册成功的BeanDefinition个数
	return getRegistry().getBeanDefinitionCount() - countBefore;
}

以上流程大致如下:

  1. 通过反射实例化并返回用于解析xml格式的bean定义BeanDefinitionDocumentReader对象。
  2. 注册BeanDefinition。
  3. 返回当前注册成功的BeanDefinition个数。

registerBeanDefinitions(doc, createReaderContext(resource))

接下来我们来简单看看xml配置元数据的解析步骤。

// DefaultBeanDefinitionDocumentReader#registerBeanDefinitions
@Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
	this.readerContext = readerContext;
	doRegisterBeanDefinitions(doc.getDocumentElement());
}
/**
 * 根据资源文件中给定的<beans/>,注册各个<bean/>
 */
 // DefaultBeanDefinitionDocumentReader#doRegisterBeanDefinitions
@SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
protected void doRegisterBeanDefinitions(Element root) {
	// 任何的元素都会在此方法递归。
	// 若当前parent为空则创建子delegate,并引用父delegate
	BeanDefinitionParserDelegate parent = this.delegate;
	this.delegate = createDelegate(getReaderContext(), root, parent);
	// 确定给定的节点是否表明默认命名空间。
	// 也就是我们配置文件的<beans/>的xmlns属性“http://www.springframework.org/schema/beans”
	if (this.delegate.isDefaultNamespace(root)) {
		// 获取当前<beans/>的profile属性名
		String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
		// 判空
		if (StringUtils.hasText(profileSpec)) {
			// MULTI_VALUE_ATTRIBUTE_DELIMITERS=“,; ”
			// 按照","或";"或" "作为分隔符分割
			String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
					profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
			// 获取spring.profiles.active属性值,判断specifiedProfiles是否包含,false则return
			if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
				// 部分省略
				return;
			}
		}
	}
	// 空方法,可以通过重写的方式前置处理
	preProcessXml(root);
	// 开始解析
	parseBeanDefinitions(root, this.delegate);
	// 空方法,通过重写后置处理
	postProcessXml(root);
	this.delegate = parent;
}

大致步骤如下:

  1. 创建BeanDefinitionParserDelegate,它是用于解析资源文件的委托类。

  2. isDefaultNamespace(root)检查被定义的命名空间是否是默认命名空间。

    // BeanDefinitionParserDelegate
    public boolean isDefaultNamespace(Node node) {
    	return isDefaultNamespace(getNamespaceURI(node));
    }
    // 这边获取到的就是<beans/>的xmlns属性,xmlns:命名空间
    @Nullable
    public String getNamespaceURI(Node node) {
    	return node.getNamespaceURI();
    }
    
    // BEANS_NAMESPACE_URI = "http://www.springframework.org/schema/beans"
    public boolean isDefaultNamespace(@Nullable String namespaceUri) {
    	return !StringUtils.hasLength(namespaceUri) || BEANS_NAMESPACE_URI.equals(namespaceUri);
    }
    
  3. 第二步若为true则获取<beans/>profile属性值,若该属性值不为空,进行检查当前节点是否满足环境配置。profile用于指定当前运行环境的启用配置。

    示例:

    public class XmlTest {
        static ApplicationContext context;
        static {
            context = new ClassPathXmlApplicationContext("classpath*:config/*Config.xml");
        }
        @Test
        public void instanceBeanBaseOnConstructorByXml() {
            Car car = (Car) context.getBean("constructorCar");
            System.out.println(car.toString());
        }
        @Test
        public void secInstanceBeanBaseOnConstructorByXml() {
            Car car = (Car) context.getBean("secConstructorCar");
            System.out.println(car.toString());
        }
    }
    

    修改demo中的xml文件配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <beans profile="dev test">
            <bean id="constructorCar" class="com.zt.bean.Car">
                <constructor-arg name="brand" value="Benz"/>
                <constructor-arg name="price" value="200"/>
                <constructor-arg name="color" value="white"/>
            </bean>
        </beans>
        <beans profile="dev">
            <bean id="secConstructorCar" class="com.zt.bean.Car">
                <constructor-arg name="brand" value="Benz"/>
                <constructor-arg name="price" value="200"/>
                <constructor-arg name="color" value="white"/>
            </bean>
        </beans>
    </beans>
    

    修改环境参数:设置环境启动参数
    执行结果:

    org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'secConstructorCar' available
    ···
    Car(brand=Benz, price=200.0, color=white)
    
  4. parseBeanDefinitions(root, this.delegate)开始配置解析。

parseBeanDefinitions(root, this.delegate)

// DefaultBeanDefinitionDocumentReader#parseBeanDefinitions
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
	// 判断是否是Spring默认命名空间,
	// true,例如<beans/><import/><bean/><alias/>
	// false,例如<context:component-scan>等
	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);
	}
}

我们先查看默认命名空间元素是如何解析的:

// DefaultBeanDefinitionDocumentReader#parseDefaultElement
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)) {
		// 递归<beans/>下的所有节点
		doRegisterBeanDefinitions(ele);
	}
}

本文暂时只看processBeanDefinition(ele, delegate)分支。

processBeanDefinition(ele, delegate)

// DefaultBeanDefinitionDocumentReader#processBeanDefinition
// 处理给定的bean元素,解析bean的定义并注册。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
	// 解析当前<bean/>转换为BeanDefinitionHolder
	BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
	if (bdHolder != null) {
		// <bean/>自定义属性及子标签解析
		bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
		try {
			// BeanDefinition注册
			BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
		}
		catch (BeanDefinitionStoreException ex) {
			getReaderContext().error("Failed to register bean definition with name '" +
					bdHolder.getBeanName() + "'", ele, ex);
		}
		// 触发注册事件
		getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
	}
}

大致处理流程如下:

  1. delegate.parseBeanDefinitionElement(ele)<bean/>定义解析为BeanDefinitionHolderBeanDefinitionHolder持有着BeanDefinition、beanName及aliases(别名)。
    public class BeanDefinitionHolder implements BeanMetadataElement {
    
    	private final BeanDefinition beanDefinition;
    
    	private final String beanName;
    
    	@Nullable
    	private final String[] aliases;
    	// 省略
    }
    
  2. <bean/>自定义属性及子标签解析。
  3. BeanDefinition注册。

我们先从第一步,查看如何解析为BeanDefinitionHolder

parseBeanDefinitionElement(ele)

// BeanDefinitionParserDelegate#parseBeanDefinitionElement
@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele) {
	return parseBeanDefinitionElement(ele, null);
}

@Nullable
public BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) {
	// 获取<bean/>的id
	String id = ele.getAttribute(ID_ATTRIBUTE);
	// 获取<bean/>的name
	String nameAttr = ele.getAttribute(NAME_ATTRIBUTE);

	List<String> aliases = new ArrayList<>();
	// 获取是否有name属性的定义
	if (StringUtils.hasLength(nameAttr)) {
		// 将名称按照","或";"或" "作为分隔符分割
		String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS);
		aliases.addAll(Arrays.asList(nameArr));
	}
	
	String beanName = id;
	// 若<bean/>未定义ID属性,并且name属性不为空,
	// 则beanName=aliases.remove(0)
	if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) {
		beanName = aliases.remove(0);
		// 部分省略
	}

	if (containingBean == null) {
		// 名称唯一性检查
		checkNameUniqueness(beanName, aliases, ele);
	}
	// 创建BeanDefinition实例
	AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean);
	if (beanDefinition != null) {
		// 若<bean/>标签未指定id与name,则为true
		if (!StringUtils.hasText(beanName)) {
			try {
				if (containingBean != null) {
					// 生成beanName
					beanName = BeanDefinitionReaderUtils.generateBeanName(
							beanDefinition, this.readerContext.getRegistry(), true);
				}
				else {
					// 生成beanName为类全限定名+容器中已存在的名称个数+1(从0开始)
					// 例如 <bean class="com.zt.bean.Car" />
					// beanName = com.zt.bean.Car#0					
					beanName = this.readerContext.generateBeanName(beanDefinition);
					String beanClassName = beanDefinition.getBeanClassName();
					if (beanClassName != null &&
							beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() &&
							!this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) {
						aliases.add(beanClassName);
					}
				}
				// 部分省略
			} catch (Exception ex) {
				error(ex.getMessage(), ele);
				return null;
			}
		}
		String[] aliasesArray = StringUtils.toStringArray(aliases);
		return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray);
	}
	return null;
}

代码步骤大致如下:

  1. 获取<bean>的id、name以及aliases等信息,判断IOC容器中是否存在同名beanName。
  2. parseBeanDefinitionElement(ele, beanName, containingBean)创建BeanDefinition实例。
  3. 处理未设置id与name的<bean>定义。
  4. 返回BeanDefinitionHolder,通过构造方法设置beanDefinition、beanName、aliases。

第二步是怎么创建BeanDefinition实例的呢?

// BeanDefinitionParserDelegate#parseBeanDefinitionElement
/**
 * 解析bean定义,过程中出现异常则返回null
 */
@Nullable
public AbstractBeanDefinition parseBeanDefinitionElement(
		Element ele, String beanName, @Nullable BeanDefinition containingBean) {

	this.parseState.push(new BeanEntry(beanName));

	String className = null;
	// 判断是否存在class属性
	if (ele.hasAttribute(CLASS_ATTRIBUTE)) {
		className = ele.getAttribute(CLASS_ATTRIBUTE).trim();
	}
	String parent = null;
	// 判断是否存在parent属性。
	if (ele.hasAttribute(PARENT_ATTRIBUTE)) {
		parent = ele.getAttribute(PARENT_ATTRIBUTE);
	}

	try {
		// 创建AbstractBeanDefinition
		AbstractBeanDefinition bd = createBeanDefinition(className, parent);
		// 将<bean/>上定义的属性set到bd对象
		parseBeanDefinitionAttributes(ele, beanName, containingBean, bd);
		bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT));
		// 解析<meta/>
		parseMetaElements(ele, bd);
		// 解析<lookup-method/>
		parseLookupOverrideSubElements(ele, bd.getMethodOverrides());
		// 解析<replaced-method/>
		parseReplacedMethodSubElements(ele, bd.getMethodOverrides());
		// 解析<constructor-arg/>
		parseConstructorArgElements(ele, bd);
		// 解析<property/>
		parsePropertyElements(ele, bd);
		// 解析<qualifier/>
		parseQualifierElements(ele, bd);
		// 设置对应配置文件Resource
		bd.setResource(this.readerContext.getResource());
		bd.setSource(extractSource(ele));
		return bd;
	}
	// 部分省略
	finally {
		this.parseState.pop();
	}
	return null;
}

我们先回顾一下<bean>标签的属性及子标签:

<bean>标签属性
id设置在容器中的唯一标识
namebean的名称定义
class类全限定名
scopesingleton:默认单例;prototype:多例
init-method类初始化方法
destory-method类销毁方法
parent指代“父bean”,不指代父类。
primary是否注入优先,默认:false
abstract声明为抽象bean
depends-on声明该bean与其它bean的依赖关系
factory-bean指定bean工厂
factory-method指定bean工厂方法
autowire自动装配模式(1)no:默认值;(2)byName:根据bean名称自动装配;(3)byType:按照类型进行装配;(4)constructor:通过构造器自动装配;(5)default:使用<beans/>的default-autowire属性配置。
autowire-candidate默认为true,表示允许自动装配注入到其它bean
lazy-init默认为false,表示在IOC容器启动的时候进行实例化;true,则代表使用的时候进行实例化。
<bean>标签的子标签
<meta>源数据设置
<property>bean成员变量属性设置
<constructor-arg>构造方法参数设置
<description>描述信息
<lookup-method>方法注入
<qualifier>指定注入bean名称
<replaced-method>方法替换

大致流程如下:

  1. 创建AbstractBeanDefinition
  2. 解析<bean>属性。
    /**
     * 解析<bean>标签属性配置赋予到bd
     */
    // BeanDefinitionParserDelegate#parseBeanDefinitionAttributes
    public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
    		@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
    	// 判断是否存在single属性配置
    	if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) {
    		error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele);
    	}
    	// 是否存在scope属性
    	else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) {
    		bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE));
    	}
    	else if (containingBean != null) {
    		// Take default from containing bean in case of an inner bean definition.
    		bd.setScope(containingBean.getScope());
    	}
    	// 是否存在abstract属性
    	if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) {
    		bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE)));
    	}
    	// 设置lazt-init属性,默认false
    	String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE);
    	if (isDefaultValue(lazyInit)) {
    		lazyInit = this.defaults.getLazyInit();
    	}
    	bd.setLazyInit(TRUE_VALUE.equals(lazyInit));
    	
    	// 设置自动装配模式默认为0及默认自动装配
    	String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE);
    	bd.setAutowireMode(getAutowireMode(autowire));
    
    	// 是否存在depends-on属性
    	if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) {
    		String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE);
    		bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS));
    	}
    	
    	// 是否存在autowire-candidate属性,默认为true
    	String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE);
    	if (isDefaultValue(autowireCandidate)) {
    		String candidatePattern = this.defaults.getAutowireCandidates();
    		if (candidatePattern != null) {
    			String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern);
    			bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName));
    		}
    	}
    	else {
    		bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate));
    	}
    	
    	// 是否存在primary属性
    	if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) {
    		bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE)));
    	}
    	
    	// 是否存在init-method属性
    	if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) {
    		String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE);
    		bd.setInitMethodName(initMethodName);
    	}
    	else if (this.defaults.getInitMethod() != null) {
    		bd.setInitMethodName(this.defaults.getInitMethod());
    		bd.setEnforceInitMethod(false);
    	}
    
    	// 是否存在destroy-method属性
    	if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) {
    		String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE);
    		bd.setDestroyMethodName(destroyMethodName);
    	}
    	else if (this.defaults.getDestroyMethod() != null) {
    		bd.setDestroyMethodName(this.defaults.getDestroyMethod());
    		bd.setEnforceDestroyMethod(false);
    	}
    	
    	// 是否存在factory-method属性
    	if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) {
    		bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE));
    	}
    	
    	// 是否存在factory-bean属性
    	if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) {
    		bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE));
    	}
    	return bd;
    }
    
  3. 解析<bean>子标签(源码就不在这里贴了,可以自行查看)。
  4. 返回解析并组装完毕后的AbstractBeanDefinition

BeanDefinition已经获取到了,接下来就进行到注册这一步了,接下来我们来查看它的处理逻辑。

registerBeanDefinition(bdHolder, getReaderContext().getRegistry())

/**
 * 注册到容器
 */
// BeanDefinitionReaderUtils#registerBeanDefinition
public static void registerBeanDefinition(
		BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
		throws BeanDefinitionStoreException {
	// 使用beanName注册BeanDefinition
	String beanName = definitionHolder.getBeanName();
	registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

	// 别名注册
	String[] aliases = definitionHolder.getAliases();
	if (aliases != null) {
		for (String alias : aliases) {
			registry.registerAlias(beanName, alias);
		}
	}
}

跟进查看注册逻辑:

// DefaultListableBeanFactory#registerBeanDefinition
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
		throws BeanDefinitionStoreException {

	Assert.hasText(beanName, "Bean name must not be empty");
	Assert.notNull(beanDefinition, "BeanDefinition must not be null");

	if (beanDefinition instanceof AbstractBeanDefinition) {
		try {
			// beanDefinition校验
			((AbstractBeanDefinition) beanDefinition).validate();
		}
		// 部分省略
	}
	// 按beanName从beanDefinitionMap获取beanDefinition
	BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
	if (existingDefinition != null) {
		// 若根据beanName可以获取到beanDefinition,
		// 则判断该beanDefinition是否允许覆盖
		if (!isAllowBeanDefinitionOverriding()) {
			throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
		}
		// 部分省略
		// 允许覆盖的前提下,将当前beanDefinition放到beanDefinitionMap中
		this.beanDefinitionMap.put(beanName, beanDefinition);
	}
	else {
		// 检查是否存在被标记为已创建状态的bean
		if (hasBeanCreationStarted()) {
			// Cannot modify startup-time collection elements anymore (for stable iteration)
			synchronized (this.beanDefinitionMap) {
				this.beanDefinitionMap.put(beanName, beanDefinition);
				List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
				updatedDefinitions.addAll(this.beanDefinitionNames);
				updatedDefinitions.add(beanName);
				this.beanDefinitionNames = updatedDefinitions;
				removeManualSingletonName(beanName);
			}
		}
		else {
			// 进入启动注册阶段
			// key:beanName,value:BeanDefinition,将当前beanDefinition放到beanDefinitionMap中
			this.beanDefinitionMap.put(beanName, beanDefinition);
			// 将BeanDefinition的名称add到beanDefinitionNames中(for)
			this.beanDefinitionNames.add(beanName);
			// 处理environment、systemProperties、systemEnvironment、applicationStartup等bean
			removeManualSingletonName(beanName);
		}
		this.frozenBeanDefinitionNames = null;
	}

	if (existingDefinition != null || containsSingleton(beanName)) {
		// 重置当前beanName的所有bean定义
		resetBeanDefinition(beanName);
	}
	else if (isConfigurationFrozen()) {
		clearByTypeCache();
	}
}

至此我们也终于大致阅读完obtainFreshBeanFactory()的源码,其它具体细节有兴趣可以自行阅读。

总结

至此,我们已经了解了refresh()核心方法的前两个prepareRefresh()obtainFreshBeanFactory()方法,从初始化并校验环境配置,初始化应用监听与事件,到解析资源配置文件,将文件中关于bean定义加载到beanFactory并返回beanFactory的过程。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人生逆旅我亦行人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值