目录
前言
本文主要介绍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名称,点号后面是属性名称。
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的属性。