正 文

J2EE中使用Spring AOP框架和EJB组件


www.7dspace.com  更新日期:2006-2-14 17:52:15  七度空间


  通知由Spring托管的组件

  在前面的内容中,我们重构了服务入口点,以便使用Spring托管的bean。现在,我将向您说明这样做将如何帮助改进组件和实现新功能。

  首先,假定用户想看到某些符号的价格,而这些价格并非由您的TradeManager组件所托管。换句话说,您需要连接到一个外部服务,以便获得当前您不处理的所请求符号的当前市场价格。您可以使用雅虎门户中的一个基于HTTP的免费服务,但是实际的应用程序将连接到提供实时数据的供应商(比如 Reuters、Thomson、Bloomberg、NAQ等等)的实时数据更新服务(data feed)。

  首先,需要创建一个新的YahooFeed组件,该组件实现了相同的TradeManager接口,然后从雅虎金融门户获得价格信息。自然的实现可以使用HttpURLConnection发送一个HTTP请求,然后使用正则表达式解析响应。例如:

public class YahooFeed implements TradeManager {
  private static final String SERVICE_URL = http://finance.yahoo.com/d/quotes.csv?f=k1&s=; 

  private Pattern pattern = Pattern.compile("\"(.*) - (.*)\""); 

  public BigDecimal getPrice(String symbol) {
    HttpURLConnection conn;
    String responseMessage;
    int responseCode;
    try {
      URL serviceUrl = new URL(SERVICE_URL+symbol);
      conn = (HttpURLConnection) serviceUrl.openConnection();
      responseCode = conn.getResponseCode();
      responseMessage = conn.getResponseMessage();
    } catch(Exception ex) {
      throw new RuntimeException("Connection error", ex);
    } 

    if(responseCode!=HttpURLConnection.HTTP_OK) {
      throw new RuntimeException("Connection error "+responseCode+" "+responseMessage);
    } 

    String response = readResponse(conn);
    Matcher matcher = pattern.matcher(response);
    if(!matcher.find()) {
      throw new RuntimeException("Unable to parse response ["+response+"] for symbol "+symbol);
    }
    String time = matcher.group(1);
    if("N/A".equals(time)) {
      return null;  // unknown symbol
    }
    String price = matcher.group(2);
    return new BigDecimal(price);
  } 

  public void setPrice(String symbol, BigDecimal price) {
    throw new UnsupportedOperationException("Can't set price of 3rd party trade");
  } 

  private String readResponse(HttpURLConnection conn) {
    // ...
    return response;
  }

}

  完成这种实现并测试(在容器外部!)之后,就可以把它与其他组件进行集成。传统的做法是向TradeManager2Impl添加一些代码,以便检查getPrice()方法返回的值。这会使测试的次数至少增加一倍,而且要求为每个测试用例设定附加的先决条件。然而,如果使用Spring AOP框架,就可以更漂亮地完成这项工作。您可以实现一条通知,如果初始的TradeManager没有返回所请求符号的值,该通知将使用 YahooFeed组件来获取价格(在这种情况下,它的值是null,但是也可能会得到一个UnknownSymbol异常)。

  要把通知应用到具体的方法,需要在Spring的bean配置中声明一个Advisor。有一个方便的类叫做NameMatchMethodPointcutAdvisor,它允许通过名称选择方法,在本例中还需要一个getPrice方法:

<bean id="yahooFeed" class="org.javatx.spring.aop.YahooFeed"/> 

  <bean id="foreignTradeAdvisor" 
        class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
    <property name="mappedName" value="getPrice"/>
    <property name="advice">
      <bean class="org.javatx.spring.aop.ForeignTradeAdvice">
        <constructor-arg index="0" ref="yahooFeed"/>
      </bean>
    </property>
  </bean>

  正如您所看到的,上面的advisor指派了一个 ForeignTradeAdvice给getPrice()方法。针对通知类,Spring AOP框架使用了AOP Alliance API,这意味着环绕通知的ForeignTradeAdvice应该实现MethodInterceptor接口。例如:

public class ForeignTradeAdvice implements MethodInterceptor {
  private TradeManager tradeManager; 

  public ForeignTradeAdvice(TradeManager manager) {
    this.tradeManager = manager;
  } 

  public Object invoke(MethodInvocation invocation) throws Throwable {
    Object res = invocation.proceed();
    if(res!=null) return res; 

    Object[] args = invocation.getArguments();
    String symbol = (String) args[0];
    return tradeManager.getPrice(symbol);
  }

}

  上面的代码使用invocation.proceed()调用了一个原始的组件,而且如果它返回null,它将调用另一个在通知创建时作为构造函数参数注入的tradeManager。参见上面foreignTradeAdvisor bean的声明。

  现在可以把在Spring的bean配置中定义的tradeManager重新命名为baseTradeManager,然后使用 ProxyFactoryBean把tradeManager声明为一个代理。新的baseTradeManager将成为一个目标,我们将使用上面定义的foreignTradeAdvisor通知它:

<bean id="baseTradeManager" class="org.javatx.spring.aop.TradeDao">
    ... same as tradeManager definition in the above example
  </bean> 

  <bean id="tradeManager" class="org.springframework.aop.framework.ProxyFactoryBean">
    <property name="proxyInterfaces" value="org.javatx.spring.aop.TradeManager"/>
    <property name="target" ref="baseTradeManager"/>
    <property name="interceptorNames">
      <list>
        <idref local="foreignTradeAdvisor"/>
      </list>
    </property>
  </bean>

  基本上,就是这样了。我们实现了附加的功能而没有修改原始的组件,而且仅使用Spring应用程序上下文来重新配置依赖性。要想不借助于Spring AOP框架在典型的EJB组件中实现类似的修改,要么必须为EJB添加附加的逻辑(这会使其难以测试),要么必须使用decorator模式(实际上增加了EJB的数量,同时也提高了测试的复杂性,延长了部署时间)。在上面的例子中,您可以看到,借助于Spring,可以轻松地不修改现有组件而向这些组件添加附加的逻辑。现在,您拥有的是几个轻量级组件,而不是紧密耦合的bean,您可以独立测试它们,使用Spring Framework组装它们。注意,使用这种方法,ForeignTradeAdvice就是一个自包含的组件,它实现了自己的功能片断,可以当作一个独立单元在应用服务器外部进行测试,下面我将对此进行说明。

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

上一篇:情人节个性礼品光盘制作全攻略
下一篇:Alexa排名能帮你赚钱么?
标题:J2EE中使用Spring AOP框架和EJB组件 作者:Eugene Kuleshov 来源:BEA
收藏此页】【打印】【关闭
站 内 搜 索
 

热 点 导 读
特 别 推 荐