spring 容器后处理器 BeanFactoryPostProcessor 使用需要注意的一个问题

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

处理的

你可能感兴趣的:(spring)