BeanPostProcessor和BeanFactoryPostProcessor的区别
官方文档:
在Spring核心的1.8章节
使用BeanPostProcessor自定义Bean
BeanPostProcessor 接口定义了您可以实现的回调方法,以提供您自己的(或覆盖容器的默认)实例化逻辑,依赖关系解析逻辑等。如果要在Spring容器完成实例化,配置和初始化bean之后实现某些自定义逻辑,则可以插入一个或多个 BeanPostProcessor 实现。
您可以配置多个 BeanPostProcessor 实例,并且可以通过设置 order 属性来控制这些 BeanPostProcessor 实例的执行顺序。你可以设置仅当BeanPostProcessor 实现 Ordered 接口时才具有此属性。如果你自己编写 BeanPostProcessor ,你也应该考虑实现 Ordered 接口。有关更多详细信息,请参阅 BeanPostProcessor 和 Ordered 接口的javadoc。
BeanPostProcessor实例在bean(或对象)实例上运行。也就是说,Spring IoC容器实例化一个bean实例,然后BeanPostProcessor实例完成它们的工作。
BeanPostProcessor 实例的范围是每个容器的范围。仅当您使用容器层次结构时,这才是相关的。如果在一个容器中定义 BeanPostProcessor ,则它仅对该容器中的bean进行后处理。换句话说,在一个容器中定义的bean不会被另一个容器中定义的 BeanPostProcessor 进行后处理,即使两个容器都是同一层次结构的一部分。
要更改实际的bean定义(即定义bean的蓝图),您需要使用 BeanFactoryPostProcessor
org.springframework.beans.factory.config.BeanPostProcessor 接口恰好包含两个回调方法。当这样的类被注册为带有容器的后处理器时,对于容器创建的每个bean实例,后处理器在容器初始化方法之前从容器中获取回调(例如 InitializingBean.afterPropertiesSet() ,在任何声明的 init 之后)方法)被调用,并在任何bean初始化后回调。后处理器可以对bean实例执行任何操作,包括完全忽略回调。 bean后处理器通常检查回调接口,或者它可以用代理包装bean。一些Spring AOP基础结构类实现为bean后处理器,以便提供代理包装逻辑。
ApplicationContext 自动检测在实现 BeanPostProcessor 接口的配置元数据中定义的任何bean。 ApplicationContext 将这些bean注册为后处理器,以便稍后在创建bean时调用它们。 Bean后处理器可以以与任何其他bean相同的方式部署在容器中。
请注意,在配置类上使用 @Bean factory方法声明 BeanPostProcessor 时,工厂方法的返回类型应该是实现类本身或至少是org.springframework.beans.factory.config.BeanPostProcessor 接口,清楚地表明该bean的后处理器性质。否则, ApplicationContext 无法在完全创建之前按类型自动检测它。由于需要提前实例化 BeanPostProcessor 以便应用于上下文中其他bean的初始化,因此这种早期类型检测至关重要。
以编程方式注册
BeanPostProcessor实例 虽然BeanPostProcessor注册的推荐方法是通过ApplicationContext自动检测(如前所述),但您可以使用addBeanPostProcessor方法以编程方式对ConfigurableBeanFactory注册它们。当您需要在注册前评估条件逻辑或甚至跨层次结构中的上下文复制Bean post处理器时,这非常有用。但请注意,以编程方式添加的BeanPostProcessor实例不尊重Ordered接口。这里,注册的顺序决定了执行的顺序。另请注意,以编程方式注册的BeanPostProcessor实例始终在通过自动检测注册的实例之前处理,而不管任何显式排序。
BeanPostProcessor实例和AOP自动代理
实现 BeanPostProcessor 接口的类是特殊的,容器会对它们进行不同的处理。作为 ApplicationContext 特殊启动阶段的一部分,它们直接引用的所有BeanPostProcessor 实例和bean都会在启动时实例化。接下来,所有 BeanPostProcessor 实例都以排序方式注册,并应用于容器中的所有其他bean。因为AOP自动代理是作为 BeanPostProcessor 本身实现的,所以 BeanPostProcessor 实例和它们直接引用的bean都不符合自动代理的条件,因此没有将方面编入其中。
对于任何此类bean,您应该看到一条信息性日志消息: Bean someBean is not eligible for getting processed by all BeanPostProcessor interfaces (for example: not eligible for auto-proxying) 。
如果您通过使用自动装配或 @Resource (可能会回退到自动装配)将bean连接到 BeanPostProcessor ,则Spring可能会在搜索类型匹配依赖项候选项时访问意外的bean,从而使它们不符合自动代理或其他类型的 beans 后处理。例如,如果您有一个使用 @Resource 注释的依赖项,其中字段或setter名称不直接对应于bean的声明名称且未使用name属性,则Spring会访问其他bean以按类型匹配它们。
以下示例显示如何在 ApplicationContext 中编写,注册和使用 BeanPostProcessor 实例。
示例:Hello World,BeanPostProcessor样式
第一个例子说明了基本用法。该示例显示了一个自定义 BeanPostProcessor 实现,它调用容器创建的每个bean的 toString() 方法,并将生成的字符串打印到系统控制台。
以下清单显示了自定义 BeanPostProcessor 实现类定义:
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
以下 beans 元素使用 InstantiationTracingBeanPostProcessor :
<?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:lang="http://www.springframework.org/schema/lang"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/lang
http://www.springframework.org/schema/lang/spring-lang.xsd">
<lang:groovy id="messenger"
script-source="classpath:org/springframework/scripting/groovy/Messenger.groovy">
<lang:property name="message" value="Fiona Apple Is Just So Dreamy."/>
</lang:groovy>
<!--
when the above bean (messenger) is instantiated, this custom
BeanPostProcessor implementation will output the fact to the system console
-->
<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
</beans>
注意 InstantiationTracingBeanPostProcessor 仅仅是如何定义的。它甚至没有名称,并且,因为它是一个bean,它可以像任何其他bean一样依赖注入。 (前面的配置还定义了一个由Groovy脚本支持的bean。Spring动态语言支持在 Headers 为 Dynamic Language Support 的章节中有详细说明。)
以下Java应用程序运行上述代码和配置:
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.scripting.Messenger;
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("scripting/beans.xml");
Messenger messenger = (Messenger) ctx.getBean("messenger");
System.out.println(messenger);
}
}
上述应用程序的输出类似于以下内容:
Bean 'messenger' created : [email protected]
[email protected]
示例:RequiredAnnotationBeanPostProcessor
将回调接口或注释与自定义 BeanPostProcessor 实现结合使用是扩展Spring IoC容器的常用方法。一个例子是Spring的RequiredAnnotationBeanPostProcessor - 一个带有Spring发行版的 BeanPostProcessor 实现,它确保用(任意)注释标记的bean上的JavaBean属性实际上(配置为)依赖注入值。
使用BeanFactoryPostProcessor自定义配置元数据
我们看到的下一个扩展点是 org.springframework.beans.factory.config.BeanFactoryPostProcessor 。此接口的语义类似于 BeanPostProcessor 的语义,但有一个主要区别: BeanFactoryPostProcessor 对bean配置元数据进行操作。也就是说,Spring IoC容器允许 BeanFactoryPostProcessor 读取配置元数据,并可能在容器实例化除 BeanFactoryPostProcessor 实例之外的任何bean之前更改它。
您可以配置多个 BeanFactoryPostProcessor 实例,并且可以通过设置 order 属性来控制这些 BeanFactoryPostProcessor 实例的运行顺序。但是,如果BeanFactoryPostProcessor 实现 Ordered 接口,则只能设置此属性。如果你自己编写 BeanFactoryPostProcessor ,你也应该考虑实现 Ordered 接口。有关更多详细信息,请参阅 BeanFactoryPostProcessor 和 Ordered 接口的javadoc。
如果要更改实际的bean实例(即从配置元数据创建的对象),则需要使用
BeanPostProcessor。虽然技术上可以在BeanFactoryPostProcessor中使用bean实例(例如,通过使用BeanFactory.getBean()),但这样做会导致过早的bean实例化,从而违反标准容器生命周期。这可能会导致负面影响,例如绕过bean后期处理。
此外, BeanFactoryPostProcessor 实例的范围是每个容器的范围。仅当您使用容器层次结构时,这才有意义。如果在一个容器中定义BeanFactoryPostProcessor ,则它仅应用于该容器中的bean定义。即使两个容器都是同一层次结构的一部分,一个容器中的Bean定义也不会被另一个容器中的BeanFactoryPostProcessor 实例进行后处理。
Bean工厂后处理器在 ApplicationContext 中声明时会自动执行,以便将更改应用于定义容器的配置元数据。 Spring包含许多预定义的bean工厂后处理器,例如 PropertyOverrideConfigurer 和 PropertyPlaceholderConfigurer 。您还可以使用自定义 BeanFactoryPostProcessor - 例如,注册自定义属性编辑器。
ApplicationContext 会自动检测部署到其中的任何实现 BeanFactoryPostProcessor 接口的bean。它在适当的时候使用这些bean作为bean工厂后处理器。您可以像处理任何其他bean一样部署这些后处理器bean。
与
BeanPostProcessor一样,您通常不希望为延迟初始化配置BeanFactoryPostProcessor。如果没有其他bean引用Bean(Factory)PostProcessor,则该后处理器根本不会被实例化。因此,将忽略将其标记为延迟初始化,即使您在<beans />元素的声明上将default-lazy-init属性设置为true,也会急切地实例化Bean(Factory)PostProcessor。
示例:类名替换PropertyPlaceholderConfigurer
您可以使用标准Java Properties 格式在单独的文件中使用 PropertyPlaceholderConfigurer 来外部化bean定义中的属性值。这样做可以使部署应用程序的人员自定义特定于环境的属性,例如数据库URL和密码,而不会出现修改主XML定义文件或容器文件的复杂性或风险。
请考虑以下基于XML的配置元数据片段,其中定义了带有占位符值的 DataSource :
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
该示例显示了从外部 Properties 文件配置的属性。在运行时, PropertyPlaceholderConfigurer 应用于替换DataSource的某些属性的元数据。要替换的值指定为 ${property-name} 形式的占位符,它遵循Ant和log4j以及JSP EL样式。
实际值来自标准Java Properties 格式的另一个文件:
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
因此, ${jdbc.username} 字符串在运行时被替换为值'sa',并且同样适用于与属性文件中的键匹配的其他占位符值。 PropertyPlaceholderConfigurer 检查bean定义的大多数属性和属性中的占位符。此外,您可以自定义占位符前缀和后缀。
使用Spring 2.5中引入的 context 命名空间,您可以使用专用配置元素配置属性占位符。您可以在 location 属性中以逗号分隔列表的形式提供一个或多个位置,如以下示例所示:
<context:property-placeholder location="classpath:com/something/jdbc.properties"/>
PropertyPlaceholderConfigurer 不仅在您指定的 Properties 文件中查找属性。默认情况下,如果它在指定的属性文件中找不到属性,它还会检查JavaSystem 属性。您可以通过使用以下三个受支持的整数值之一设置configurer的 systemPropertiesMode 属性来自定义此行为:
never(0):从不检查系统属性。fallback(1):如果在指定的属性文件中无法解析,则检查系统属性。这是默认值。override(2):在尝试指定的属性文件之前,首先检查系统属性。这使系统属性可以覆盖任何其他属性源。
有关更多信息,请参阅 PropertyPlaceholderConfigurer javadoc。
您可以使用
PropertyPlaceholderConfigurer替换类名,这在您必须在运行时选择特定实现类时有时很有用。以下示例显示了如何执行此操作:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<value>classpath:com/something/strategy.properties</value>
</property>
<property name="properties">
<value>custom.strategy.class=com.something.DefaultStrategy</value>
</property>
</bean>
<bean id="serviceStrategy" class="${custom.strategy.class}"/>
如果在运行时无法将类解析为有效类,则在将要创建bean时,bean的解析将失败,这在非延迟初始化bean的 preInstantiateSingletons() 阶段期间会失败。
示例:PropertyOverrideConfigurer
PropertyOverrideConfigurer ,另一个bean工厂后处理器,类似于 PropertyPlaceholderConfigurer ,但与后者不同,原始定义可以具有默认值或根本没有值用于bean属性。如果重写的 Properties 文件没有某个bean属性的条目,则使用默认的上下文定义。
请注意,bean定义不知道被覆盖,因此从XML定义文件中可以立即看出正在使用覆盖配置器。如果多个 PropertyOverrideConfigurer 实例为同一个bean属性定义了不同的值,则由于覆盖机制,最后一个获胜。
属性文件配置行采用以下格式:
beanName.property=value
以下清单显示了格式的示例:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
此示例文件可以与包含名为 dataSource 且具有 driver 和 url 属性的bean的容器定义一起使用。
也支持复合属性名称,只要路径的每个组件(重写的最终属性除外)都已经非空(可能由构造函数初始化)。在以下示例中, tom bean的 fred 属性的 bob属性的 sammy 属性设置为标量值 123 :
tom.fred.bob.sammy=123
使用Spring 2.5中引入的 context 命名空间,可以使用专用配置元素配置属性覆盖,如以下示例所示:
<context:property-override location="classpath:override.properties"/>
代码:
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory...");
int count = beanFactory.getBeanDefinitionCount();
String[] names = beanFactory.getBeanDefinitionNames();
System.out.println("当前BeanFactory中有"+count+" 个Bean");
System.out.println(Arrays.asList(names));
} }
@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor { @Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("MyBeanFactoryPostProcessor...postProcessBeanFactory...");
int count = beanFactory.getBeanDefinitionCount();
String[] names = beanFactory.getBeanDefinitionNames();
System.out.println("当前BeanFactory中有"+count+" 个Bean");
System.out.println(Arrays.asList(names));
} }
测试结果:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor,Ordered { @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean);
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// TODO Auto-generated method stub
System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean);
return bean;
} @Override
public int getOrder() {
return 2;
} }
@Configuration
@ComponentScan(value="com.atguigu.bean")
public class MainConfig3 { //给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
} }
测试:
public class MainTest {
@SuppressWarnings("resource")
public static void main(String[] args) {
// ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
// Person bean = (Person) applicationContext.getBean("person");
// System.out.println(bean);
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
}

BeanPostProcessor和BeanFactoryPostProcessor的区别的更多相关文章
- spring 后置处理器BeanFactoryPostProcessor和BeanPostProcessor的用法和区别
主要区别就是: BeanFactoryPostProcessor可以修改BEAN的配置信息而BeanPostProcessor不能,下面举个例子说明 BEAN类: package com.spring ...
- Spring的BeanPostProcessor和BeanFactoryPostProcessor区别
Spring提供了两种后处理bean的扩展接口,分别为BeanPostProcessor和BeanFactoryPostProcessor,这两者在使用上是有所区别的. BeanPostProcess ...
- 003-Spring4 扩展分析-spring类初始化@PostConstruct > InitializingBean > init-method、ApplicationContext、BeanPostProcessor、BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
一.spring类初始化@PostConstruct > InitializingBean > init-method InitializingBean接口为bean提供了初始化方法的方式 ...
- Spring中的BeanPostProcessor和BeanFactoryPostProcessor
BeanPostProcessor BeanFactoryPostProcessor 标准ioc容器初始化之后的后置处理器 BeanDefintionRegisterPostProcessor 在所有 ...
- beanPostProcessor与beanFactoryPostProcessor
BeanFactoryPostProcessor的典型应用:PropertyPlaceholderConfigurer BeanFactoryPostProcessor会在所有的bean配置载入之后执 ...
- Spring源码系列 — 容器Extend Point(一)
前言 前文介绍了Spring中的BeanDefinition的细节,随着Spring的启动流程,这节我们介绍Spring的后续处理过程 - Spring的扩展点: BeanFactoryPostPro ...
- 死磕Spring之IoC篇 - 深入了解Spring IoC(面试题)
该系列文章是本人在学习 Spring 的过程中总结下来的,里面涉及到相关源码,可能对读者不太友好,请结合我的源码注释 Spring 源码分析 GitHub 地址 进行阅读 Spring 版本:5.1. ...
- BeanFactory 和 ApplicationContext的区别
今天在网上查资料无意中看到这一行代码 BeanFactory factory = new ClassPathXmlApplicationContext("applicationContext ...
- Spring中ApplicationContext和beanfactory区别---解析一
BeanFacotry是spring中比较原始的Factory.如XMLBeanFactory就是一种典型的BeanFactory.原始的BeanFactory无法支持spring的许多插件,如AOP ...
随机推荐
- flask response 详解
from flask import Flask,Response,jsonify #Flask = werkzeug(处理网络的) + sqlalchemy(处理数据库的) + jinja2 (处理模 ...
- Codeforces Round #590 (Div. 3)(e、f待补
https://codeforces.com/contest/1234/problem/A A. Equalize Prices Again #include<bits/stdc++.h> ...
- js知识点——1
onload 事件会在页面或图像加载完成后立即发生. document.write("内容")将在加载页面时输出 内容可以是什么,可以是一个标签,它输出的文件不能自动换行: < ...
- ubuntu 安装 Anaconda2和3的tips
Anaconda 2 3 安装tips 安装anaconda2 我们要下载Anaconda2-4.3.0-Linux-x86_64.sh安装文件 下载好之后,在文件路径下执行以下命令: bash An ...
- mybatis映射文件mapper详解
mapper.xml映射文件主要是用来编写sql语句的,以及一些结果集的映射关系的编写,还有就是缓存的一些配置等等. 1.<select>元素 <select>元素就是sql查 ...
- 阿里云CentOs服务器 安装与配置mysql数据库
阿里云CentOs服务器 安装与配置mysql数据库 以上为Linux安装mysql数据库 Linux 安装mysql 数据库 一下为mysql 安装教程 Using username "r ...
- 使用CXF开发WebService程序的总结(三):创建webservice客户端
1.创建一个maven子工程 ws_client,继承父工程 1.1 修改父工程pom配置 <modules> <module>ws_server</module> ...
- Linux学习--第十三天--日志、系统运行级别、grub加密
日志 rsyslogd取代了syslogd. /var/log/cron #定时任务相关日志 /var/log/cups #打印信息相关日志 /var/log/dmesg #开机内核自检相关日志,dm ...
- IBM产品系列和AIX系统版本
AIX系统版本 AIX 7.2 No supported AIX levels. AIX 7.1 Technology Level Base Level Recommended L ...
- hdu 1003 最大连续子串
#include <bits/stdc++.h> #define PI acos(-1.0) #define mem(a,b) memset((a),b,sizeof(a)) #defin ...