我是靠谱客的博主 开放皮带,最近开发中收集的这篇文章主要介绍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);  
    }  
  
}

 

最后

以上就是开放皮带为你收集整理的spring扩展点之BeanFactoryPostProcessor和BeanPostProcessor的全部内容,希望文章能够帮你解决spring扩展点之BeanFactoryPostProcessor和BeanPostProcessor所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(48)

评论列表共有 0 条评论

立即
投稿
返回
顶部