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 ...
随机推荐
- Android自动化测试(UiAutomator)
一.一个BUG引发的问题 如果研发过程中有一个BUG:“不断的切换手机语言出现花屏现象”.这个问题我们如何验证呢?我想,最好的方式应该是自动化测试. 那么,自动化测试可以完成哪些任务呢? ...
- IF-ELSE嵌套练习
一,1,编写程序,由键盘输入三个整数分别存入变量num1,num2,num3中,对它们进行排序,使用if-else结构,并按从小到大的顺序输出: package practice; import ja ...
- linux shutdown 命令 关机 重启
关机 shutdown -h now 重启 shutdown -r now
- Laravel5.8自定义函数存放位置
1. 创建文件 app/helpers.php <?php // 示例函数 function foo() { return "foo"; } 2. 修改项目 composer ...
- PythonWeb框架Django搭建过程
首先下载PyCharm专业版 破解地址:https://www.52pojie.cn/thread-997094-1-1.html 之后创建python虚拟环境(创建虚拟环境在上一篇博客) 激活虚拟环 ...
- Python 从大型csv文件中提取感兴趣的行
帮妹子处理一个2.xG 大小的 csv文件,文件太大,不宜一次性读入内存,可以使用open迭代器. with open(filename,'r') as file # 按行读取 for line in ...
- 关于在docker中配置elasticsearch容器的方法
一.关于docker的安装,注意几点 1.如果系统是Win10家庭版,是没有Hyper-V的,所以无法安装docker(运行docker安装包会报错),为此docker官网提供的解决方法是安装dock ...
- Linux上安装JDK1.8,tomcat9,以及mysql8的步骤
(该篇是在centos7上安装JDK1.8.0_201 tomcat9.0.16 和 mysql8.0.15) 一.安装JDK 方式一 1.首先,下载JDK(链接http://www.oracle. ...
- ArrayList知识详解
简介 ArrayList是Java集合常用的数据结构之一,继承自AbstractList,实现了List,RandomAccess.Cloneable.Serializable等一系列接口,支持快速访 ...
- python virtualenv和virtualenv的使用
首先下面的步骤我都是在windows下执行,Python2.7,pip==19.1.1 1.安装virtualenv pip install virtualenv 2.新建virtualenv vir ...