开发者社区> hello熊本> 正文

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

简介: 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容器的扩展点就讲到这里啦!

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
JSP---JSP中4个容器-pageContext使用
这里重点只讲pageContext容器的用法哦。 因为另外的3个容器(request,session,application)在前面的servlet中已经演示过很多遍了 容器 作用域 pageContex 仅仅是当前页面,无法传参 request 当前页面,可以传参 session 同一个JSESSIONID共用一个 application 只要服务器还没重新启动,就一直存在 详细介绍: pageContext – 它的作用范围仅为当前JSP页面。
843 0
阿里开源富容器引擎 PouchContainer 的 network 连接机制
PouchContainer 是阿里巴巴集团开源的高效、轻量级企业级富容器引擎技术,拥有隔离性强、可移植性高、资源占用少等特性。可以帮助企业快速实现存量业务容器化,同时提高超大规模下数据中心的物理资源利用率。
2399 0
使用OpenApi弹性释放和设置云服务器ECS释放
云服务器ECS的一个重要特性就是按需创建资源。您可以在业务高峰期按需弹性的自定义规则进行资源创建,在完成业务计算的时候释放资源。本篇将提供几个Tips帮助您更加容易和自动化的完成云服务器的释放和弹性设置。
20879 0
201608北京云栖Workshop - 基于容器服务的视频点播应用(一)
8月份北京云栖大会Workshop专场《打造千万用户海量视频网站》,为您介绍了如何从0到1,搭建一个大型的视频网站。也许您依然意犹未尽,或者不巧错过了,没有关系,本系列文章将为您回顾如何基于容器服务快速搭建一个视频点播应用。
2373 0
201608北京云栖Workshop - 基于容器服务的视频点播应用(二)
8月份北京云栖大会Workshop专场《打造千万用户海量视频网站》,为您介绍了如何从0到1,搭建一个大型的视频网站。也许您依然意犹未尽,或者不巧错过了,没有关系,本系列文章将为您回顾如何基于容器服务快速搭建一个视频点播应用。
2286 0
201604深圳云栖大会Workshop - 阿里容器服务与持续集成
目标 熟悉容器服务的基本概念 练习通过容器服务实现持续集成的过程 准备工作 容器服务控制台 容器服务控制台 镜像管理平台 阿里Code 帮助文档 产品概念解释 注意事项:预计耗费的费用 容器服务暂时不收费;演示过程中会创建ECS实例和SLB,按量收费。 步骤 1. 创建集群
5658 0
Spring IOC 容器源码分析
原文出处:https://javadoop.com/post/spring-ioc Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器。
784 0
解决tomcat引入spring容器出错
错误1: log4j:WARN Please initialize the log4j system properly. 2013-1-15 20:22:28 org.apache.catalina.core.StandardContext filterStart 严重: Exception starting filter struts2 Class: com.opensympho
1388 0
Spring 框架文档之核心技术—— AOP
Spring AOP 提供声明式企业服务(如声明式事务管理)。简化和补充 OOP。基于动态代理(JDK 动态代理或 CGLIB 代理)实现。
6565 0
+关注
hello熊本
热衷技术,热爱生活的熊本同学!
243
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载