分享
 
 
 

利用Spring框架改进J2EE编程

王朝java/jsp·作者佚名  2006-11-24
窄屏简体版  字體: |||超大  

摘要

J2EE编程正在变得越来越复杂。J2EE已经发展为一个API、复杂化的编程和配置的复杂网络。为了应对这种复杂性,新的框架和方法不断涌现。这些框架高度依赖于一个称为IoC(Inversion of Control,反向控制)的概念。本文将探讨这种方法的一些特性和优点,因为这种方法与J2EE编程相关,而且可以使J2EE编程变得更轻松。

简介

马克·吐温的一句话常被引用:“……关于我死亡的报道是一种夸张。”现在已经出现了很多关于.Net的流言,以及认为J2EE API的复杂性无法克服和EJB作为一种组件架构即将灭亡的流行极客(geek)文化。从学术或者只是想像的立场来看,这没什么大不了的,但事实是J2EE/EJB API已经经历了一场达尔文式的进化。具有DCOM或CORBA项目经验的读者会明白我的意思。过去,人们都乐于听闻EJB组件模型的美好前景。实际情况是,人们在与J2EE相关的各个方面都投入巨大。宣布抛弃以前的所有工作并重新组织,这种想法看起来也许有理,但是它并没有建立在良好的业务洞察力之上。EJB继续发展,而术语、实践和框架也随之涌现(spring up),它们弥补了J2EE API的不足。我说的不是“Spring出现(up)”,对吧?

我是一名顾问,职责是帮助构建大型的分布式应用程序,而且通常是J2EE应用程序。因此,我有机会亲历许多项目的整个生命周期。另外我还能够将我从一个刚刚完成的项目中刚刚学到的东西直接带入一个全新的项目。从某种意义上说我的“自然选择”过程加快了。我可以说最近Spring(更具体地说就是IoC,即反向控制)已经越来越多地融入到我的项目中了。在本文中,我将从支持或增强J2EE项目的角度来探讨Spring。更确切地讲,Spring框架能够标准化许多J2EE最佳实践,还能同类化(homogenize)许多无处不在的J2EE模式。接下来我们将浏览Spring庞大体系中的一小部分内容,重点介绍(依我浅见)能够帮助改进J2EE应用程序的功能。

IoC简介

一般来说,IoC是一种管理类之间关联的技术。没错,就这么简单!任何人都不是孤立的,对于各个对象来说也是如此。应用程序中的对象是相互依赖的。通过编程方式来表现这种依赖性通常既冗长又容易出错。好的IoC框架将声明式地(通过一个XML配置文件)而不是编程式地(这种方式的可靠性较差)——串连起应用程序之间的相互依赖性。

自由使用接口是IoC开发的一个主要方针。接口编程大大提高了应用程序的灵活性,从而增强了声明式的关联。接口实现是通过IoC配置在运行时声明的,这样就能够在不影响或少影响实际应用程序代码的情况下“重建(rewire)”关联。这在各种IoC框架中是反复提及的一个主题,一般而言,也是应该遵循的良好实践。

一个小例子

我喜欢通过例子来更快地理解概念。下面就是运用了IoC的一组例子;您将看到,这些例子的复杂性是逐递增的。大多数人在一开始使用IoC容器时都是利用其依赖注入(inject dependency)功能——即,声明式地将对象关联起来。利用IoC有助于创建更整洁的代码,如有必要重建对象之间的关联,一般来说对于这些代码也会更灵活、更容易。IoC的优点远不止依赖注入,而其扩展功能确是以依赖注入程序为起点的。

我们将从构建简单的依赖注入例子开始。第一个例子用于阐明已经提及的两个概念。第一个概念是IoC在运行时构建和关联对象的能力,第二个是与接口编码相结合而产生的灵活性。首先假定架构师递交了图1所示的UML。

图1. 接口可插性

这个小例子表示一个温度测量系统。几个传感器对象属于不同的类型,但都实现了ProtocolAdapterIfc接口,因此在将它们插入TemperatureSensor对象时,它们是可互换的。在需要TemperatureSensor时,系统中的某个实体必须知道要生成并与该传感器对象关联的ProtocolAdapterIfc的具体类型。在本例中,该传感器可基于命令行参数、数据库中的行或通过属性文件进行配置。本例还不足以造成挑战或展示一个复杂框架,但它足以阐明IoC基础。

但是,想象一下:在一个相当复杂的应用程序中这种情况屡屡发生,而您还希望能动态地——至少要在外部——改变对象关联。假设有一个DummyProtocolAdapter,它总是返回42这个值,使用它来进行测试。为什么不提供一个单个的统一框架?——让开发人员能够依靠该框架,以一种一致的、外部配置的方式建立类之间的关联,并且不引起工厂单元素类(factory singleton classe)的异常增加。这听起来可能没什么大不了,但它要依赖于IoC的简单性。

我们使用一个TemperatureSensor类,它与一个实现ProtocolAdapterIfc接口的类有关联。TemperatureSensor将使用该委托类来获得温度值。如UML图所示,在实现ProtocolAdapterIfc并且随后可用于该关联的应用程序中有若干个类。我们将使用IoC框架(在本例中是Spring)来声明要使用的ProtocolAdaperIfc的实现。Spring将在运行时建立关联。我们先来看XML代码,它将实例化TemperatureSensor对象并将一个ProtocolAdapterIfc实现与它关联起来。该代码如下所示: <bean id="tempSensor"

class="yourco.project.sensor.TemperatureSensor">

<property name="sensorDelegate">

<ref bean="sensor"/>

</property>

</bean>

<!-- Sensor to associate with tempSensor -->

<bean id="sensor" class="yourco.project.comm.RS232Adapter"/>

看了这些代码之后,对于其目的就应该非常清楚了。我们配置Spring来实例化TemperatureSensor对象,并将其与RS232Adapter相关联,作为实现ProtocolAdapterIfc接口的类。若想改变已经与TemperatureSensor关联的实现,惟一需要更改的就是sensor bean标记中的class值。只要实现了ProtocolAdapterIfc接口,TemperatureSensor就不再关心关联了什么。

将这应用于应用程序相当简单。我们必须先接入Spring框架,将它指向正确的配置文件,然后根据名称向Spring索取tempSensor对象的实例。下面是相应的代码:

ClassPathXmlApplicationContext appContext =

new ClassPathXmlApplicationContext(

new String[]

{ "simpleSensor.xml" });

BeanFactory bf = (BeanFactory) appContext;

TemperatureSensor ts = (TemperatureSensor)

bf.getBean("tempSensor");

System.out.println("The temp is: "+

ts.getTemperature());

可以看出,这些代码并不是非常难。首先是启动Spring并指定要使用的配置文件。接下来根据名称(tempSensor)引用Bean。Spring使用这样一种机制:基于simpleSensor.xml文件的描述创建该对象并与其他对象关联。它用于注入依赖性——在本例中,通过将它作为一个参数传递给sensorDelegate()方法而实例化RS232Adapter对象并将其与TemperatureSensor对象关联。

比较起来,使用编程式Java完成这一任务也不是很难。如下所示:

TemperatureSensor ts2 = new TemperatureSensor();

ts2.setSensorDelegate(new RS232Adapter());

纯粹主义者或许会认为实际上这是更好的方法。代码行数少,并且可读性可能更强。确实如此,但这种方法的灵活性要小得多。

可以随意换入和换出不同层中不同对象的不同实现。例如,若Web层中的组件需要来自新业务对象的额外的功能,您只需将该业务对象与Web层对象相关联,就像上面TemperatureSensor例子中的做法。它将被“注入”到Web对象中以随时使用。

能够重新配置整个应用程序的结构,意味着可以轻松更改数据源。比如说,或者为不同的部署场景创建不同的配置文件,或者为测试场景创建更有用的、不同的配置文件。在测试场景中可能会注入实现接口的模拟对象,而不注入真正的对象。稍后我们将介绍一个这样的例子。

上面所述的例子可能是依赖注入的最简单形式。利用相同的策略,我们不仅能够关联不同的类,还能够在类中安装属性。诸如字符串、整数或浮点数之类的属性,只要具有JavaBean样式的存取器,就可以通过Spring配置文件将它们注入类中。我们还可以通过构造函数来创建对象和安装属性或bean引用。其语法只比通过属性进行设置稍稍复杂一些。

所有这一切都是利用一种灵活的声明性配置完成的。无需更改代码,建立依赖关联的所有艰难任务都由Spring来完成。

Spring--标准化的定位器模式

我一直将服务定位器模式视作良好的J2EE规范的主要组成部分。对于不熟悉这一术语的人来说,可以这样理解它:我们一般认为典型的J2EE应用程序由若干层组成。通常有Web层、服务层(EJB、JMS、WS、WLS控件)以及数据库。一般来说,完成某一请求所需的“查找”服务中都包含了一些方法。Service Locator(服务定位器)模式认为,将这些方法包装在某种隐藏了生成或查找给定服务的复杂性的工厂类中是一个好主意。这减少了JNDI或只会造成Web层操作类混乱的其他服务产品代码的增加。在Spring出现以前,这通常是由经过考验证明可靠的(tried-and-true)Singleton类来实现的。Singleton/Locator/Factory模式可以描绘为:

图2. 定位器模式的顺序图

这是对散布在整个Web控制器代码中的增加的JNDI查找代码的一个巨大改进。它被巧妙地隐藏在工厂内部的协作类中。我们可以使用Spring来改进这一术语。此外,该解决方案将适用于EJB、Web services、异步JMS调用,甚至还有基于WLS控件的服务。由Spring实现的这种定位器模式的变体考虑了业务服务之间的一些抽象化和同质性。换句话说,Web控制器的开发人员真的可以不考虑他们所使用的服务的种类,一个类似于“WLS控件”但是更通用的概念。

IoC框架大大改进了这种模式的效用,而且实际上废除了复杂而特殊的singleton代码来实现它。通过借用上例中引入的概念,我们实际上无需额外代码便能构建一个非常强大且无处不在的Service Locator模式。为此,在一开始有一个简单的要求,即Web操作的开发人员应专门处理实现接口的那些事情。这基本上已经通过EJB编程实现,但并不是说Web操作的开发人员处理的服务必须通过EJB来实现。它们可能只是普通Java对象或Web services。要点是应当通过接口(这样实现能够换入换出)来编写服务程序,并且运行时配置能够由Spring处理。

Spring之所以非常适合于Service Locator模式,是因为它或多或少能够统一地处理不同类型的对象。通过少许的规划和大量使用IoC,我们多少都能够以一种通用方式来处理大多数对象,而不用管它们的特性(EJB、POJO等等)如何,并且不会引起Singleton工厂类的增加。这使Web层编程变得更加轻松和灵活。

我们先来看一个关于这种模式如何应用于EJB的例子。我们都知道使用EJB可能是最复杂的方法,因为要将一个活动的引用引入EJB要做很多工作。若使用Spring,建议用EJB接口扩展非特定于EJB的业务接口。这样做有两个目的:保持两个接口自动同步,以及帮助保证业务服务对非EJB实现是可交换的,以便进行测试或清除(stubbing)。我们可以利用Spring固有的实用工具来定位和创建EJB实例,同时为我们处理所有难以处理的工作。相应代码如下所示:

<bean id="myBizServiceRef"

class="org.springframework.ejb.access.

LocalStatelessSessionProxyFactoryBean">

<property name="jndiName">

<value>myBizComponent</value>

</property>

<property name="businessInterface">

<value>

yourco.project.biz.MyBizInterface

</value>

</property>

</bean>

接下来可以检索bean并开始使用它,方法如下:

MyBizInterface myService = bf.getBean("myBizServiceRef");

这将返回Spring动态创建并包装了底层目标(在本例中是一个本地EJB实例)的一个对象。这种方法非常好,因为它完全隐藏了我们在处理EJB这一事实。我们将与一个实现简单业务接口的代理对象交互。Spring已经基于“真正的”业务对象考虑周到地动态生成了该对象。所包装的对象当然就是Spring定位和检索引用所要获得的本地EJB。此外,您还会注意到,这种代码形式与前面用于检索tempSensor对象的代码完全相同。

那么如果我们改变主意,想用普通Java对象来实现业务组件;或者可能在测试中,我们想用一个返回“固定(canned)”响应的已清除(stubbed)对象来替换重量级EJB,该怎么做呢?利用IoC和Spring,通过更改Spring上下文文件就可轻而易举地实现这些目标。我们只需使用更常规一点的东西(如我们在第一个Spring例子中所看到的)来替换EJB代理的连接即可:

<bean id="myBizServiceRef"

class="yourco.project.biz.MyStubbedBizService">

</bean>

请注意,我只更改了Spring框架所返回的内容的细节,没有更改bean id。最后的结果是业务对象的解决方案未变;它看上去和以前完全一样:

MyBizInterface myService =

bf.getBean("myBizServiceRef");

最大的区别显然是实现该业务接口的对象现在由一个普通Java对象(POJO)支持,并且只是该接口的一个已清除(stubbed)版本。这给单元测试或改变业务服务的特性带来了极大方便,而对客户端代码的影响很小。

使用Spring来标准化异常

Spring的一大贡献是“模板化”代码块。这在纯JDBC编程中表现得最为明显。我们都曾写过具有下述功能的代码:

创建一个数据库连接,可以的话从某个池创建。

构造一个查询字符串并提交。

迭代结果并将数据封送到域对象中。

处理不同阶段出现的大量异常。

确保记得编写finally代码块以关闭连接。

但是各处的这种代码往往都会或多或少地有点“样板化”。一般来说这是有害的,不仅因为不需要的代码会增加,还因为有些东西可能会遗漏,如非常重要的关闭连接,如果没有实现它,可能导致数据资源池的泄漏。

虽然我敢肯定我们都曾多次写过这类“样板”代码,但是将Spring方法和直接的JDBC实现对照来看,其结果将会有趣而又对比鲜明。“传统”的JDBC实现可能如下:

Connection con = null;

try

{

String url = "jdbc://blah.blah.blah;";

con = myDataSource().getConnection();

Statement stmt = con.createStatement();

String query = "SELECT TYPE FROM SENSORS";

ResultSet rs = stmt.executeQuery(query);

while(rs.n

[1] [2] [3] 下一页

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有