基础结构及其应用实现

在这一章我们将学习在J2EE应用中实现业务逻辑。首先买我们看一个允许我们专注于业务逻辑的基础结构。这是J2EE规范的主要目标。但不幸的是,J2EE的复杂性意味着这个目标实现起来非常困难。

在这一章中,我们将看到附加的基础结构怎样被用来解决一般的问题以及如何简化J2EE的使用。我们需要考虑以下的内容:

一个强有力的应用基础结构应当实现的目标,以及为什么应用中应当使用一个基础结构是重要的。

一些包含示例应用的基础包的背后的动机和实现,这些包分成两类:

一类包通过JavaBeans提供一个一致的方法配置应用并促进了基于接口的设计。

一类包简化了复杂J2EE API(如:EJBJMS)的使用。

最后,我们看一看使用这章中描述的基础结构,在示例应用中的一些关键功能的实现。进而阐述怎样简化代码,以及怎样从工作效率和可维护性上获益。

基础结构

一个应用拥有一个坚固的基础结构是非常重要的,该基础结构能使应用代码专注于实现具体的业务逻辑,不用关心处理其他问题。这是我们选择使用J2EE的原因;也是如果我们需要编写过度复杂的代码时,选择使用EJB的原因;也是充分的基础结构类几乎和应用服务器同等重要原因。

我们通常需要亲自构造部分基础结构,但是在有选择余地时,最好选择已经存在的基础结构。

不幸的是,现在还没有一个简化J2EE使用的标准。因此,不同的项目会作出不同的选择。基础结构可能来源包括:

现有的自产的基础结构。大多数大型的组织机构开发了许多常用包,尽管他们的质量不同。通常,这样的基础结构没有一个合理的策略,失去了重用的可能性。这是一个花费巨大但丢失的重用性的机会;在这一章中,我们将会看到代码可以被重用的许多方面以及在不同项目中采用一致习惯是非常有益的。

MVCweb用用框架。一些产品(如Struts)能为web应用提供一个框架而不仅仅是一个MVC的实现。不过MVCweb框架只打算提供web应用中使用;让所有的基础结构都基于这个web框架可能使测试非常困难,并且减少了重用的可能性。

开源框架。在这个领域还没有任何一个框架像领先的MVC web框架一样有名并得到广泛的使用,许多产品是非常复杂的,笔者在此无法推荐一个这样的产品。

商业产品,例如同应用服务器一起提供的框架或者承诺简化J2EE开发的框架。在这个领域同样没有一个产品被广泛使用。来自应用服务器提供的框架把用户限定在了具体的应用服务器中,进而取消了应用服务器直接移植的可能性。

本章所描述的,解决了许多常见问题的基础结构代码。这个基础结构相对简单,广泛应用JavaBeans最小化应用代码对它的依赖,尽可能避免牵连太深。

在这章中我们看基础结构的问题主要通过分析在示例应用使用的支持包。这写代码依赖笔者在一些实际项目的成功经验来阐释一些主要问题。

坚固基础结构的目标

使用坚固的标准基础结构能提供一个更快、更好的应用。一个强有力的基础结构通过实现以下目标使得这成为可能:

允许应用代码集中精力在实现业务逻辑,最小精力分散在其他应用功能。这通过减少开发者尝试而减少上市的时间按,通过在项目的生命周期内使得应用代码更具有可维护性而减少花费。这是一个终极目标,下面的许多目标都帮我们达成这个终极目标。

把配置从java代码中分离出来。

通过排除一般的妥协促进OO设计的使用。

通过每个问题只解决一次,消除代码重复。一旦我们有了一个好的解决方案,就应当始终使用这种解决方案。

隐藏J2EE API的复杂性。我们已经在JDBC中谈论过这个目标。

确保正确的错误处理。

如果有必要,应当使国际化更便利。

在不违背架构原则的前提下改进生产效率。如果基础结构设计不充分,势必会急功近利的修改将会引起后续问题。合适的基础结构应当鼓励和促进充分设计原则的应用。

组织机构内的应用之间应获取一致性。

确保应用容易被测试。在凡是可能的地方,一个应用应当允许应用代码不用部署在应用服务器中就能被测试。

即使我们开发的是一个web应用,基础结构不应当以web为中心。这使得不会引入必要的困难到测试中,也不会依赖一个特殊的用户接口。这样的基础结构不应当受限于一个诸如Struts类的基础结构。只是从表示层产品分离出业务逻辑是不够的:业务逻辑根本不应该出现在web特有的类中。例如,StrutsAction类束缚于Servlet API。这是的StrutsAction是一个参与业务逻辑的一个极差的场所。因此,尽管使用web框架很重要,web层依然是位于不同业务逻辑层上面的一个薄层。

基础结构将采用框架的形式,而不是类库的形式。通常,控制反转对应用代码消除复杂性是非常重要的。

一个框架应当使可扩展的。虽然一个良好设计的框架将满足大多数需求。但除非他能够扩展以满足未预计到的需求,它最终将会失败。在框架中使用接口将帮助我们实现这一目标。

一个框架应当很容易被使用。过度复杂的框架将会被开发者所忽视,而且会引发他们自身许多的问题。不相关的或者极少使用的功能将是一个主要的危险。巴雷托定律与框架设计非常相关。一般来说,一个能解决大多数问题的简单解决方法,要比一个全面但使许多任务变得困难的复杂解决方法更令人满意。

现在来看看示例应用中使用的核心基础结构。同大多数成功的基础结构一样,这里所描述的基础结构是分层的,当使用它时,我们可以选择从不同的抽象级别中进行选择,每一层都建立在下一层的基础构建上面。

使用框架配置应用组件

让我们从实现外部配置和促进OO设计的目标开始。

问题

在应用配置中采用一直的风格是非常重要的。外部化配置对参数化应用是必不可少的,但在没有支持性基础结构的前提下实现起来非常困难的。

没哟这样的基础结构,配置管理通常是无规则的。J2EE应用通常以多种形式(例如:属性文件,XML文档,EJBJARWAR开发描述符,数据库表)保存配置。在大型应用中配置怎样被管理没有一致性。例如,属性命名约定在不同开发人员或者不同的类之间是不同的。

更严重的,应用类将会因为不相干的配置管理代码的业务责任而变得非常庞大。许多应用对象可能需要读取属性文件或者解析XML文档。即使,他们使用帮助类简化了底层API的使用,他们的业务目的是模糊的,并且依然束缚于最初设定的配置格式。

更加严重的是,如果没有一个用于配置管理的中心基础结构,应用代码很可能会使用多种查找对象位置的方法。单例设计模式经常被过度使用,进而导致下面将要讲解的问题。作为一种选择,一些应用对象可能会绑定到JNDI上,或者附加到全局的ServeletContext中。这两种方法都可能是JNDI服务器外部的测试更加复杂。每一个应用对象通常独立被重新装配。

使用JavaBeans

使用基于bean的操作非常适用于应用配置数据被保存在Java代码外部的应用,也适用于数据绑定,例如用Http请求构造出Java对象状态。

如果把所有的应用都是JavaBeans组件,我们就最大限度的提高了配置数据从应用分离的能力。我们也能确保应用组件能以一致的方式呗配置,无论配置数据保存在什么地方。即使我们不知道一个应用的运行时的具体类,我们仍然知道如何配置它,只要它是一个JavaBean

一个对象不需要实现任何特殊的接口或者扩展一个特殊的超类来成为一个简单的JavaBean,暴露bean的属性但是不支持bean的事件。所有这些需要一个无参构造函数和若干遵循简单命名约定的属性方法。不像EJBJavaBeans不增加任何运行时开销。不过处理beans更加复杂,需要使用反射来实例化对象和通过名称来调用方法。核心JavaBeansAPI没有提供在JavaBean上执行一些有用操作的能力,例如:

在一个操作中设置多个属性,在一个或多个属性设置失败时,抛出一个组合式异常。

获取或设置嵌套属性,即属性的属性,例如getSpouse().getAge().java1.4引入了这种支持,通过java.beans.Statementjava.beans.Expression类。

在不使JavaBeans的实现更复杂的情况下支持组件事件传播。在只使用了标准beansAPIbean类才会通过调用像java.beans.PropertyChangeSupport类似的API支持自己的管道工程。

提供一个特殊的方法在所有bean属性被设置以后执行初始化。

因为通过方法名调用方法和初始化beans意味着处理来自核心反射包中的异常,错误处理也会变得十分笨拙,除非我们施工更高级别的抽象。

Spring的最底层结构是org.springframework.beans包。这个包提供了轻松处理beans的能力,并增加了前面描述的功能,同时遵循和避免重复javaBeans基础结构。

这个包的核心是BeanWrapper接口和默认实现BeanWrapperImpl.一个BeanWrapper对象可以再给定任意JavaBean的条件下被创建,而且提供了个别或批量处理属性和增加监听器的能力。这些监听器在使用BeanWrapper处理组件时被通知。在BeanWrapper接口中最主要的方法包括:

ObjectgetPropertyValue(String propetyName) throws BeansException

这个方法在给定一个遵循JavaBeans约定的string类型的名称,会返回一个属性值。String类型的”age”将会返回通过getAge()方法暴露的属性。不过,所有的BeanWrapper方法都支持任意深度的嵌套属性:string字符串”spouse.age“将尝试获取spouse属性对象上的age属性。即使spouse属性的被声明类型没有暴露一个age属性但只要当前值暴露了一个age属性,这个串仍将工作。

SetPropertyValue()方法是一个用哟下列签名的并行设置器方法:

voidsetPropertyValue(String propertyName, Object value) throwsPropertyVetoException, BeansException

这个方法抛出java.beans.PropertyVetoException,允许使用事件监听器否决属性的改变,就像JavaBeans规范中描述那样。如果需要,新的参数值可以使用标准beansAPI PropertyEditor支持,从一个string中转换过来。另一个设置器有以下的签名:

voidsetPropertyValue(PropertyValue pv) throws PropertyVetoException,BeansException;

这个方法类似于前面的setProperty()方法。但接受一个保存了属性名和值的简单对象。这是允许组合属性更新所必须的,就跟下面的方法一样。

voidsetPropertyValues(PropertyValues pvs) throws BeansException;

这个方法在一个批量更新中设置多个属性。PropertyValues参数包含有多个PropertyValue对象,当遇到个别属相设置异常是,属性设置继续进行。任何异常都被保存在一个统一的异常PropertyVetoExceptionsException中,这个异常在整个操作完成是立即被抛出。设置成功的属性依然被更新,批量属性更新在许多情形下都是有用的,比如当填充来自HTTP请求参数的JavaBean时。

BeanWrapper接口包含一些便利的方法查找属性是否存在,例如:

booleanisReadbleProperty(String propetyName)

使用下面的方法获取被BeaWrapper包装的对象是可能的。

ObjectgetWrappedInstance()

这个能使我们在bean处理完成以后立即转到普通JavaBeans的调用。

使用下面的方法可以增加和删除事件监听器:

voidaddPropertyChangeListener(PropertyChangeListener 1);

voidaddVetoableChangeListener(VetoableChangeListener 1);

这就意味着,如果BeanWrapper用来处理它的bean属性,属性更改事件将要被触发即使一个正在被操作的JavaBean没有实现属性更改支持。

笔者采取了这个重要的设计决策以便是beans的异常体系编程为检测异常。由与在初始化代码中常常使用beans,这个决策是正确的;未能处理的命名属性故障通常是致命的。不过让应用代码捕获仍然是可能的。BeanException的几个子类包括:

TypeMisMatchException,当不能转换一个属性值到所需要的类型时被抛出。

NotWritablePropertyException,尝试设置一个不能被写的属性时被抛出。

MetodInvovationException,setter或者getter方法抛出异常时抛出。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值