正 文

用AspectJ和Spring进行依赖项插入


www.7dspace.com  更新日期:2006-1-26 7:31:09  七度空间


  基于接口的插入

  迄今为止,我使用了 Spring 容器读取的 bean 定义来确定对象的依赖项。这个方案的一个变体采用合约接口,由客户端声明它的要求。假设前一节的 Account 实体要求访问 AccountOperationValidationService。我可以声明一个接口,如清单 4 所示:

清单 4. 客户端接口

public interface AccountOperationValidationClient { 

   public void setAccountOperationValidationService(
      AccountOperationValidationService aValidationService);

}

  现在,需要访问 AccountOperationValidationService 的对象必须实现这个接口,并把自己声明为客户。使用与前一节开发的方面类似的方面,我可以匹配实现这个接口的客户对象的所有初始化连接点。由它负责第一个代理职责:确定什么时候需要配置对象。第二个职责在接口中被明确表达:必须满足的依赖项是验证服务依赖项。我将用一个方面插入所有客户验证服务的依赖项。方面得到合适服务的最简单方法就是把服务插入到方面自身!清单 5 显示了一个示例:

清单 5. 服务插入器方面

   /** 
     * ensure that all clients of the account validation service
     * have access to it 
     */
    public aspect AccountOperationValidationServiceInjector { 

      private AccountOperationValidationService service; 

      /**
       * the aspect itself is configured via Spring DI
       */
      public void setService(AccountOperationValidationService aService){
        this.service = aService;
      } 

      /**
       * the creation of any object that is a client of the 
       * validation service
       */
      pointcut clientCreation(AccountOperationValidationClient aClient) :
        initialization(AccountOperationValidationClient+.new(..)) &&
        this(aClient); 

      /**
       * inject clients when they are created
       */
      after(AccountOperationValidationClient aClient) returning :
        clientCreation(aClient) {
        aClient.setAccountOperationValidationService(this.service);
      } 

    }

  这个解决方案提供了两级控制。服务本身实际的定义是在 Spring 的配置文件中提供的,就像清单 6 中的 XML 片段示例一样:

清单 6. 服务插入器配置

    <beans>  
      <bean name="AccountOperationValidationServiceInjector"
        class="org.aspectprogrammer.dw.
                  AccountOperationValidationServiceInjector"
        factory-method="aspectOf">
        <property name="service">
        <ref bean="AccountOperationValidationService"/>
          </property>
      </bean> 

      <bean name="AccountOperationValidationService" 
        class="org.aspectprogrammer.dw.
                 DefaultAccountOperationValidationService">
      </bean>
    </beans>

  服务的客户只需要实现 AccountOperationValidationClient 接口,那么就会自动用 Spring 定义的服务的当前实例对它们进行配置。

  重复插入
 
在 Spring 中的查询方法插入

查询方法插入是 Spring 容器支持的一种高级特性:由容器覆盖被管理 bean 的抽象或具体方法,返回在容器中查询另一个命名 bean 的结果。查询通常是非单体 bean。查询依赖项的 bean ,用被查询 bean 类型所声明的返回类型,定义查询方法。Spring 配置文件在 bean 的内部使用 <lookup-method> 元素告诉 Spring 在调用查询方法时应当返回什么 bean 实例。请参阅 参考资料 学习关于这项技术的更多内容。带有 HotSwappable 目标源的 Spring AOP 代理提供了另一种方法。

  迄今为止,我介绍的解决方案都是在对象初始化之后立即配置对象。但是,在某些情况下,客户需要与之协调的对象在运行的时候变化。例如,通过与系统进行交互,销售团队可以动态地为在线预订应用程序修改报价策略和座位分配策略。与报价策略和座位分配策略交互的预订服务需要的策略实现,应当是预订时的实现,而不是预订服务第一次初始化的时候实现的版本。在这种情况下,可以把依赖项的插入延迟到客户第一次需要它的时候,并在每次引用依赖项的时候,将依赖项的最新版本重新插入客户。

  这个场景的基本技术包括字段级插入或 getter 方法覆盖。在进入示例之前,我要再次强调:我要介绍的插入技术所面向的对象,是在 Spring 容器的控制之外 创建的。对于 Spring 创建的对象,Spring 容器已经提供了解决这些需求的简单机制。

  字段级插入

  在下面的示例中,可以看出如何为延迟插入或重复插入应用字段级插入。字段的 get 连接点让我可以确定什么时候进行插入,而字段类型可以确定要插入的依赖项。所以,如果客户声明了这样的一个字段:

private PricingStrategy pricingStrategy;

  而在客户的方法中,发现了下面的代码

this.pricingStrategy.price(.....);

  那么代码在运行时的执行会形成 pricingStrategy 字段的 get() 连接点,我可以用它插入当前报价策略实现,如清单 7 所示:

清单 7. 字段级插入示例

    public aspect PricingStrategyInjector {
    
      private PricingStrategy currentPricingStrategy; 
     
      public void setCurrentPricingStrategy(PricingStrategy aStrategy) {
        this.currentPricingStrategy = aStrategy;
      } 

      /**
       * a client is trying to access the current pricing strategy
       */
      pointcut pricingStrategyAccess() : 
        get(PricingStrategy *) &&
        !within(PricingStrategyInjector);  // don’t advise ourselves! 

      /**
       * whenever a client accesses a pricing strategy field, ensure they
       * get the latest...
       */
      PricingStrategy around() : pricingStrategyAccess() {
        return this.currentPricingStrategy;
      }
    }

  请参阅 下载 获得这个技术的实际效果。

  服务定位策略

  重复插入的一个替代就是用更常规的技术,用服务定位策略技术实现插入客户。例如:

public interface PricingStrategyLocator {
      PricingStrategy getCurrentPricingStrategy();
}

  虽然代价是定义一个额外接口,还会使客户代码更长一些,但是这项技术对于代码清晰性来说具有优势。

  结束语

  在这篇文章中,我把依赖项插入看作对象和对象执行的环境之间的合约。对象不愿意外出寻找自己需要的资源、要协作的合作伙伴或者使用的服务。相反,对象提供了一种机制,允许把这些依赖项提供给它。然后,在对象需要依赖项之前,执行环境负责把对象需要的所有依赖项提供给它。

  我讨论了依赖项插入解决方案的四个关键职责,这些是代理代表对象获取依赖项时必须解决的问题。最后,我介绍了满足这些需求的许多不同的技术。显然,如果能够 用 Spring 容器初始化并配置对象,那么就应当这么做。对于在 Spring 容器的控制之外创建的对象,例如一些使用非单体实例化模型的域对象或方面,我推荐使用 @SpringConfigured 标注或类似的东西。这项技术让您可以把全部配置信息完全转移到外部的 Spring 配置文件中。

4页,页码:[1] [2] [3] [4] 

上一篇:PHP程序员的优化调试技术和技巧
下一篇:VC实现类似Excel文件夹式样的标签控制
标题:用AspectJ和Spring进行依赖项插入 作者:Adrian Colyer 来源:ibm
收藏此页】【打印】【关闭
站 内 搜 索
 

热 点 导 读
特 别 推 荐