BeanFactoryPostProcessor 和 BeanPostProcessor
前者是对于容器而已,也就是容器启动时候会把所有的BeanFactoryPostProcessor执行一遍,
当你自己自定义BeanFactoryPostProcessor的时候,并且以javaconfig的形式,例如
package com.fengdui.dubbo.config;
import com.alibaba.dubbo.config.*;
import com.alibaba.dubbo.config.spring.AnnotationBean;
import com.alibaba.dubbo.config.spring.ReferenceBean;
import com.fengdui.dubbo.service.IDubboDemoService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* @author FD
* @date 2016/12/20
*/
@Configuration
//@PropertySource(value = "classpath:/dubbo.properties")
public class DubboConfig {
@Value("${dubbo.application.name}")
private String applicationName;
// @Value("${dubbo.application.logger}")
// private String logger;
@Value("${dubbo.registr.protocol}")
private String protocol;
@Value("${dubbo.registry.address}")
private String registryAddress;
@Value("${dubbo.protocol.name}")
private String protocolName;
@Value("${dubbo.protocol.port}")
private int protocolPort;
@Value("${dubbo.provider.timeout}")
private int timeout;
@Value("${dubbo.provider.retries}")
private int retries;
@Value("${dubbo.provider.delay}")
private int delay;
@Value("${dubbo.annotation.package}")
private String packageName;
// @Bean
// public ReferenceBean referenceBean(ApplicationConfig application, RegistryConfig registry) {
// // 引用远程服务
// ReferenceBean reference = new ReferenceBean(); // 此实例很重,封装了与注册中心的连接以及与提供者的连接,请自行缓存,否则可能造成内存和连接泄漏
// reference.setApplication(application);
// reference.setRegistry(registry); // 多个注册中心可以用setRegistries()
// reference.setInterface(IDubboDemoService.class);
// reference.setVersion("1.0.0");
// // 和本地bean一样使用xxxService
IDubboDemoService xxxService = reference.get(); // 注意:此代理对象内部封装了所有通讯细节,对象较重,请缓存复用
// return reference;
// }
/**
* 注入dubbo上下文
* @return
*/
@Bean
public ApplicationConfig applicationConfig() {
System.out.println(delay);
// 当前应用配置
ApplicationConfig applicationConfig = new ApplicationConfig();
applicationConfig.setName(this.applicationName);
return applicationConfig;
}
@Bean
public AnnotationBean annotationBean(/*@Value("${dubbo.annotation.package}") String packageName*/) {
System.out.println("Fd");
AnnotationBean annotationBean = new AnnotationBean();
annotationBean.setPackage(packageName);
return annotationBean;
}
/**
* 注入dubbo注册中心配置,基于zookeeper
*
* @return
*/
@Bean
public RegistryConfig registryConfig() {
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setProtocol(protocol);
registry.setAddress(registryAddress);
return registry;
}
/**
* 默认基于dubbo协议提供服务
*
* @return
*/
@Bean
public ProtocolConfig protocolConfig() {
// 服务提供者协议配置
ProtocolConfig protocolConfig = new ProtocolConfig();
protocolConfig.setName(protocolName);
protocolConfig.setPort(protocolPort);
protocolConfig.setThreads(200);
System.out.println("默认protocolConfig:" + protocolConfig.hashCode());
return protocolConfig;
}
/**
* dubbo服务提供
*
* @param applicationConfig
* @param registryConfig
* @param protocolConfig
* @return
*/
@Bean(name="defaultProvider")
public ProviderConfig providerConfig(ApplicationConfig applicationConfig, RegistryConfig registryConfig, ProtocolConfig protocolConfig) {
System.out.println(delay);
ProviderConfig providerConfig = new ProviderConfig();
providerConfig.setTimeout(timeout);
providerConfig.setRetries(retries);
providerConfig.setDelay(delay);
providerConfig.setApplication(applicationConfig);
providerConfig.setRegistry(registryConfig);
providerConfig.setProtocol(protocolConfig);
return providerConfig;
}
}
先解释一下,AnnotationBean 实现了BeanFactoryPostProcessor接口,
如果按照上面的方式,你会发现@value注解是不生效的,
需要在方法前加上static
@Bean
public static AnnotationBean annotationBean(@Value("${dubbo.annotation.package}") String packageName) {
AnnotationBean annotationBean = new AnnotationBean();
annotationBean.setPackage(packageName);
return annotationBean;
}
如果不加static,那么这个bean是会依赖外部bean的,也就是getbean("annotationBean")的时候还会getbean("dubboConfig")
触发getbean("annotationBean"),是在容器启动的时候,在abstractApplicationContext的invokeBeanFactoryPostProcessors方法,
此时BeanPostProcessor还没有被注册,也就是hasInstantiationAwareBeanPostProcessors为false,
当你getbean的时候会使用populateBean方法来进行属性的注入,其中对于value注解的注入的逻辑
if (hasInstAwareBpps || needsDepCheck) {
PropertyDescriptor[] filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
if (hasInstAwareBpps) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
if (pvs == null) {
return;
}
}
}
}
if (needsDepCheck) {
checkDependencies(beanName, mbd, filteredPds, pvs);
}
}
hasInstAwareBpps 这里为false,就不会属性注入(属性注入会使用一个AutowiredAnnotationBeanPostProcessor),所以会出现上面@value不生效的问题,
文档也有说明
http://docs.spring.io/spring-framework/docs/4.0.4.RELEASE/javadoc-api/org/springframework/context/annotation/Bean.html
BeanFactoryPostProcessor
-returning @Bean
methods
Special consideration must be taken for @Bean
methods that return SpringBeanFactoryPostProcessor
(BFPP
) types. Because BFPP
objects must be instantiated very early in thecontainer lifecycle, they can interfere with processing of annotations such as@Autowired
,@Value
, and @PostConstruct
within@Configuration
classes. To avoid theselifecycle issues, mark BFPP
-returning@Bean
methods as static
. For example:
@Bean
public static PropertyPlaceholderConfigurer ppc() {
// instantiate, configure and return ppc ...
}
By marking this method as static
, it can be invoked without causing instantiation of itsdeclaring@Configuration
class, thus avoiding the above-mentioned lifecycle conflicts.Note however thatstatic
@Bean
methods will not be enhanced for scoping and AOPsemantics as mentioned above. This works out inBFPP
cases, as they are not typicallyreferenced by other @Bean
methods. As a reminder, a WARN-level log message will beissued for any non-static@Bean
methods having a return type assignable toBeanFactoryPostProcessor
.
自定义BeanFactoryPostProcessor以javaconfig方式配置要加上static。
2018.03.14更新 --------------------------------------------------------------------------------------
在ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod方法中
if (metadata.isStatic()) {
// static @Bean method
beanDef.setBeanClassName(configClass.getMetadata().getClassName());
beanDef.setFactoryMethodName(methodName);
}
else {
// instance @Bean method
beanDef.setFactoryBeanName(configClass.getBeanName());
beanDef.setUniqueFactoryMethodName(methodName);
}
如果是非静态的,会设置一个factorybean,在我这个例子里就是dubboconfig
然后容器启动到得到这个annotationBean的时候,发现有factoryBeanName,会再次getbean("dubboconfig"),
但是此时处理@value注解的beanpostprocess还未准备好。
AbstractAutowireCapableBeanFactory#instantiateUsingFactoryMethod =》
ConstructorResolver#instantiateUsingFactoryMethod
@Value注解是使用
PropertySourcesPlaceholderConfigurer
处理的