博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
spring扩展点之一:BeanFactoryPostProcessor和BeanPostProcessor
阅读量:6995 次
发布时间:2019-06-27

本文共 9425 字,大约阅读时间需要 31 分钟。

一、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的实例,需要重载

Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

还有一点区别就是BeanFactoryPostProcessor的回调比BeanPostProcessor要早。

 

二、BeanPostProcessors

1、bean生成过程

首先回顾下bean的生命周期如下图:

 

2、BeanPostProcessors接口

public interface BeanPostProcessor {        /**      * Apply this BeanPostProcessor to the given new bean instance before any bean      * initialization callbacks (like InitializingBean's {
@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. */   //实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务 Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException; /** * Apply this BeanPostProcessor to the given new bean instance after any bean * initialization callbacks (like InitializingBean's {
@code afterPropertiesSet} * or a custom init-method). The bean will already be populated with property values. */   //实例化、依赖注入、初始化完毕时执行 Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException; }

如果这个接口的某个实现类被注册到某个容器,那么该容器的每个受管Bean在调用初始化方法的前后,都会获得该接口实现类的一个回调。容器调用接口定义的方法时会将该受管Bean的实例和名字通过参数传入方法,进过处理后通过方法的返回值返回给容器。

要使用BeanPostProcessor回调,就必须先在容器中注册实现该接口的类,那么如何注册呢?BeanFactory和ApplicationContext容器的注册方式不大一样:

  • 若使用BeanFactory,则必须要显示的调用其addBeanPostProcessor()方法进行注册,参数为BeanPostProcessor实现类的实例;
  • 如果是使用ApplicationContext,那么容器会在配置文件在中自动寻找实现了BeanPostProcessor接口的Bean,然后自动注册,我们要做的只是配置一个BeanPostProcessor实现类的Bean就可以了

假如我们使用了多个的BeanPostProcessor的实现类,那么如何确定处理顺序呢?其实只要实现Ordered接口,设置order属性就可以很轻松的确定不同实现类的处理顺序了。

3、示例

3.1 applicationContext.xml

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;      }  }

3.4 测试方法

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接口类似,只时回调时机不同。

 三、与BeanFactoryPostProcessor接口的区别

BeanFactoryPostProcessor:允许自定义对ApplicationContext的 bean definitions 进行修饰,扩展功能。 

1、实现BeanFactoryPostProcessor 接口,会被Application contexts自动发现 
2、BeanFactoryPostProcessor 仅仅对 bean definitions 发生关系,不能对bean instances 交互,对bean instances 的交互,由BeanPostProcessor的实现来处理 
3、PropertyResourceConfigurer 是一个典型的实现
 (PropertyResourceConfigurer是BeanFactoryPostProcessor的一个实现)

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接口实现类可以在当前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配置

重新运行,结果

BeanFactoryPostProcessor doing

User{name='新名字', id=100}

示例2:

有这样的也个业务场景: 
spring支持系统对username_进行占位符的配置为properties文件配置,试想如果我们有个配置中心,我们希望spring启动的时候,从远程配置中心取数据,而非本地文件,这里就需要我们自定义一个实现BeanFactoryPostProcessor的PropertyResourceConfigurer 实例。 
看下面的例子: 
bean.xml配置: 
模拟从远程取文件: 
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
list = new ArrayList
(); Properties p = PropertiesLoaderUtils.loadAllProperties("config.properties"); list.add(p); //这里是关键,这就设置了我们远程取得的List
列表 setPropertiesArray(list.toArray(new Properties[list.size()])); } }
javaBean配置: 
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);      }    }

 

 

 

 

转载地址:http://olbvl.baihongyu.com/

你可能感兴趣的文章
ML 02、监督学习
查看>>
兄弟郊游问题
查看>>
UltraEdit打开中文乱码
查看>>
Linux输出重定向
查看>>
Oracle 数据恢复指导具体解释
查看>>
ArcPad 10 的安装部署
查看>>
Spring 注解Autowired自动注入bean异常解决
查看>>
一个睡五分钟等于六个钟头的方法
查看>>
Material Designer的低版本兼容实现(五)—— ActivityOptionsCompat
查看>>
Mysql监控工具小集合
查看>>
POJ 1654 Area 计算几何
查看>>
Linux下Nginx+Tomcat整合的安装与配置
查看>>
Python的安装和详细配置(转)
查看>>
FloatingActionButton
查看>>
[再寄小读者之数学篇](2014-11-24 Abel 定理)
查看>>
iText导出pdf、word、图片
查看>>
android脚步---不同界面之间切换
查看>>
降压转换器 (Buck)
查看>>
SQL点滴26—常见T-SQL面试解析
查看>>
Wami Map Project – 开源的 OSM API 服务
查看>>