当前位置: 代码迷 >> 综合 >> Spring容器扩展(BeanPostProcessor,BeanFactoryPostProcessor,FactoryBean)—Spring官方文档系列
  详细解决方案

Spring容器扩展(BeanPostProcessor,BeanFactoryPostProcessor,FactoryBean)—Spring官方文档系列

热度:15   发布时间:2023-12-12 09:36:11.0


一、使用BeanPostProcessor定制bean

       

BeanPostProcessor接口定义了回调方法,你可以实现它来提供你自己的(或重载容器的默认)实例化逻辑,依赖解析逻辑等等。如果你想在Spring容器完成实例化,配置和初始化bean之后实现一些自定义逻辑,可以插入一个或多个BeanPostProcessor实现。

你可以配置多个BeanPostProcessor实例,你可以通过设置order属性来控制这些BeanPostProcessor的执行顺序。只有当BeanPostProcessor实现了“Ordered”接口时,才可以设置此属性;如果你写自己的BeanPostProcessor,你应该考虑实现“Ordered”接口。

BeanPostProcessor操作bean(或对象)实例; 也就是说,Spring IoC容器实例化一个bean实例,然后BeanPostProcessor做他们的工作.BeanPostProcessor的作用域是每个容器。 这仅在使用容器层次结构时才会用到这个。 如果你在一个容器中定义一个BeanPostProcessor,它将只对该容器中的bean进行后处理。 换句话说,在一个容器中定义的bean不会被另一个容器中定义的BeanPostProcessor进行后处理,即使两个容器都是同一层次结构的一部分。

org.springframework.beans.factory.config.BeanPostProcessor接口恰好包含两个回调方法。当这样的类在容器内注册为post-processor,对于由容器创建的每个bean实例,容器创建所有bean,在容器初始化方法(比如InitializingBeanafterProperieSet()方法和其他所有的声明的init方法)和所有bean 初始化回调之前,运行post-processor回调。post-processor可以对bean实例采取任何操作,包括完全忽略回调。 bean post-processor通常检查回调接口或者可以用代理包装bean。一些Spring AOP基础结构类被实现为bean post-processor,以便提供代理包装逻辑。

一个ApplicationContext 自动检测在配置元数据中定义的实现BeanPostProcessor接口的任何bean。 ApplicationContext将这些bean注册为后处理器,以便稍后在创建bean时调用它们。 Bean后处理器可以像任何其他bean一样部署在容器中(直接使用<bean>)。

注意,当在配置类上使用@Bean工厂方法声明BeanPostProcessor时,工厂方法的返回类型应该是实现类本身或至少是org.springframework.beans.factory.config.BeanPostProcessor接口,清楚地表明该bean的后处理器性质。否则,ApplicationContext将不能在完全创建它之前通过类型自动检测它。因为BeanPostProcessor需要尽早的实例化,以便应用于上下文中其他bean的初始化,所以这种尽早的类型检测是至关重要的。


二、使用BeanFactoryPostProcessor定制配置元数据  

    我们将要看到的下一个扩展点是“org.springframework.beans.factory.config.BeanFactoryPostProcessor”。这个接口的语义类似于BeanPostProcessor,其中一个主要区别是:BeanFactoryPostProcessor对* bean配置元数据*操作;也就是说,Spring IoC容器允许BeanFactoryPostProcessor读取配置元数据,并可能在容器实例化(除BeanFactoryPostProcessor之外的任何)bean之前改变它。

你可以配置多个BeanFactoryPostProcessors,你可以通过设置orderproperty来控制这些BeanFactoryPostProcessor的执行顺序。但是,如果BeanFactoryPostProcessor实现了 Ordered接口,则只能设置此属性。如果你写自己的“BeanFactoryPostProcessor”,你应该考虑实现 Ordered接口。

如果你想改变实际的bean 实例(即从配置元数据创建的对象),那么你需要使用一个BeanPostProcessor。虽然在技术上可以使用BeanFactoryPostProcessor(例如,使用BeanFactory.getBean())中的bean实例,这样做会导致提前的bean实例化,违反标准容器生命周期。这可能导致负面的副作用,如绕过bean后处理。此外,BeanFactoryPostProcessors的作用域也是在各自的容器内。这仅在使用容器层次结构时才相关。如果你在一个容器中定义一个BeanFactoryPostProcessor,它只会应用于该容器中的bean定义。一个容器中的Bean定义不会被另一个容器中的BeanFactoryPostProcessor进行后置处理,即使这两个容器都是同一层次结构的一部分。

接下里用代码来看看BeanPostProcessor和BeanFactoryPostProcessor

<!-- ContainnerDevelopment --><!-- 支持Spring注解 --><bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" /><!-- 注册一个BeanPostProcessor --><bean id="postProcessor" class="com.jay.document.containnerDevelop.PostProcessor"/><!-- 注册一个BeanFactoryPostProcessor --><bean id="factoryPostProcessor" class="com.jay.document.containnerDevelop.FactoryPostProcessor"/><!-- 普通bean --><bean id="beanFactoryPostProcessorTest" class="com.jay.document.containnerDevelop.BeanFactoryPostProcessorTest"><property name="name" value="张三"/><property name="sex" value="男"/></bean>

上面的postProcessor实现了BeanPostProcessor接口,FactoryPostProcessor实现了BeanFactoryPostProcessor。

postProcessor:

public class PostProcessor implements BeanPostProcessor {@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {System.out.println("后置处理器AfterInitialization()方法处理bean=【"+beanName+"】 开始");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return bean;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {System.out.println("后置处理器BeforeInitialization()方法处理bean=【"+beanName+"】完毕!");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}return bean;}}

FactoryPostProcessor:

public class FactoryPostProcessor implements BeanFactoryPostProcessor {@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {System.out.println("--->调用了BeanFactoryPostProcessor");String[] beanStr = configurableListableBeanFactory.getBeanDefinitionNames();for (String beanName : beanStr) {//这里输出  看看Bean配置的顺序 System.out.println("循环的beanName-->>"+beanName);// 这里只改变beanFactoryPostProcessorTest的属性if ("beanFactoryPostProcessorTest".equals(beanName)) {BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition(beanName);MutablePropertyValues m = beanDefinition.getPropertyValues();System.out.println("--->>>"+m.getPropertyValueList().toString());if (m.contains("name")) {m.addPropertyValue("name", "李白");System.out.println("--->修改了name属性初始值了");}}}}
}

BeanFactoryPostProcessorTest:

public class BeanFactoryPostProcessorTest implements InitializingBean,DisposableBean,BeanNameAware,BeanFactoryAware{private String name;private String sex;public String getName() {return name;}//getter  setter@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {System.out.println("-->调用了BeanFactoryAware的setBeanFactory方法了");}@Overridepublic void setBeanName(String arg0) {System.out.println("-->调用了BeanNameAware的setBeanName方法了");}@Overridepublic void destroy() throws Exception {System.out.println("-->调用了DisposableBean的destroy方法了");       }@Overridepublic void afterPropertiesSet() throws Exception {System.out.println("-->调用了Initailization的afterPropertiesSet方法了");}@Overridepublic String toString() {return "BeanFactoryPostProcessorTest [name=" + name + ", sex=" + sex + "]";}
}

Test:

public static void main(String[] args) {System.out.println("-->Spring ApplicationContext容器开始初始化了......");ApplicationContext applicationcontext= new ClassPathXmlApplicationContext("applicationContext.xml");System.out.println("-->Spring ApplicationContext容器初始化完毕了......");//BeanLifecycle beanLifecycle =applicationcontext.getBean("beanLifecycle",BeanLifecycle.class);BeanFactoryPostProcessorTest beanFactoryPostProcessorTest=applicationcontext.getBean(BeanFactoryPostProcessorTest.class);System.out.println(beanFactoryPostProcessorTest.toString());}

运行结果 :

-->Spring ApplicationContext容器开始初始化了......

--->调用了BeanFactoryPostProcessor  // 调用了 FactoryPostProcessor的postProcessBeanFactory方法

循环的beanName-->>myAware   //这是通过@component注解配置的bean  在读取配置是在第一个
循环的beanName-->>org.springframework.context.annotation.internalConfigurationAnnotationProcessor
循环的beanName-->>org.springframework.context.annotation.internalAutowiredAnnotationProcessor
循环的beanName-->>org.springframework.context.annotation.internalRequiredAnnotationProcessor
循环的beanName-->>org.springframework.context.annotation.internalCommonAnnotationProcessor
循环的beanName-->>container   //这是通过xml配置的bean  
循环的beanName-->>atList        //这是也通过xml配置的bean    顺序是按照配置的顺序
循环的beanName-->>org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#0
循环的beanName-->>postProcessor
循环的beanName-->>factoryPostProcessor
循环的beanName-->>beanFactoryPostProcessorTest  // 这里可以看出 在bean没有实例化之前 ,就改变了 bean的属性配置 

--->>>[bean property 'name', bean property 'sex']    //  BeanFactoryPostProcessor  是对读取每个bean的配置文件都有效

--->修改了name属性初始值了                                        //

循环的beanName-->>org.springframework.context.annotation.ConfigurationClassPostProcessor.importAwareProcessor
循环的beanName-->>org.springframework.context.annotation.ConfigurationClassPostProcessor.enhancedConfigurationProcessor
后置处理器BeforeInitialization()方法处理bean=【myAware】完毕 // BeanPostProcessor 同样是对读取每个bean的配置文件都有效
后置处理器AfterInitialization()方法处理bean=【myAware】 开始
后置处理器BeforeInitialization()方法处理bean=【atList】完毕!
后置处理器AfterInitialization()方法处理bean=【atList】 开始
后置处理器BeforeInitialization()方法处理bean=【container】完毕!
后置处理器AfterInitialization()方法处理bean=【container】 开始
-->调用了BeanNameAware的setBeanName方法了
-->调用了BeanFactoryAware的setBeanFactory方法了//这里可以看出 BeanPostProcessor 的BeforeInitialization方法 执行                                                                                            //在 Initailization的afterPropertiesSet方法之前
后置处理器BeforeInitialization()方法处理bean=【beanFactoryPostProcessorTest】完毕!
-->调用了Initailization的afterPropertiesSet方法了
后置处理器AfterInitialization()方法处理bean=【beanFactoryPostProcessorTest】 开始
-->Spring ApplicationContext容器初始化完毕了......

BeanFactoryPostProcessorTest [name=李白, sex=男] // 属性已经改变

三、 使用FactoryBean定制实例化逻辑

    

对象实现org.springframework.beans.factory.FactoryBean接口,则成为它本身的工厂。

FactoryBean接口是Spring IoC容器实例化逻辑的扩展点。假如初始化代码非常复杂,此时使用java编码比使用XML配置更容易表达。这种场景中,你可以创建自己的FactoryBean,在该类中编写复杂的初始化程序,然后将你的自定义FactoryBean插入到容器。

FactoryBean接口提供了三种方法:

  • Object getObject():返回此工厂创建的对象的实例。实例可以共享,这取决于这个工厂是返回单例还是原型。
  • boolean isSingleton():如果这个“FactoryBean”返回单例,则返回true,否则返回false。
  • 类getObjectType():返回由getObject()方法或null返回的对象类型,如果类型不是预先知道的。

FactoryBean概念和接口在Spring框架中的许多地方使用; Spring内置的有超过50个实现。

当你需要向容器请求一个实际的FactoryBean实例本身而不是它生成的bean时,在调用ApplicationContext的getBean()方法时,用符号(&)作为前缀。所以对于给定的FactoryBean,id为myBean,在容器上调用getBean(“myBean”)会返回FactoryBean所产生的bean;而调用getBean(“&myBean”)返回FactoryBean实例本身。


FactoryBean的核心就在于通过getObject方法可以获取的是它所生产的对象,所以我们在Proxy创建代理对象的时候就比较方便。还有一些bean,如果通过配置的方式,会显得比较麻烦和复杂,那么这时候适当的采用编码方式在某些场合下还是挺不错的。

给个小例子(来自此博)

  1. </span>public interface FactoryBean<T> {  
  2.         //返回由FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例会放到Spring容器中单实例缓存池中。  
  3.        T getObject() throws Exception;    
  4.        //返回FactoryBean创建的bean类型。  
  5.        Class<?> getObjectType();  
  6.        //返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。  
  7.        boolean isSingleton();      
  8.     }  
  1. public class Person {  
  2.     private String name;  
  3.     private String address;  
  4.     private int age;  
  5. }  
  1. <bean id="personBean" class="com.gh.MavenTest.Person">  
  2.     <property name="name" value="gh1" />  
  3.     <property name="address" value="address1" />  
  4.     <property name="age" value="28" />  
  5. </bean>  

那么现在我们可以通过getBean("personBean")来获取该对象。那么我们来看下如果通过实现FactoryBean以后该怎么写呢?来看下我们的PersonFactoryBean的代码:

  1. public class PersonFactoryBean implements FactoryBean<Person>{  
  2.       
  3.     private String personInfo;  
  4.       
  5.     public Person getObject() throws Exception {  
  6.         Person person =  new  Person () ;      
  7.         String []  infos =  personInfo.split ( "," ) ;  
  8.         person.setName(infos[0]);  
  9.         person.setAddress(infos[1]);  
  10.         person.setAge(Integer.parseInt(infos[2]));  
  11.         return person;  
  12.     }  
  13.   
  14.     public Class<Person> getObjectType() {  
  15.         return Person.class;  
  16.     }  
  17.   
  18.     public boolean isSingleton() {  
  19.         return true;  
  20.     }  
  21. }  

我们看到,这里PersonFactoryBean实现了FactoryBean接口,那么自然也要实现它定义的方法。这里我们是通过一个personInfo字符串解析得到Person对象,那么我们在配置Spring的时候就可以这么配置:

  1. <bean id="personFactory" class="com.hik.MavenTest.PersonFactory">  
  2.     <property name="personInfo" value="gh2,address2,22"></property>  
  3. </bean>   
通过上面的小案例的代码,我们可以看到如果一个类实现了FactoryBean接口,那么getBean得到的不是他本身了,而是它所产生的对象,如果我们希望得到它本身,只需要加上&符号即可。至于FactoryBean的实际应用,需要大家去发现理解。
  相关解决方案