SpringFramework核心技术一(IOC:Spring容器的拓展点)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: Spring容器的拓展点通常,应用程序开发人员不需要ApplicationContext 实现类的子类。相反,Spring IoC容器可以通过插入特殊集成接口的实现来扩展。

Spring容器的拓展点

通常,应用程序开发人员不需要ApplicationContext 实现类的子类。相反,Spring IoC容器可以通过插入特殊集成接口的实现来扩展。接下来的几节将介绍这些集成接口。

一、使用BeanPostProcessor定制bean

1.BeanPostProcessor概念讲解

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

您可以配置多个BeanPostProcessor实例,并且可以BeanPostProcessor通过设置order属性来控制这些实例的执行顺序。只有在BeanPostProcessor实现Ordered接口时才可以设置此属性; 如果你自己写,BeanPostProcessor你应该考虑实现Ordered 接口。有关更多详细信息,请参阅BeanPostProcessor和 Ordered接口的javadocs 。另请参阅以下关于s的程序注册BeanPostProcessor的注释 。

BeanPostProcessors对bean(或对象)实例进行操作 ; 也就是说,Spring IoC容器实例化一个bean实例,然后 BeanPostProcessor完成他们的工作。
BeanPostProcessors是每个容器的作用域。这只有在使用容器层次结构时才有意义。如果您BeanPostProcessor在一个容器中定义了一个容器,它将只对该容器中的bean进行后处理。换句话说,在一个容器中定义的bean不会被BeanPostProcessor另一个容器中定义的后处理,即使这两个容器都是同一层次结构的一部分。
要更改实际的bean定义(即定义bean 的蓝图),您需要使用a BeanFactoryPostProcessor,如 使用BeanFactoryPostProcessor定制配置元数据中所述。

2.BeanPostProcessor回调方法

该org.springframework.beans.factory.config.BeanPostProcessor接口恰好包含两个回调方法。当这样的类被注册为容器的后处理器时,对于容器创建的每个bean实例,后处理器都会在容器初始化方法(如InitializingBean的afterPropertiesSet()之前)和容器声明的init方法)以及任何bean初始化回调之后被调用。后处理器可以对bean实例执行任何操作,包括完全忽略回调。一个bean后处理器通常检查回调接口,或者可能用一个代理包装一个bean。一些Spring AOP基础设施类被实现为bean后处理器,以提供代理包装逻辑。
这里写图片描述

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

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

以编程方式注册BeanPostProcessors
而对于建议的方法BeanPostProcessor注册是通过 ApplicationContext自动检测(如上文所述),但也可以注册它们编程对一个ConfigurableBeanFactory使用 addBeanPostProcessor方法。当需要在注册之前评估条件逻辑,或者甚至跨层次结构中的上下文复制Bean后处理器时,这会非常有用。但请注意,BeanPostProcessor以编程方式添加的s 不尊重Ordered接口。这是注册顺序决定执行顺序。还要注意,BeanPostProcessor通过编程注册的程序总是在通过自动检测注册的程序之前进行处理,而不管任何明确的排序。
BeanPostProcessors和AOP自动代理
实现BeanPostProcessor接口的类是特殊的,并且容器对其进行不同的处理。它们直接引用的所有BeanPostProcessors 和bean都会在启动时实例化,作为特殊启动阶段的一部分 ApplicationContext。接下来,所有BeanPostProcessors以已排序的方式注册并应用于容器中的所有其他bean。由于AOP自动代理是作为一个BeanPostProcessor自身实现的,所以BeanPostProcessor它们不直接引用的bean都有资格进行自动代理,因此没有编入它们的方面。
对于任何这样的bean,您应该看到一条信息性日志消息:“ Bean foo不适合通过所有BeanPostProcessor接口进行处理(例如:不适合自动代理) ”。
请注意,如果您的bean已连接到BeanPostProcessor使用自动装配或 @Resource(可能会回退到自动装配),Spring可能会在搜索类型匹配的依赖关系候选时访问意外的bean,因此使它们不适用于自动代理或其他类型的bean post -处理。例如,如果你有一个依赖注释,@Resource其中的字段/设置者名称并不直接与bean的声明名称相对应,并且没有使用名称属性,那么Spring将访问其他bean以便按类型匹配它们。

二、如何使用BeanPostProcessor自定义一个Bean

例如:Hello World,BeanPostProcessor风格
这第一个例子说明了基本用法。该示例显示了一个自定义 BeanPostProcessor实现,它调用toString()每个bean 的方法,因为它是由容器创建的,并将生成的字符串打印到系统控制台。
在自定义BeanPostProcessor实现类定义下面查找:

  • 写一个InstantiationTracingBeanPostProcessor实现了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;
    }
}
  • 定义一个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: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动态语言支持在标题为动态语言支持的章节中有详细介绍 。)

以下简单的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:org.springframework.scripting.groovy.GroovyMessenger@272961 
org.springframework.scripting.groovy.GroovyMessenger@272961

示例:RequiredAnnotationBeanPostProcessor
将回调接口或注释与定制BeanPostProcessor实现结合使用 是扩展Spring IoC容器的常用方法。Spring的一个例子是RequiredAnnotationBeanPostProcessor一个BeanPostProcessor随Spring发布版一起发布的 实现,它确保Bean标记为(任意)注释的JavaBean属性实际上(配置为)依赖注入一个值。

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

1.概念和定义

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

您可以配置多个BeanFactoryPostProcessors,并且可以BeanFactoryPostProcessor通过设置order属性来控制这些s的执行顺序。但是,如果BeanFactoryPostProcessor实现 Ordered接口,则只能设置此属性。如果你自己写BeanFactoryPostProcessor,你应该考虑实现Ordered接口。有关更多详细信息,请查阅BeanFactoryPostProcessor和Ordered接口的javadocs 。

如果您想更改实际的bean 实例(即从配置元数据创建的对象),则需要使用BeanPostProcessor (如上所述,使用BeanPostProcessor定制bean)。虽然技术上可以在一个BeanFactoryPostProcessor(例如使用 BeanFactory.getBean())bean中使用bean实例,但这样做会导致bean过早实例化,从而违反标准容器生命周期。这可能会导致负面影响,如绕过豆后处理。
另外,每个容器BeanFactoryPostProcessor都有作用域。这只有在使用容器层次结构时才有意义。如果您在一个容器中定义一个容器,它将只应用于该容器中的bean定义。一个容器中的Bean定义不会由另一个容器中的s 后处理,即使两个容器都是同一层次结构的一部分。BeanFactoryPostProcessorBeanFactoryPostProcessor。

一个bean工厂后处理器在其中声明时会自动执行 ApplicationContext,以便将更改应用于定义容器的配置元数据。Spring包含许多预定义的bean工厂后处理器,比如PropertyOverrideConfigurer和 PropertyPlaceholderConfigurer。自定义BeanFactoryPostProcessor也可使用,例如,以注册自定义属性编辑器。
一个ApplicationContext自动检测部署在它实现了任何bean BeanFactoryPostProcessor接口。它在适当的时候使用这些bean作为bean工厂后处理器。您可以像任何其他bean一样部署这些后处理器bean。

和BeanPostProcessors一样,你通常不想配置 BeanFactoryPostProcessors进行延迟初始化。如果没有其他bean引用a Bean(Factory)PostProcessor,那么后处理器根本就不会被实例化。因此,将其标记为延迟初始化将被忽略,并且Bean(Factory)PostProcessor即使您将该default-lazy-init属性设置true为您的<beans />元素声明时,它 也会被急切实例化 。

四、下面来看一个例子

1. 类名替换PropertyPlaceholderConfigurer

您可以PropertyPlaceholderConfigurer使用标准Java Properties格式将bean定义中的属性值在单独的文件中进行外部化。通过这样做,部署应用程序的人员可以自定义特定于环境的属性,如数据库URL和密码,而无需修改容器的主XML定义文件或文件的复杂性或风险。

2.基于有XML的配置

考虑以下基于XML的配置元数据片段,其中DataSource 带有占位符值的定义。该示例显示了从外部Properties文件配置的属性。在运行时,将PropertyPlaceholderConfigurer应用于将替换DataSource的某些属性的元数据。要替换的值被指定为遵循Ant / log4j / JSP EL样式的表单的占位符${property-name}。

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations" value="classpath:com/foo/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>

3.实际值来自标准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定义的属性占位符检查。此外,占位符前缀和后缀可以自定义。

4.使用contextSpring 2.5中引入的命名空间

使用contextSpring 2.5中引入的命名空间,可以使用专用配置元素配置属性占位符。一个或多个位置可以作为location属性中的逗号分隔列表提供。

<context:property-placeholder location="classpath:com/foo/jdbc.properties"/>

5.PropertyPlaceholderConfigurer

在PropertyPlaceholderConfigurer不仅将查找在属性Properties 指定的文件。默认情况下,它也检查Java System属性,如果它无法在指定的属性文件中找到属性。您可以通过systemPropertiesMode使用以下三个支持的整数值之一来设置配置器的属性来自定义此行为:

  • 从不(0):从不检查系统属性
  • 回退(1):如果不能在指定的属性文件中解析,请检查系统属性。这是默认设置。
  • 重写(2):在尝试指定的属性文件之前,首先检查系统属性。这允许系统属性覆盖任何其他属性源。

请查阅PropertyPlaceholderConfigurerjavadoc以获取更多信息。
您可以使用PropertyPlaceholderConfigurer替换类名称,这在您需要在运行时选择特定实现类时有时很有用。例如:

<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations">
        <value>classpath:com/foo/strategy.properties</value>
    </property>
    <property name="properties">
        <value>custom.strategy.class=com.foo.DefaultStrategy</value>
    </property>
</bean>

<bean id="serviceStrategy" class="${custom.strategy.class}"/>

如果无法在运行时将该类解析为有效的类,那么在即将创建非bean类的非懒惰初始化preInstantiateSingletons() 阶段期间,bean的解析将失败ApplicationContext。

6.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的bean ,该bean 具有驱动程序和url属性。
只要路径中除最终属性被重写的每个组件都已经非空(可能由构造函数初始化),也支持复合属性名称。在这个例子中…

foo.fred.bob.sammy = 123

在sammy该财产bob的财产fred的财产foobean被设置为标量值123。

指定的覆盖值总是文字值; 它们不会被翻译成bean引用。当XML bean定义中的原始值指定一个bean引用时,这个约定也适用。

通过contextSpring 2.5中引入的命名空间,可以使用专用配置元素配置属性覆盖:

<context:property-override location="classpath:override.properties"/>

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

org.springframework.beans.factory.FactoryBean为自己工厂的对象实现接口 。
该FactoryBean接口是Spring IoC容器实例化逻辑的可插入点。如果你有复杂的初始化代码,用Java来表达更好,而不是(可能)冗长的XML,你可以创建自己的代码 FactoryBean,在该类中编写复杂的初始化代码,然后将自定义FactoryBean插入到容器中。

该FactoryBean界面提供了三种方法:

  • Object getObject():返回这个工厂创建的对象的一个​​实例。这个实例可能是共享的,这取决于这个工厂是否返回单例或原型。

  • boolean isSingleton():true如果FactoryBean返回单例,false则返回; 否则返回 。

  • Class getObjectType():返回该getObject()方法返回的对象类型,或者null如果该类型未预先知道。

这个FactoryBean概念和接口在Spring框架的许多地方都有使用; 该FactoryBean接口的50多个实现与Spring本身一起提供。
当你需要向一个实际的FactoryBean实例本身而不是它产生的bean 请求一个容器&时,在调用该getBean()方法的时候,用&符号()来标记该bean的id ApplicationContext。因此,对于给定FactoryBean 的id myBean,getBean(“myBean”)在容器上调用返回的产品FactoryBean; 而调用getBean(“&myBean”)返回 FactoryBean实例本身。

好啦,本节的Spring容器的扩展点就讲到这里啦!

目录
相关文章
|
1月前
|
Java 数据库连接 API
【Spring】1、Spring 框架的基本使用【读取配置文件、IoC、依赖注入的几种方式、FactoryBean】
【Spring】1、Spring 框架的基本使用【读取配置文件、IoC、依赖注入的几种方式、FactoryBean】
49 0
|
5天前
|
安全 Java API
第5章 Spring Security 的高级认证技术(2024 最新版)(上)
第5章 Spring Security 的高级认证技术(2024 最新版)
29 0
|
6天前
|
Java 关系型数据库 MySQL
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。一套UWB精确定位系统,最高定位精度可达10cm,具有高精度,高动态,高容量,低功耗的应用。
一套java+ spring boot与vue+ mysql技术开发的UWB高精度工厂人员定位全套系统源码有应用案例
|
21天前
|
XML Java 数据格式
Spring(一)IOC小案例
Spring(一)IOC小案例
|
1月前
|
Java 容器 Spring
【spring(一)】核心容器总结
【spring(一)】核心容器总结
|
1月前
|
Java 开发者 容器
【Java】深入了解Spring容器的两个关键组件
【Java】深入了解Spring容器的两个关键组件
10 0
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (下)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界
|
1月前
|
XML Java 数据格式
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)
Spring 的奇幻起源:从 IoC 容器到 Bean 的魔法世界 (上)
|
1月前
|
Cloud Native Java Docker
【Spring云原生】Spring官宣,干掉原生JVM,推出 Spring Native!整体提升性能!Native镜像技术在Spring中的应用
【Spring云原生】Spring官宣,干掉原生JVM,推出 Spring Native!整体提升性能!Native镜像技术在Spring中的应用
|
2月前
|
前端开发 Java 数据格式
10个知识点让你读懂spring MVC容器
随着 Spring Boot 逐步全面覆盖到我们的项目之中,我们已经基本忘却当年经典的 Servlet + Spring MVC 的组合,那让人熟悉的 web.xml 配置。而本文,我们想先抛开 Spring Boot 到一旁,回到从前,一起来看看 Servlet 是怎么和 Spring MVC 集成,怎么来初始化 Spring 容器的。
20 1