Spring原始碼分析(4)---BeanFactoryPostProcessor(看見的不一定是真的)
阿新 • • 發佈:2019-02-05
在第二編對BeanFactory的分析中,我們老能看見BeanFactoyPostProcessor的身影,那麼在這一節中,我們來詳細的討論一下
BeanFactoryPostProcessor的程式碼結構,從中學習他的優秀之處;
BeanFactoryPostProcessor能夠修改bean配置檔案中的bean的定義;使得我們能夠進行一些額外的處理;
在spring中,我們幾乎不用自己擴充套件這個介面,因為他內建了很多實現,但是,我們從原理上和程式碼上來分析其功能的實現;
一下是他的類圖實現:
拿PropertyPlaceholderConfigurer舉例;
PropertyPlaceholderConfigurer的功能是這樣的,我們在配置檔案中配置如下Bean:
userinfo.properties:
db.username:scott
db.password:tiger
然後在ApplicationContext下面會自動呼叫這個PostProcessor;把${db.userName}轉換為scott;
在BeanFactory下面的話,必須手動生成以上PostProcesor物件,並且手動呼叫postProcessorBeanFactory(configureableBeanFactory)方法;
那麼我們現在來看一下程式碼的實現
在PropertyResourceConfigurer中,定義了 postProcessBeanFactory方法,定義了方法執行的流程,使用了模板模式;
將具體演算法的實現暴露到了子類;
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = mergeProperties();
// Convert the merged properties, if necessary.
convertProperties(mergedProps);
// Let the subclass process the properties.
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
模板方法1:mergeProperties()如下:
protected Properties mergeProperties() throws IOException {
Properties result = new Properties();
if (this.localOverride) {
// Load properties from file upfront, to let local properties override.
loadProperties(result);
}
if (this.localProperties != null) {
for (int i = 0; i < this.localProperties.length; i++) {
Properties props = this.localProperties[i];
// Use propertyNames enumeration to also catch default properties.
for (Enumeration en = props.propertyNames(); en.hasMoreElements();) {
String key = (String) en.nextElement();
result.setProperty(key, props.getProperty(key));
}
}
}
if (!this.localOverride) {
// Load properties from file afterwards, to let those properties override.
loadProperties(result);
}
return result;
}
在這個方法中,將載入你在構造bean的時候傳入的properties值,然後儲存到這個PostProcessor中,方便覆蓋bean定義的元資料,如${db.username}等等;
模板方法processProperties被推到子類實現了:
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
//構造一個BeanDefinition訪問器(前一節已經分析過),是其內部類:
BeanDefinitionVisitor visitor = new PlaceholderResolvingBeanDefinitionVisitor(props);
//得到所有beanName
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (int i = 0; i < beanNames.length; i++) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
if (!(beanNames[i].equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(beanNames[i]);
try {
//在這段程式碼中會遍歷所有的Bean定義的帶來${}的屬性,包括map ,list,String等等;
visitor.visitBeanDefinition(bd);
}
catch (BeanDefinitionStoreException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanNames[i], ex.getMessage());
}
}
}
}
內部的BeanDefinition訪問器:
private class PlaceholderResolvingBeanDefinitionVisitor extends BeanDefinitionVisitor {
private final Properties props;
public PlaceholderResolvingBeanDefinitionVisitor(Properties props) {
this.props = props;
}
protected String resolveStringValue(String strVal) throws BeansException {
return parseStringValue(strVal, this.props, new HashSet());
}
}
在訪問器中visitBeanDefinition(bd)會遍歷此BeanDefinition的proerty,constructor等等可以設定${}的地方例如propertiy:
protected void visitPropertyValues(MutablePropertyValues pvs) {
PropertyValue[] pvArray = pvs.getPropertyValues();
for (int i = 0; i < pvArray.length; i++) {
PropertyValue pv = pvArray[i];
//分別解決每個屬性欄位的解析;
Object newVal = resolveValue(pv.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
pvs.addPropertyValue(pv.getName(), newVal);
}
}
}
如何解析,是暴露到子類中去進行的如PropertyPlaceholderConfigurer是對${}進行外部檔案的替換,我們也可以自己實現別的替換方式,如:****替換位"corey",很無聊吧: ->;
resolveStringValue((String) value);:
protected Object resolveValue(Object value) {
if (value instanceof BeanDefinition) {
visitBeanDefinition((BeanDefinition) value);
}
else if (value instanceof BeanDefinitionHolder) {
visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition());
}
else if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
String newBeanName = resolveStringValue(ref.getBeanName());
if (!newBeanName.equals(ref.getBeanName())) {
return new RuntimeBeanReference(newBeanName);
}
}
else if (value instanceof List) {
visitList((List) value);
}
else if (value instanceof Set) {
visitSet((Set) value);
}
else if (value instanceof Map) {
visitMap((Map) value);
}
else if (value instanceof TypedStringValue) {
TypedStringValue typedStringValue = (TypedStringValue) value;
String visitdString = resolveStringValue(typedStringValue.getValue());
typedStringValue.setValue(visitdString);
}
else if (value instanceof String) {
return resolveStringValue((String) value);
}
return value;
}
那${userName}舉例:在PropertyPlaceholderConfigurer中:
protected String parseStringValue(String strVal, Properties props, Set visitedPlaceholders)
throws BeanDefinitionStoreException {
StringBuffer buf = new StringBuffer(strVal);
//提取出${}中間的字串,
int startIndex = strVal.indexOf(this.placeholderPrefix);
while (startIndex != -1) {
int endIndex = buf.toString().indexOf(
this.placeholderSuffix, startIndex + this.placeholderPrefix.length());
if (endIndex != -1) {
String placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex);
if (!visitedPlaceholders.add(placeholder)) {
throw new BeanDefinitionStoreException(
"Circular placeholder reference '" + placeholder + "' in property definitions");
}
//用System.getEnv和外部的properties檔案替代了${}中間的值
String propVal = resolvePlaceholder(placeholder, props, this.systemPropertiesMode);
if (propVal != null) {
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
//巢狀執行;直至無法解析;
propVal = parseStringValue(propVal, props, visitedPlaceholders);
buf.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isDebugEnabled()) {
logger.debug("Resolved placeholder '" + placeholder + "' to value [" + propVal + "]");
}
startIndex = buf.toString().indexOf(this.placeholderPrefix, startIndex + propVal.length());
}
else if (this.ignoreUnresolvablePlaceholders) {
// Proceed with unprocessed value.
startIndex = buf.toString().indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
}
else {
throw new BeanDefinitionStoreException("Could not resolve placeholder '" + placeholder + "'");
}
visitedPlaceholders.remove(placeholder);
}
else {
startIndex = -1;
}
}
return buf.toString();
}
在這裡,模板模式再一次展現了他的魅力,我想在這裡討論一下:PropertiesLoaderSupport和PropertyResourceConfigurer的關係,我們看見PropertiesLoaderSupport提供了properties檔案的載入,在這裡繼承抽象類PropertiesLoaderSupport其實是達到與複用;
我們繼承一個抽象類的原因有兩個:
1):與其它類功能方法之間的複用(比如這裡的PropertiesLoaderSupport);而不是從分類學上面屬於一類,這樣的抽象類屬於工具類;這裡的功能複用,有兩種手段可以實現,一種是組合,一種是繼承;
2):抽象類中約定類流程,把演算法的具體實現暴露給子類;
BeanFactoryPostProcessor的程式碼結構,從中學習他的優秀之處;
BeanFactoryPostProcessor能夠修改bean配置檔案中的bean的定義;使得我們能夠進行一些額外的處理;
在spring中,我們幾乎不用自己擴充套件這個介面,因為他內建了很多實現,但是,我們從原理上和程式碼上來分析其功能的實現;
一下是他的類圖實現:
拿PropertyPlaceholderConfigurer舉例;
PropertyPlaceholderConfigurer的功能是這樣的,我們在配置檔案中配置如下Bean:
- <beanid="PropertyPlaceholderConfigurer"
- <propertyname="order">1</property>
- <propertyname="locations">
- <list>
- <value>userinfo.properties</value>
- </list>
- </property>
- </bean>
- <beanid="user"class="org.test.UserInfo"
- <propertyname="order" value="${db.userName}"></property>
- <propertyname="order" value="${db.password}"></property>
- </property>
- </bean>
userinfo.properties:
db.username:scott
db.password:tiger
然後在ApplicationContext下面會自動呼叫這個PostProcessor;把${db.userName}轉換為scott;
在BeanFactory下面的話,必須手動生成以上PostProcesor物件,並且手動呼叫postProcessorBeanFactory(configureableBeanFactory)方法;
那麼我們現在來看一下程式碼的實現
在PropertyResourceConfigurer中,定義了
將具體演算法的實現暴露到了子類;
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = mergeProperties();
// Convert the merged properties, if necessary.
convertProperties(mergedProps);
// Let the subclass process the properties.
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}
模板方法1:mergeProperties()如下:
protected Properties mergeProperties() throws IOException {
Properties result = new Properties();
if (this.localOverride) {
// Load properties from file upfront, to let local properties override.
loadProperties(result);
}
if (this.localProperties != null) {
for (int i = 0; i < this.localProperties.length; i++) {
Properties props = this.localProperties[i];
// Use propertyNames enumeration to also catch default properties.
for (Enumeration en = props.propertyNames(); en.hasMoreElements();) {
String key = (String) en.nextElement();
result.setProperty(key, props.getProperty(key));
}
}
}
if (!this.localOverride) {
// Load properties from file afterwards, to let those properties override.
loadProperties(result);
}
return result;
}
在這個方法中,將載入你在構造bean的時候傳入的properties值,然後儲存到這個PostProcessor中,方便覆蓋bean定義的元資料,如${db.username}等等;
模板方法processProperties被推到子類實現了:
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
//構造一個BeanDefinition訪問器(前一節已經分析過),是其內部類:
BeanDefinitionVisitor visitor = new PlaceholderResolvingBeanDefinitionVisitor(props);
//得到所有beanName
String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();
for (int i = 0; i < beanNames.length; i++) {
// Check that we're not parsing our own bean definition,
// to avoid failing on unresolvable placeholders in properties file locations.
if (!(beanNames[i].equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(beanNames[i]);
try {
//在這段程式碼中會遍歷所有的Bean定義的帶來${}的屬性,包括map ,list,String等等;
visitor.visitBeanDefinition(bd);
}
catch (BeanDefinitionStoreException ex) {
throw new BeanDefinitionStoreException(bd.getResourceDescription(), beanNames[i], ex.getMessage());
}
}
}
}
內部的BeanDefinition訪問器:
private class PlaceholderResolvingBeanDefinitionVisitor extends BeanDefinitionVisitor {
private final Properties props;
public PlaceholderResolvingBeanDefinitionVisitor(Properties props) {
this.props = props;
}
protected String resolveStringValue(String strVal) throws BeansException {
return parseStringValue(strVal, this.props, new HashSet());
}
}
在訪問器中visitBeanDefinition(bd)會遍歷此BeanDefinition的proerty,constructor等等可以設定${}的地方例如propertiy:
protected void visitPropertyValues(MutablePropertyValues pvs) {
PropertyValue[] pvArray = pvs.getPropertyValues();
for (int i = 0; i < pvArray.length; i++) {
PropertyValue pv = pvArray[i];
//分別解決每個屬性欄位的解析;
Object newVal = resolveValue(pv.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
pvs.addPropertyValue(pv.getName(), newVal);
}
}
}
如何解析,是暴露到子類中去進行的如PropertyPlaceholderConfigurer是對${}進行外部檔案的替換,我們也可以自己實現別的替換方式,如:****替換位"corey",很無聊吧: ->;
resolveStringValue((String) value);:
protected Object resolveValue(Object value) {
if (value instanceof BeanDefinition) {
visitBeanDefinition((BeanDefinition) value);
}
else if (value instanceof BeanDefinitionHolder) {
visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition());
}
else if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
String newBeanName = resolveStringValue(ref.getBeanName());
if (!newBeanName.equals(ref.getBeanName())) {
return new RuntimeBeanReference(newBeanName);
}
}
else if (value instanceof List) {
visitList((List) value);
}
else if (value instanceof Set) {
visitSet((Set) value);
}
else if (value instanceof Map) {
visitMap((Map) value);
}
else if (value instanceof TypedStringValue) {
TypedStringValue typedStringValue = (TypedStringValue) value;
String visitdString = resolveStringValue(typedStringValue.getValue());
typedStringValue.setValue(visitdString);
}
else if (value instanceof String) {
return resolveStringValue((String) value);
}
return value;
}
那${userName}舉例:在PropertyPlaceholderConfigurer中:
protected String parseStringValue(String strVal, Properties props, Set visitedPlaceholders)
throws BeanDefinitionStoreException {
StringBuffer buf = new StringBuffer(strVal);
//提取出${}中間的字串,
int startIndex = strVal.indexOf(this.placeholderPrefix);
while (startIndex != -1) {
int endIndex = buf.toString().indexOf(
this.placeholderSuffix, startIndex + this.placeholderPrefix.length());
if (endIndex != -1) {
String placeholder = buf.substring(startIndex + this.placeholderPrefix.length(), endIndex);
if (!visitedPlaceholders.add(placeholder)) {
throw new BeanDefinitionStoreException(
"Circular placeholder reference '" + placeholder + "' in property definitions");
}
//用System.getEnv和外部的properties檔案替代了${}中間的值
String propVal = resolvePlaceholder(placeholder, props, this.systemPropertiesMode);
if (propVal != null) {
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
//巢狀執行;直至無法解析;
propVal = parseStringValue(propVal, props, visitedPlaceholders);
buf.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isDebugEnabled()) {
logger.debug("Resolved placeholder '" + placeholder + "' to value [" + propVal + "]");
}
startIndex = buf.toString().indexOf(this.placeholderPrefix, startIndex + propVal.length());
}
else if (this.ignoreUnresolvablePlaceholders) {
// Proceed with unprocessed value.
startIndex = buf.toString().indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
}
else {
throw new BeanDefinitionStoreException("Could not resolve placeholder '" + placeholder + "'");
}
visitedPlaceholders.remove(placeholder);
}
else {
startIndex = -1;
}
}
return buf.toString();
}
在這裡,模板模式再一次展現了他的魅力,我想在這裡討論一下:PropertiesLoaderSupport和PropertyResourceConfigurer的關係,我們看見PropertiesLoaderSupport提供了properties檔案的載入,在這裡繼承抽象類PropertiesLoaderSupport其實是達到與複用;
我們繼承一個抽象類的原因有兩個:
1):與其它類功能方法之間的複用(比如這裡的PropertiesLoaderSupport);而不是從分類學上面屬於一類,這樣的抽象類屬於工具類;這裡的功能複用,有兩種手段可以實現,一種是組合,一種是繼承;
2):抽象類中約定類流程,把演算法的具體實現暴露給子類;