Spring 之 Container Extension points

一般地,应用程序开发者不需要实现ApplicationContext的子类。作为替代,可以通过指定接口实现插入扩展Spring IoC容器。下面的章节描述了这些集成接口。

 

 

4.8.1 使用BeanPostProcessor定制beans

 

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

 

 

你可以定义多个BeanPostProcessor实例,并且你可以通过设置order属性控制这些BeanPostProcessors执行的顺序。只要BeanPostProcessor实现了Ordered接口你就可以设置这个属性了。如果你写了自己的BeanPostProcessor,你就要考虑实现Ordered接口。

 

 

注意:BeanPostProcessors操作在bean或者对象实例上;就是说,Spring IoC容器实例化一个bean实例并且之后BeanPostProcessors做它们的工作。

 

 

BeanPostProcessors范围位于pre-container。其仅在使用容器继承中有用。如果你在一个容器中定义一个BeanPostProcessor,其将是在那个容器内部post-process的beans。换句话说,在一个容器中定义的bean不被在另一个容器中定义的BeanPostProcessorpost-processed,即使两个容器属于相同的层级。

 

 

为改变真实的bean定义(blueprint定义了bean),你需要使用一个BeanFactoryProcessor。

 

 

 

org.springframework.beans.factory.config.BeanPostProcessor接口由两个回调方法组成。当这样的一个类以post-processor注册到容器内时,对于由这个容器创建的每个bean实例,post-processor在调用容器初始化方法前(比如InitializingBean的afterPropertiesSet()方法和任何声明的init方法)和任何bean实例化调用后从容器内获取一个回调。Post-processor可以获取那个bean实例的所有动作,包括完全忽略回调。一个bean post-processor一般地检查回调接口或者可能用一个代理包装一个bean。一些Spring AOP底层类以bean post-processor实现来提供包装代理逻辑。

 

 

 

一个ApplicationContext自动查找在配置元数据中定义的beans,实现了BeanPostProcessor接口。ApplicationContext以post-processor注册这些bean,这样它们可以在其后面的创建中调用。它们也可以如同其他bean一样销毁。

 

 

程序化注册BeanPostProcessors

                     注册BeanPostProcessor的推荐方式是上面提及的ApplicationContext的自动查找,也可能程序化注册一个BeanPostProcessor,借助ConfigurableBeanFactory的addBeanPostProcessor方法。这个对于要在注册前进行评估判断条件的时候,或者甚至同级中上下文之间复制bean post Procesors很有用。注意然而程序化增加的BeanPostProcessors不遵循Ordered接口。这里它是注册的顺序,同时指定了执行的顺序。也要注意那个程序化注册的BeanPostProcessors总是通过自动查找在注册前处理,而不管任何确定的顺序。

 

 

BeanPostProcessor 和 自动代理AOP

 

实现了BeanPostProcessor接口的类是特殊的并且会被容器特殊对待。所有的BeanPostProcessors和它们直接引用的beans在启动时实例化,其作为那个ApplicationContext的指定启动阶段的一部分。下一步,所有的BeanPostProcessors在一个存储的fashion中注册并应用于容器内所有的将来的beans。因为AOP自动代理以一个BeanPostProcessor自身实现,不管是BeanPostProcessors还是直接相关的beans,都不能合格的自动代理,而是切面编织进去。

 

(neither BeanPostProcessors nor the beans theyreference directly are eligible for auto-proxying, and thus do not have aspectswoven into them)----翻译待定

 

 

对于任何这样的bean,你应该看到一些信息日志:“Bean foo is not eligiblefor getting processed by all BeanPostProcessor interfaces (for example: noteligible for auto-proxying

 

 

下面的例子显示了如何在一个应用程序中写,注册和使用BeanPostProcessors。

 

 

例子:Hello World BeanPostProcessor-style

 

第一个例子演示了基本的用法。这个例子显示了一个自定义的BeanPostProcessor实现,在容器创建每个bean时调用toString()方法,并在控制台打印结果。




package scripting;

import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.BeansException;

public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {

  // simply return the instantiated bean as-is
  public Object postProcessBeforeInitialization(Object bean, String beanName)
                                                                     throws BeansException {
      return bean; // we could potentially return any object reference here...
  }

  public Object postProcessAfterInitialization(Object bean, String beanName)
                                                                     throws BeansException {
      System.out.println("Bean '" + beanName + "' created : " + bean.toString());
      return bean;
  }
}


?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-3.0.xsd
         http://www.springframework.org/schema/lang
         http://www.springframework.org/schema/lang/spring-lang-3.0.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一样依赖注入。

 

 

 

 

 

下面简单的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


4.8.1.2 例子:RequiredAnnotationBeanPostProcessor

 

 

 

使用回调接口或者使用BeanPostProcessor自定义实现的注解是扩展Spring IoC容器的通用方法。例子之一是Spring的RequiredAnnotationBeanPostProcessor------一个BeanPostProcessor实现,其在Spring分布中流转,确保了以注解标识的beans的JavaBean属性可以以一个值依赖注入。

 

 

 

4.8.2 使用BeanFactoryPostProcessor定制配置元数据

 

 

我们将查看的下一扩展点是org.springframework.beans.factory.config.BeanFactoryPostProcessor.。这个接口的语义与BeanPostProcessor的语义很相似。主要的一个区别是:BeanFactoryPostProcessors操作在bean配置元数据上;即,SpringIoC容器允许BeanFactoryPostProcessors读取配置元数据并可能在容器实例化任何bean之前改变这些配置元数据。

 

 

你可以配置多个BeanFactoryPostProcessors,并且通过设置order属性可以控制这些BeanFactoryPostProcessor的执行顺序。然而,只有实现了Ordered接口的BeanFactoryPostProcessor才能设置这个属性。如果你自定义BeanFactoryPostProcessor,应该考虑实现Ordered接口。

 

 

注意:

     如果你想改变bean实例(即,从配置元数据中创建的对象),稍后你需要使用BeanPostProcessor。虽然技术上在BeanFactoryPostProcessor内可以与bean实例一起工作是可能的,但是这样做会导致bean实例化不完全,违反了标准容器的生命周期。这个可能在绕过bean postprocessor中产生负面效果。

 

 

 

BeanFactoryPostProcessors范围是pre-container。仅在容器级别有效。如果你在一个容器内定义了一个BeanFactoryPostProcessor,就仅在那个容器内应用。一个容器内的bean定义将不能在另一个容器内由BeanFactoryPostProcessor post-processed,即使是两个容器处于相同的界别。

 

 

 

一个beanfactory post-processor是自动执行的,当其在ApplicationContext内部申明时,为了适应容器中定义的配置元数据的变化。Spring包含了大量的预定义bean 工厂post-processors,比如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer。也能使用自定义的BeanFactoryPostProcessor,例如,注册自定义的属性编辑。

 

 

 

一个ApplicationContext自动查找部署在其中的所有的bean,其实现了BeanFactoryPostProcessor接口。它在适当地时候使用这些beans作为bean factory post-processors。你可以如同其他bean一样部署这些bean。

 

 

 

 

注意:

对于BeanPostProcessors,一般地你不想配置BeanFactoryPostProcessors来延迟初始化。如果没有其他的bean引用一个bean(Factory)PostProcessor,post-processor不将实例化。那么,将忽略其延迟实例化,并且即使你在<bean/>元素中设置default-lazy-init属性为true,Bean(Factory)PostProcessor也将马上实例化。

 

 

 

 

 

 

 

 

4.8.2.1 例子:PropertyPlaceholderConfigurer

 

 

使用PropertyPlaceholderConfigurer从一个独立文件的bean定义中使用标准Java属性格式设置合适的值。这样做能够使的开发人员部署应用定制化指定环境的属性,比如数据库的URLs和密码,不需要冒着修改容器主要XML文件的风险。

 

 

考虑下面的基于XML配置元数据的片段,使用占位符定义了DataSource。下面的例子显示了内部属性文件的属性配置。在运行时,PropertyPlaceholderConfigurer被应用到元数据,将替代DataSource的一些属性。这个替代值由${属性名}格式的占位符指定。,其遵循Ant/log4j/JSP EL类型。



<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>


确切的值在标准Java属性文件中定义。


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介绍的命名空间中,可能使用一个专用配置元素配置合适的占位符。在location属性中可以使用逗号隔开提供更多的locations。

 

 

 

PropertyPlaceholderConfigurer不仅仅查找你定义的属性文件中的属性。默认地,它也检查Java系统属性,如果其不能在一个指定属性文件中找到一个属性。你可以通过设置配置中的systemPropertiesMode属性定制化行为,使用下面提供的整形值的一个。

 

 

Never(0):从不检查系统属性

Fallback(1):如果在指定的属性文件中找到可用值,查找系统属性。这是默认的。

 

Oberride(2):首先检查系统属性,再检查指定的配置文件。这个允许系统属性重写任何其他的属性源。

 

 

类名替代

 

你可以使用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。

 

 

4.8.2.2 例子:PropertyOverrideConfigurer

 

 

PropertyOverrideConfigurer,另一个bean 工厂post-processor,与PropertyPlaceholderConfigurer相似,但也与后者不是完全像,最初的定义可以有bean的默认值或者没有值。如果重写的属性文件对于某一bean属性没有设置,就使用默认上下文定义的值。

 

 

注意bean定义不在意是否重写,因此从XML定义文件中发现使用重写配置不是很明显。在多PropertyOverrideConfigurer实例情况中,对于相同bean属性的定义不同的值,最后的一个覆盖前面的,就是因为重写机制。

 

 

属性文件定义格式如下:

 

beanName.property=value

 

例如:

dataSource.driverClassName=com.mysql.jdbc.Driver

dataSource.url=jdbc:mysql:mydb

 

 

这个例子文件可以用于容器定义中,包含了一个叫做dataSource的bean,其有driver和url属性。

 

 

复杂的属性名也支持,只要路径的每个组件,除了最后的需要重写的属性是非null的(假定构造器初始化)。在这个例子中,

 

 

foo.fred.bob.sammy=123

 

 

 

foo bean的fred属性的bob属性的sammy属性的数量值为123.

 

 

注意:

指定的重写的值总是文字的值;它们不能翻译成bean的引用。这个惯例也适用XMLbean定义的的初始值指定一个bean‘的引用。

 

 

 

在Spring 2.5介绍的Context命名空间中,使用一个指定的配置元素来配置属性重写。

 

 

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

 

 

 

4.8.3 Customizing instantiation logic with a  FactoryBean(自定义一个FactoryBean的实例化逻辑)

 

 

实现了org.springframework.beans.factory.FactoryBean接口的对象自身就是工厂。

 

FactoryBean是Spring IoC容器的实例化逻辑的a point of pluggability。如果你有复杂的初始化代码,其在Java的中很好的表示代替XML中冗余的表示,你可以创建自己的FactoryBean,在那个类中写复杂的初始化逻辑,并以插件形式将自定义的FactoryBean插入那个容器。

 

 

FactoryBean接口提供了三个方法:

 

  1 Object getObject() 返回工厂创建的对象的实例。这个实例可能被共享,取决于这个工厂返回对象是singleton还是prototype。

  2  boolean isSingleton() 如果这个对象返回的是singleton则返回true,否则false。

  3 Class getObjectType() 返回有getObject()返回的对象类型或者null,如果类型不可知。

 

 

 

FactoryBean概念和接口用于Spring框架的许多地方。在Spring中超过50多个类实现了FactoryBean接口。

 

当你需要请求容器一个确切的FactoryBean实例,而不是自己创建,以&开始防止bean的id前面,当调用ApplicationContext的getBean()方法时。所以对于给定mybean id的FactoryBean,调用容器的getBean(“myBean”),返回FactoryBean的子类。然而,调用getBean(“&mybean”)返回FactoryBean自身


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值