spring扩展点之BeanFactoryPostProcessor和BeanPostProcessor
先来看下这两个接口的定义:
/*
* 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
*
* https://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;
import org.springframework.lang.Nullable;
/**
* Factory hook that allows for custom modification of new bean instances,
* e.g. checking for marker interfaces or wrapping them with proxies.
*
* <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
* bean definitions and apply them to any beans subsequently created.
* Plain bean factories allow for programmatic registration of post-processors,
* applying to all beans created through this factory.
*
* <p>Typically, post-processors that populate beans via marker interfaces
* or the like will implement {@link #postProcessBeforeInitialization},
* while post-processors that wrap beans with proxies will normally
* implement {@link #postProcessAfterInitialization}.
*
* @author Juergen Hoeller
* @since 10.10.2003
* @see InstantiationAwareBeanPostProcessor
* @see DestructionAwareBeanPostProcessor
* @see ConfigurableBeanFactory#addBeanPostProcessor
* @see BeanFactoryPostProcessor
*/
public interface BeanPostProcessor {
/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>The default implementation returns the given {@code bean} as-is.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding {@code bean instanceof FactoryBean} checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* <p>The default implementation returns the given {@code bean} as-is.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
/*
* 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
*
* https://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和BeanPostProcessor的区别
BeanFactoryPostProcessor和BeanPostProcessor都是spring初始化bean的扩展点。两个接口非常相似。
BeanFactoryPostProcessor可以对bean的定义(配置元数据)进行处理。也就是说,Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其它的bean之前读取配置元数据,并有可能修改它。如果你愿意,你可以配置多个BeanFactoryPostProcessor。你还能通过设置'order'属性来控制BeanFactoryPostProcessor的执行次序。
注册BeanFactoryPostProcessor的实例,需要重载void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
通过beanFactory可以获取bean的示例或定义等。同时可以修改bean的属性,这是和BeanPostProcessor最大的区别。
例如:
BeanDefinition bd = beanFactory.getBeanDefinition("xxBean");
MutablePropertyValues mpv = bd.getPropertyValues();
if(pv.contains("xxName")) {
pv.addPropertyValue("xxName", "icoder");
}
BeanPostProcessor的实例,需要重载下面两个方法
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
二、BeanPostProcessors
1、bean生成过程
首先回顾下bean的生命周期如下图:
如果这个接口的某个实现类被注册到某个容器,那么该容器的每个受管Bean在调用初始化方法的前后,都会获得该接口实现类的一个回调。容器调用接口定义的方法时会将该受管Bean的实例和名字通过参数传入方法,进过处理后通过方法的返回值返回给容器。
要使用BeanPostProcessor回调,就必须先在容器中注册实现该接口的类,那么如何注册呢?BeanFactory和ApplicationContext容器的注册方式不大一样:
- 若使用BeanFactory,则必须要显示的调用其addBeanPostProcessor()方法进行注册,参数为BeanPostProcessor实现类的实例;
- 如果是使用ApplicationContext,那么容器会在配置文件在中自动寻找实现了BeanPostProcessor接口的Bean,然后自动注册,我们要做的只是配置一个BeanPostProcessor实现类的Bean就可以了。
假如我们使用了多个的BeanPostProcessor的实现类,那么如何确定处理顺序呢?其实只要实现Ordered接口,设置order属性就可以很轻松的确定不同实现类的处理顺序了。
3、示例
3.1 applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<!-- 声明注解方式加载bean-->
<context:annotation-config/>
<!-- 要加载的bean的包路径-->
<context:component-scan base-package="com.meituan.hyt.test1"/>
<bean id="userPostProcessor" class="com.meituan.hyt.test1.UserPostProcessor"/>
</beans>
3.2 自己的业务bean
package com.meituan.hyt.test1;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class User {
@Value("老名字")
private String name;
@Value("50")
private Integer id;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", id=" + id +
'}';
}
}
3.3 postProcessor bean
package com.meituan.hyt.test1;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class UserPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
if(o instanceof User){
User user = (User)o;
user.setName("新名字");
user.setId(100);
return user;
}
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
return o;
}
}
package com.meituan.hyt.test1;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Main2 {
public static void main(String[] args) {
ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
User user = (User) cxt.getBean("user");
System.out.println(user.toString());
}
}
3.5 执行结果
如果没有<bean id="userPostProcessor" class="com.meituan.hyt.test1.UserPostProcessor"/>
User{name='老名字', id=50}
添加<bean id="userPostProcessor" class="com.meituan.hyt.test1.UserPostProcessor"/>
User{name='新名字', id=100}
4、InstantiationAwareBeanPostProcessor是BeanPostProcessor的子接口
InstantiationAwareBeanPostProcessor是BeanPostProcessor的子接口,可以在Bean生命周期的另外两个时期提供扩展的回调接口,即实例化Bean之前(调用postProcessBeforeInstantiation方法)和实例化Bean之后(调用postProcessAfterInstantiation方法)。 其使用方法与上面介绍的BeanPostProcessor接口类似,只时回调时机不同。该接口是在bean被实例化之后,相关属性被设置之前之后回调
三、与BeanFactoryPostProcessor接口的区别
1、实现BeanFactoryPostProcessor 接口,会被Application contexts自动发现
2、BeanFactoryPostProcessor 仅仅对 bean definitions 发生关系,不能对bean instances 交互,对bean instances 的交互,由BeanPostProcessor的实现来处理
3、PropertyResourceConfigurer 是一个典型的实现 (PropertyResourceConfigurer是BeanFactoryPostProcessor的一个实现)
BeanFactoryPostProcessor接口实现类可以在当前BeanFactory初始化后,bean实例化之前对BeanFactory做一些处理。BeanFactoryPostProcessor是针对于bean容器的,在调用它时,BeanFactory只加载了bean的定义,还没有对它们进行实例化,所以我们可以通过对BeanFactory的处理来达到影响之后实例化bean的效果。跟BeanPostProcessor一样,ApplicationContext也能自动检测和调用容器中的BeanFactoryPostProcessor。
示例1:
package com.meituan.hyt.test1;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class UserBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("BeanFactoryPostProcessor doing");
}
}
applicationContext.xml中添加bean配置
<bean id="userBeanFactoryPostProcessor" class="com.meituan.hyt.test1.UserBeanFactoryPostProcessor"/>
重新运行,结果
BeanFactoryPostProcessor doing
User{name='新名字', id=100}
示例2:
有这样的也个业务场景:
<bean id="user" class="com.gym.UserServiceImpl" >
<property name="username" value="${username_}"/>
<property name="password" value="${password_}"/>
</bean>
spring支持系统对username_进行占位符的配置为properties文件配置,试想如果我们有个配置中心,我们希望spring启动的时候,从远程配置中心取数据,而非本地文件,这里就需要我们自定义一个实现BeanFactoryPostProcessor的PropertyResourceConfigurer 实例。
看下面的例子:
bean.xml配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd"
default-autowire="byName">
<bean id="user" class="com.gym.UserServiceImpl" >
<property name="username" value="${username_}"/>
<property name="password" value="${password_}"/>
</bean>
<bean id="myFactoryPostProcessor" class="com.gym.MyFilePlaceHolderBeanFactoryPostProcessor"/>
</beans>
模拟从远程取文件:
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.support.PropertiesLoaderUtils;
/**
* @author xinchun.wang
*/
public class MyFilePlaceHolderBeanFactoryPostProcessor
extends PropertyPlaceholderConfigurer implements InitializingBean{
public void afterPropertiesSet() throws Exception {
List<Properties> list = new ArrayList<Properties>();
Properties p = PropertiesLoaderUtils.loadAllProperties("config.properties");
list.add(p);
//这里是关键,这就设置了我们远程取得的List<Properties>列表
setPropertiesArray(list.toArray(new Properties[list.size()]));
}
}
java bean配置
public class UserServiceImpl implements IUserService{
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
public UserServiceImpl(){
logger.info("UserServiceImpl 构造函数 ");
}
private String username;
private String password;
public String getUsername() {
return username;
}
public String getPassword() {
return password;
}
public void setUsername(String username) {
logger.info("UserServiceImpl setUsername {}",username);
this.username = username;
}
public void setPassword(String password) {
logger.info("UserServiceImpl setPassword {}",password);
this.password = password;
}
@Override
public String toString() {
return "UserServiceImpl [username=" + username + ", password="
+ password + "]";
}
}
测试
public class TestApplicationContext {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(
"classpath:spring/applicationContext.xml");
IUserService userService = applicationContext.getBean(IUserService.class);
String password = userService.getPassword();
applicationContext.destroy();
System.out.println(password);
}
}
上一篇: spring4.1.8扩展实战之八:Import注解
下一篇: Spring框架(三)——AOP
推荐阅读
-
Spring扩展点之Aware接口族
-
Spring扩展点之FactoryBean接口
-
Spring扩展点之BeanFactoryPostProcessor
-
我的spring学习笔记15-容器扩展点之PropertyOverrideConfigurer
-
我的spring学习笔记14-容器扩展点之PropertyPlaceholderConfigurer
-
Spring扩展点之Aware接口族
-
spring源码分析之扩展点BeanDefinitionRegistryPostProcessor
-
【Spring源码】- 04 扩展点之BeanFactoryPostProcessor
-
spring扩展点之BeanFactoryPostProcessor和BeanPostProcessor
-
springboot原理实战(3)-- spring扩展接口BeanPostProcessor,BeanFactoryPostProcessor