好玩Spring之BeanFactoryPostProcessor

作用

/*
 * 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方法的語義。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章