【深度剖析】Spring1.0源码--5、invokeContextConfigurers方法

前言

本文主要介绍invokeContextConfigurers方法,主要完成实现BeanFactoryPostProcessor接口的bean注入,然后回调postProcessBeanFactory方法。注意,该方法需要在对象初始化之前完成。

private void invokeContextConfigurers() {
 		// 1、获取实现BeanFactoryPostProcessor接口的bean名称
		String[] beanNames = getBeanDefinitionNames(BeanFactoryPostProcessor.class);
		for (int i = 0; i < beanNames.length; i++) {
			String beanName = beanNames[i];
			// 2、创建对象
			BeanFactoryPostProcessor configurer = (BeanFactoryPostProcessor) getBean(beanName);
			//3、 回调postProcessBeanFactory方法
			configurer.postProcessBeanFactory(getBeanFactory()); 
		}
	}

BeanFactoryPostProcessor接口

首先来看看BeanFactoryPostProcessor接口,主要就一个方法postProcessBeanFactory,该方法入参是ListableBeanFactoryImpl,拿到bean工厂之后可以对Bean的定义信息进行修改,比如注释提到的使用用户自定义的配置(修改bean定义信息的属性,也就是类的属性值)。理论上来说,拿到beanfactory之后就可以为所欲为了。

/**
 * Allows for custom modification of an application context's beans.
 * Useful for custom config files targetted at system administrators that
 * override bean properties configured in the application context.
 *
 * <p>For reading "beanName.property=value" configuration from a
 * properties file, consider using PropertyResourceConfigurer.
 *
 * @author Juergen Hoeller
 * @since 06.07.2003
 * @see PropertyResourceConfigurer
 */
public interface BeanFactoryPostProcessor extends ApplicationContextAware {

	/**
	 * Modify the application context's internal bean factory after its standard
	 * initialization. All bean definitions will have been loaded, but no beans
	 * will have been instantiated yet. This allows for overriding or adding
	 * properties even to eager-initializing beans.
	 * @param beanFactory the bean factory used by the application context
	 * @throws ApplicationContextException in case of initialization errors
	 */
	void postProcessBeanFactory(ListableBeanFactoryImpl beanFactory) throws ApplicationContextException;

}

提供修改BD的行为postProcessBeanFactory,后面就要根据这些原料创建对象,很多框架都会留有扩展点,Spring也不例外,这里留了一个口子给用户修改BD或者添加BD。

画外音:我们做项目的时候,是不是也可以考虑留扩展点?

执行流程

1、获取BeanFactoryPostProcessor定义信息

getBeanDefinitionNames方法主要找到BeanFactoryPostProcessor实现类的BeanDefinition,之前的文章已经提到BeanDefinition解析后放在beanDefinitionMap。我们来看看是怎么拿的?原来就是遍历map,创建一个集合,把符合条件的类放进去,然后返回。注释上还有温馨提示,我们看看写了什么?这个方法太慢了,不要频繁使用。

/**
	 * Note that this method is slow. Don't invoke it too often:
	 * it's best used only in application initialization.
	 */
	public final String[] getBeanDefinitionNames(Class type) {
		Set keys = this.beanDefinitionMap.keySet();
		List matches = new LinkedList();
		Iterator itr = keys.iterator();
		while (itr.hasNext()) {
			String name = (String) itr.next();
			Class clazz = getMergedBeanDefinition(name).getBeanClass();
			if (type.isAssignableFrom(clazz)) {
				matches.add(name);
			}
		}
		return (String[]) matches.toArray(new String[matches.size()]);
	}

2、创建对象

getBean方法完成创建对象的过程,这里我们先跳过,先把大体的流程搞清楚后,再回过头来看看这个方法,现在只要知道是通过bean工厂去创建的即可。

3、回调方法postProcessBeanFactory

Spring只提供了BeanFactoryPostProcessor接口的一个实现类PropertyResourceConfigurer,看过高版本的小伙伴们应该知道,是有很多实现类的。我们就来看看PropertyResourceConfigurer做了什么?代码非常简单。

第一步:加载配置文件,使用jdk提供的Properties直接加载,配置文件转换成Hashtable。
第二步:根据配置文件的key找到bean,添加bean的属性。定位bean的规则是使用点号( . )隔开,点号前面是bean名称,点号后面是属性名称。
PropertyResourceConfigurer功能

public class PropertyResourceConfigurer extends ApplicationObjectSupport implements BeanFactoryPostProcessor {

	private String location;

	public void setLocation(String location) {
		this.location = location;
	}

	public void postProcessBeanFactory(ListableBeanFactoryImpl beanFactory) throws ApplicationContextException {
		if (this.location != null) {
			Properties prop = new Properties();
			prop.load(getApplicationContext().getResourceAsStream(this.location)); // 加载配置文件
			for (Iterator it = prop.keySet().iterator(); it.hasNext();) {
				String key = (String) it.next();
				processKey(beanFactory, key, prop.getProperty(key)); 
			}
		}
	}

	// 找到需要修改配置的bean,并且设置属性值
	protected void processKey(ListableBeanFactoryImpl factory, String key, String value) throws ApplicationContextException {
		int dotIndex = key.indexOf('.');
		String beanName = key.substring(0, dotIndex);
		String beanProperty = key.substring(dotIndex+1);
		factory.registerAdditionalPropertyValue(beanName, new PropertyValue(beanProperty, value));
	}
}

演示demo

class City{
    String name;
    
    public void setName(String name){
        this.name = name;
    }
}
<beans>
    <bean id ="city" class ="City"></bean>
</beans>
city.name=guangdong

主要是看processKey这个方法,根据图分析找的流程,通过map找到BD,然后添加配置。将city.name切割为两部分,city表示bean的名字(唯一的),name作为bean的一个属性,加载配置之后,将其放入对应BD的pvs中。顺带解释一下MutablePropertyValues对象,其实就是封装了一个List用于存放PropertyValue。

总结

主要分两点:
1、BeanFactoryPostProcessor接口介绍,提供回调方法,把BeanFactory传进去,可以对Bean工厂进行操作
2、找到所有BeanFactoryPostProcessor接口的实现类,然后初始化找到的实现类,最后回调postProcessBeanFactory方法
3、PropertyResourceConfigurer功能,加载配置文件,然后根据配置文件中的名字找到DeanDefinition,最后添加DeanDefinition的属性。

上一期:4、refreshBeanFactory方法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值