<bean id="myDataSource"
class="org.springframework.jndi.
JndiObjectFactoryBean">
<property name="tempSensorDS">
<value>ConnectionFactory</value>
</property>
</bean>
该代码突出了Spring所提供的关于表格的测试灵活性。该代码可以在“容器内”运行(从JNDI查找数据源),经过细微的改动之后也可在“容器外”运行。
虽然模板化机制仅适用于某些特定场合,但我们可以泛化这一概念,将它应用于更广泛的场合。例如,一种将已检查异常转变为未检查异常、此外还可能为异常处理提供一些中间或统一的异常处理策略的机制将会很有帮助。我们还可以使用该机制将底层的基本异常“软化”得更合乎人意。我们可以将PacketFrameParityFaultException软化为CommunicationsUnreliableException。这个重新映射的较软的异常表示情况可能并不那么严重,重新请求也是可以的。
Spring已经具备了一种类似于包装EJB调用(在最后一节介绍)的机制,但遗憾的是它并不具备任何“通用”的东西,至少在异常软化意义上是这样的。但Spring的确有一个非常健壮的AOP(面向方面编程)框架,我们可以用它来逼近这种行为。下面是一个关于这种软化适用领域的(公认的)精心设计的例子。
我们再来看看本文前面已经开始探讨的一些概念。在第一节中我们介绍了一个基于远程传感器的小应用程序。现在我们继续探讨这个例子。我们将从一个简单的传感器接口开始介绍,该接口代码如下:
public interface ProtocolAdapterIfc
{
public Integer getRemoteSensorValue()
throws CommChecksumFault,
CommConnectFailure,
CommPacketSequenceFault;
}
这并没有什么特别之处。显然实现该接口的任何人都会获得一个远程值并将它返回给调用者。在此期间调用者可能要面对某些可怕的灾难,该接口可能抛出的已检查异常就是例证。
接下来我们来看该接口的一个实现程序。实现ProtocolAdapter的类是CarrierPigeon,其代码类似于:
public class CarrierPigeon
implements ProtocolAdapterIfc
{
private boolean isTired = true;
private boolean canFlapWings = false;
public Integer getRemoteSensorValue()
throws CommChecksumFault,
CommConnectFailure,
CommPacketSequenceFault
{
if(isTired && !canFlapWings )
{
throw new
CommConnectFailure("I'm Tired!");
}
return new Integer(42);
}
}
为简洁起见,这里省略了属性的getter和setter方法。当调用getRemoteSensorValue()时,CarrierPigeon方法将检查它是否使用过度以及它还能否执行。如果它确是使用过度并且不能执行,我们将无法获得任何值,必须抛出CommConnectionFailure,它是一个已检查异常。到现在为止,一直都还不错。但别高兴得太早了!我已经决定不让应用程序编程人员应对“疲劳的信鸽”。可以将它包装在某种对象中,并在达到目的之前捕获异常,但必须处理20种不同的传感器。此外,我更喜欢对这些东西进行适度透明地处理,随着对该系统了解的增多,或许还会更改异常处理程序中的逻辑。我想要的是一个兼容API,应用程序编程人员能够使用它,并且随着时间的推移能够改变或增强。我想模板化异常处理,并让应用程序编程人员能够处理软的未检查异常,而不是硬异常。Spring非常符合这种情况。下面是为此而使用的策略的要点:
定义一个较软的消除已检查异常的最小接口。这就是应用编程人员将使用的接口。
使用Spring AOP结构,开发一个客户机调用和目标对象调用之间的拦截器,在本例中是“信鸽”。
使用Spring安装该拦截器并运行。
首先来看这个较软的接口:
public interface SensorIfc
{
public Integer getSensorValue();
}
请注意,在限定范围内可以重命名方法,使其更有意义。还可以消除已检查异常,就像这里所做的一样。接下来,也可能会更有趣的是我们想要让Spring将其注入调用栈的拦截器:
import org.aopalliance.intercept.MethodInterceptor;
public class SensorInvocationInterceptor
implements MethodInterceptor
{
public Object invoke(MethodInvocation
invocationTarget) throws Throwable
{
// Return object reference
Object o = null;
// Convert it to the protocol interface type
ProtocolAdapterIfc pai =
(ProtocolAdapterIfc) invocationTarget.getThis();
try
{
o = pai.getRemoteSensorValue();
}
catch (CommChecksumFault csf)
{
throw new SoftenedProtocolException(
"protocol error [checksum error]: "
+ csf.getMessage());
}
catch (CommConnectFailure cf)
{
throw new SoftenedProtocolException(
"protocol error [comm failure]: "
+ cf.getMessage());
}
catch (CommPacketSequenceFault psf)
{
throw new SoftenedProtocolException(
"protocol error [message sequence error]"
+ psf.getMessage());
}
return o;
}
}
通过实现Spring MethodInterceptor接口并将该类插入Spring系统(稍后我们将进行讨论),我们将通过MethodInvocation参数获得实际的目标方法。这样就能提取我们想要的真正对象。请记住,我们更改了被调用者看作SensorIfc的接口,该接口使目标对象得以实现ProtocolAdapterIfc。我们这样做是为了简化调用,更是为了消除所有的已检查异常。该拦截器只调用用来捕获可能抛出的任何已检查异常、并用SoftenedProtocolException将它们重新包装的目标方法。实际上,我们只重新包装消息,但要做到心中有数。SoftenedProtocolException扩展了RuntimeException,后者无疑是一个未检查异常。该操作的最终结果是应用程序开发人员不必处理任何已检查异常。如果他们真想处理异常,他们只需(非强制地)处理一个:SoftenedProtocolException。不错吧!
那么,如何才能让Spring完成这一切呢?答案就是要有正确的配置文件。我们现在就来看一看该配置文件,并了解其工作原理:
<!-- TARGET OBJECT -->
<bean id="protocolAdapter"
class="yourco.project.comm.CarrierPigeon">
<property name="isTired">
<value>true</value>
</property≷
<property name="canFlapWings">
<value>true</value>
</property≷
</bean≷
<!-- INTERCEPTOR -->
<bean id="sensorInterceptor"
class="yourco.project.springsupport.
SensorInvocationInterceptor"/>
<!--WIRE EVERYTHING UP, HAND BACK TO THE USER-->
<bean id="temperatureSensorOne"
class="org.springframework.aop.framework.
ProxyFactoryBean">
<property name="proxyInterfaces">
<value>
yourco.project.interfaces.SensorIfc
</value>
</property>
<property name="target">
<ref local="protocolAdapter"/>
</property>
<property name="interceptorNames">
<list>
<value>sensorInterceptor</value>
</list>
</property>
</bean>
我们逐节看这些代码时,可以看到它比我们前面看到的Spring配置文件要稍微复杂一些。“TARGET OBJECT”注释下面的第一节指定了要作为调用的目标对象的类。还记得拦截器曾将一个参数传入其中来表示目标对象吗?这就是其工作原理。基于该参数,Spring现在知道要将哪个对象传递进来。“INTERCEPTOR”下面的代码节是在目标方法之前调用的类。此时,如果目标类抛出已检查异常,要开始处理异常,并进行软化。Spring将它与目标类相关联。最后一节是将各种元素联系起来。用户要请求的bean位于bean键temperatureSensorOne下。ProxyFactoryBean将生成并返回一个代理类,它用来实现yourco.project.interfaces.SensorIfc接口。目标对象当然就是protocolAdapter bean,它由yourco.project.comm.CarrierPigeon的实例支持。只要用户调用代理的方法就插入并调用的拦截器位于bean键sensorInterceptor下,并由yourco.project.springsupport.SensorInvocationInterceptor支持。请注意,可使用多个拦截器,因为该属性是一个列表,因此可以将许多拦截器对象关联到方法调用中,这是一个非常有用的理念。
运行该应用程序时,根据插入的CarrierPigeon值,我们可以看到一些有趣的行为。如果我们的CarrierPigeon没有使用过度并能执行,我们将看到这样的输出:
The sensor says the temp is:42
显然“信鸽”没问题而且状况很好,并计算出温度为42。如果由于改变CarrierPigeon Spring节中的值,而造成“信鸽”使用过度或不能执行,我们将得到如下所示的结果:
yourco.project.exceptions.comm.SoftenedProtocolException: protocol
error [comm failure]: I'm Tired!
at yourco.project.springsupport.SensorInvocationInterceptor.invoke
(SensorInvocationInterceptor.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed
(ReflectiveMethodInvocation.java:144)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke
(JdkDynamicAopProxy.java:174)
at .getSensorValue(Unknown Source)
at yourco.project.main.Main.main(Main.java:32)
在这种情况下,“信鸽”或者使用过度,或者不能执行,因此得到的结果是SoftProtocolException,并附带一条消息说明发生的情况:“I'm tired!”够酷吧!
我希望人们能够开始了解Spring的强大及其多种功能。Spring框架悄然兴起,并成为开发人员的编程工具箱中的真正的“瑞士军刀”。Spring在实现让开发人员能够集中于应用程序的基本组成部分这一传说般的承诺方面做得不错,这也正是它得以大行其道的业务逻辑。Spring将您从J2EE中的一些错综复杂的方面中解放出来。Spring的强大在于它能够使大多数东西看起来就如同非常普通的Java对象一样,而不管它们的性质或来源如何。既然Spring自身肩负起创建、关联和配置的重担,那么开发人员要做的只是掌握使用对象而不是构造对象的方法。Spring就如同在杂货店购买的预煮的饭菜。您要做的只是决定想吃什么、把它带回家、加热,然后吃!
综合说明
Spring是一个非常健壮的轻量级框架,它极好地弥补了J2EE/EJB环境的不足。Spring真正伟大的一点在于它不走极端。您可以以一种非常简单的方式开始使用Spring(正如我所做的那样),只是建立常见的关联,作为定位器模式的一种实现。稍后,您将发现其惊人的功能,并且很快对它的要求会越来越多。
我所发现的一个惊人之处是Spring所提供的测试灵活性。现在人们对通过Junit进行单元测试日益重视,这增加了测试,而不是减少测试。J2EE容器的使用使测试极端复杂,以至于难以进行。这种困难局面源于业务逻辑和容器框架服务之间产生的耦合。借助于Spring的配置机制,使实现可以动态切换,这样就有助于将业务对象从容器中释放出来。我们已经看到,如果只想测试Web组件,将活动的EJB换成其代理或一个已清除的业务服务并不会造成太大影响。借助于JDBC或Hibernate数据访问对象,我们可以使用常见的简单JDBC、非XA的数据源连接来测试这些组件,并将它们无缝地换出,代之以健壮的基于JTA、JNDI的对应连接。结论是:如果代码易于测试,并因此测试得更多,那么质量必然会提高。
结束语
本文粗略地概括介绍了IoC,并详细介绍了Spring。Spring框架具有许多功能,其中许多功能在本文中只是点到为止。从基本的依赖注入到复杂的AOP操作,这些组成了Spring的强大功能,这是它的主要优点之一。能够根据问题需要使用或多或少的IoC功能是一个极具吸引力的理念,我认为,这在通常错综复杂的J2EE编程领域也是颇受欢迎的。现在看完了这篇文章,我衷心地希望它能对您有所帮助,并可以应用到您的工作中。欢迎随时提供反馈!