作用
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.config;
import org.springframework.beans.BeansException;
/**
* Allows for custom modification of an application context's bean definitions,
* adapting the bean property values of the context's underlying bean factory.
*
* <p>Application contexts can auto-detect BeanFactoryPostProcessor beans in
* their bean definitions and apply them before any other beans get created.
*
* <p>Useful for custom config files targeted at system administrators that
* override bean properties configured in the application context.
*
* <p>See PropertyResourceConfigurer and its concrete implementations
* for out-of-the-box solutions that address such configuration needs.
*
* <p>A BeanFactoryPostProcessor may interact with and modify bean
* definitions, but never bean instances. Doing so may cause premature bean
* instantiation, violating the container and causing unintended side-effects.
* If bean instance interaction is required, consider implementing
* {@link BeanPostProcessor} instead.
*
* @author Juergen Hoeller
* @since 06.07.2003
* @see BeanPostProcessor
* @see PropertyResourceConfigurer
*/
@FunctionalInterface
public interface BeanFactoryPostProcessor {
/**
* 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 org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
從類註釋可知,BeanFactoryPostProcessor在容器實例化任何bean之前讀取bean的定義(配置元數據BeanDefinition),並可以修改它,比如修改bean的屬性值,改變scope等。
注意點
而且有幾句話要特別注意:
1. All bean definitions will have been loaded, but no beans will have been instantiated yet
說明了這個接口起作用的時間點是,All bean loaded之後,instantiated之前,也就是說只能修改基本的bean定義。
public class TestBean {
String id;
String name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition bd = beanFactory.getBeanDefinition("testBean");
System.out.println(bd.getPropertyValues());
bd.getPropertyValues().addPropertyValue("id", "111");
bd.getPropertyValues().addPropertyValue("name", "222");
}
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.registerBean(TestBean.class);
MyBeanFactoryPostProcessor bpp = new MyBeanFactoryPostProcessor();
context.addBeanFactoryPostProcessor(bpp);
context.refresh();
TestBean testBean = (TestBean) context.getBean("testBean");
System.out.println(testBean.getId());
}
}
結果:
PropertyValues: length=0
111
說明通過postProcessBeanFactory(...)設置屬性值成功。
2. Doing so(bean instances) may cause premature bean instantiation, violating the container and causing unintended side-effects
如果在這個接口中進行bean的實例化,那可能會導致意外的副作用,所以It's forbidden
借用一個例子(源地址:https://www.jianshu.com/p/3d099ea43b0e)
@Component
public class PrematureBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
Map<String, BBean> map = beanFactory.getBeansOfType(BBean.class);
for (BBean bBean : map.values()) {
assert bBean.getABean() == null;
}
}
}
@Component("bBean")
public class BBean {
@Autowired
private ABean aBean;
public ABean getABean() {
return aBean;
}
}
@Component
public class ABean {
private String name = "a";
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
輸出結果:BBean中被期待注入的ABean最終爲null。爲啥呢?看下圖就知道了
BeanFactoryPostProcessor的postProcessBeanFactory方法執行,是在上圖中的①處執行的,
而@Autowired的執行,是在②處執行的,
也就是說,我在執行postProcessBeanFactory方法時,ABean就還沒注入到容器中,自然以上獲取的結果就是null了
典型應用
在之前《好玩Spring之ConfigurationClassPostProcessor分析》一文中,有以下這個類的繼承關係圖
ConfigurationClassPostProcessor最終實現了BeanFactoryPostProcessor接口。
ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry:負責掃描程序中的@Configuration、@Bean等註解,根據程序的中Bean創建BeanDefinition,並註冊到容器中。
ConfigurationClassPostProcessor#postProcessBeanFactory:負責對Full Configuration 配置進行增強,攔截@Bean方法來確保增強執行@Bean方法的語義。