本人翻译目的是用来学习Tapestry5的,共享出来希望大家批评指正。计划持续翻译。
chinajavawolf
Tapestry IoC 容器
Tapestry框架的内部结构基于IoC控制,这种设计方法允许工作系统被许多小的,易测试的块构成。
使用 IoC(控制倒转) 的额外好处是,通过打破一个复杂的系统为小的块,使它变为比较容易修正和扩充的系统(通过丢弃或更换系统的小块)。
在
Tapestry
中使用
IoC
代表了从
Tapestry3
到
Tapestry4
到
Tapestry5
的发展。
Tapestry3
没有使用
IoC
,尽管它包括一些不牢固机制,例如扩展,但它服务于同样的目的。要使
Tapestry
的行为有大规模的变化需要子类的关键类和最重要的方法。
Tapestry4引入了 HiveMind IoC容器。事实上,HiveMind 工程被明确创建为Tapestry4使用的IoC容器。Tapestry4
已经基本上实现了可扩展性和可配置性的目标,这主要是因为HiveMind的弹性。
Tapestry5
扩充了这些,使用了一个明确为
Tapestry
建造的容器代替
HiveMind
,为更易使用,表现和性能设计。并且他可以被其他的
Tapestry
独立使用。
-
为什么不是Spring?
Spring
是一个非常成功的
IoC
容器工程。
Spring
工程结合了一个非常好的
IoC
容器,集成了
AspectJ
支持和很多构建在容器之上的库。
Spring
是一个很好的应用容器,但是对于框架容器它缺少很多
必须的特性:
1.
Spring beans可以通过name(或id)被连在一起,但是它不可能引入附加命名抽象。Tapestry4
的"infrastructure:"
抽象的关键在于它允许你可以很容易地现场替换
Tapestry
内部服务而无须复制大的
web
的相关服务
(
将近
200
在
Tapestry4.0)
2.
虽然Spring允许beans被拦截,但是,它以一个新的bean的形式这么做,让未被拦截的bean可见(并不可能会被滥用)。HiveMind和Tapestry IoC
“
包裹
”
服务在拦截器内,防止未拦截的访问执行核心服务。
3.
Spring的XML配置文件相当冗长,与同等的HiveMind XML 文件相比,也常常如此。这在Spring 2.0中已经被改进了。
4.
Spring有一个简单的 map/list/value 配置方案,但它不是分散的。它是一个单独的bean定义的一部分。HiveMind和Tapestry5 IoC允许服务配置从多个模块装配。这是非常重要的对于框架的无缝扩展,使用零配置(仅仅放下模块在 classpath 内然后和所有钩子一起)。
-
为什么不是 HiveMind?
管理两个复杂框架的发布进度的困难已经被证实是个问题了。
HiveMind
的
2.0
版将融合那些当前在
Tapestry5 IoC
容器内的思想,但也将继续维持遗留所支持的现有
XML-driven
方法。
HiveMind的使用也涉及到一个Tapestry4中的一个共同问题:启动时间。这个时间是用来解析和组织所有XML显示的,要用几秒的启动时间。它被希望创建一个改进的IoC容器不用依靠XML驱动,这将减少很多问题。
随
着新技术
(
尤其是
JDK1.5
的标注
,
和通过
Javassist
产生的运行时类
)
的到来,
HiveMind
中的一些规则已遭到破坏。这就是说,在
HiveMind(
和
Spring)
内,所有
XML
都是一种笨拙的方式来描述几个基本的
Java
操作
:
具体的类和在那些类上调用的方法 (注入依赖在具体的实例上)。
Tapestry IoC
中的重要概念就是消除
XML
并且在简单对象和方法周围构建一个同等系统。
Tapestry IoC
同样代表了很多
HiveMind
的简化,代表了当创建
HiveMind
和
Tapestry4
时从两者学来的经验教训。
目标
关于
Tapestry5,
大体上,
Tapestry IoC
的目标是更简单,更健壮并且避免
XML
。
现存的IoC容器比如HiveMind和Spring包含了大量的XML配置来描述怎样和何时实例化一个详细的JavaBean,和如何使用依赖提供给那个bean(不是通过构造器注入就是通过属性注入)。其他的XML被用来钩住对象进入生命周期内。。。当对象被实例化和配置时,或者当它被丢弃时,典型的callback方法被调用。
Tapestry IoC的核心概念是Java语言自身是更容易和简洁描述对象生产和方法调用的方法。在XML中的任何近似方法最终都是更冗长和更难处理的。就像例子显示的,一小块Java
代码和一些命名约定还有标注要比一大块XML更简洁和容易。
另外,从XML转移到Java代码更便于测试;你可以对你的模块构建者类的服务构建者方法进行单元测试,但你不能如实地单元测试你的XML描述符。
Tapestry IoC模块更容易打包成JAR文件,支持零配置使用:仅仅放置JAR到classpath上。
另一个目标是“开发者友好”。
这是一个真实的横切关注点
,
并不太可能任何时候都很快被包装成一个切面。
Tapestry IoC
框架被设计为容易使用和理解的。此外,当有错误发生时,它都积极尝试通过全面的检查和准确的错误信息帮助你。另外,所有用户可见的对象都实现了一个合理的
toString()
方法,以此来帮助你理解是怎么回事,当你不可避免的试图在调试器中解决问题时。
在使用
Tapestry IoC
构建服务方面。。。目标是“灵活”,借用棋盘游戏的术语。两个游戏者放置石头在开始的空棋盘上,创建围墙来圈入领土或者除去对手放置的石头界线。游戏最后胜利者要控制最大多数的领土,
而且玩这个游戏,在占据领土和防护现有领土之间是不变的制约平衡。玩石头的地界要“
轻巧灵活”(或者有“好的形状”),当在棋盘上用最少的石头控制最大的面积时。如果玩石头的地界是“繁琐笨重”的正好给你的对手一个好机会去控制棋盘上的另一个区域。
在软件开发中,我们也努力通过简单的块创建复杂的系统,但是我们的制约平衡源自增加功能性平衡的需求与测试和维护现有代码的需求相反。通常在软件开发的世界里,所有增加功能性,测试和维护的请求都是被延期的。。。。直到为时已晚。
IoC
容器是的一般性的,
Tapestry IoC
非常明确,尝试解决这个问题,为平衡
快速增加功能性的需求和相对的测试新的功能性和维护现有功能性的需求提供基础。
IoC
容器提供手段来打破大的,复杂的,整块的为简单的,小的,可测试的块。
当构造一个服务的注册库(
registry
)时
,
灵活性是指以适当的责任分工
,
分离关注并限制系统的各部分依赖关系。这种风格常被称为迪米特法则(
Law of Demeter
)
.
使用
IoC
容器使它更容易包含这个方法
,
因为临界相关
,
哪个对象负责实例化哪个其他对象
,
完全是通过容器管理。除去生命周期关系,它变得非常容易将复杂的大块代码缩减成小的,可测试,可再用的服务。
"轻巧灵活" 的含义:
1.
有两个或三个方法的小接口
2.
有两个或三个参数的小方法(因为依赖被注入在后台优于在方法中传递)。
3.
匿名通讯途径事件优于外在方法调用。这个服务实现可以实现一个事件监听接口。
术语
在Tapestry IoC内的基本单位是一个服务。一个服务由一个服务接口和一个服务实现组成。这个服务接口是一个普通的Java接口。这个服务实现是一个实现了服务接口的Java对象。通常,每个服务接口将只会有一个单独的服务,但是在某些情形下,有可能是很多不同的服务和服务实现都共享这个相同的服务接口。
服务通过一个唯一的
id
被识别。典型的,一个服务的
id
匹配这个服务接口的绝对名,但这仅仅是个约定。
服务被合成到模块内:
1.
一个模块通过模块构建器被定义
,
一个具体类
,
包含各种静态或实例方法,用来确定服务
,
装饰他们
(
见下文
),
或贡献用于服务配置
(
再看,更下面的下文
).
2.
模块构建器类的方法定义的这个服务通过这个模块被提供,并且同样的方法负责实例化这个服务的实现。
这个被定义的方法和构造服务被称为服务构建器方法。.
注册库(registry)是这个模块和服务的外界视图。从注册库获得服务是可能,经由唯一的id或通过它的服务接口。通过唯一id访问是万无一失的。(意味着,一个匹配将被发现即使key值搜索不匹配服务自身)。
服务可能通过服务修饰器方法被修饰。这些方法创建了拦截器对象,捆绑在核心服务实现的周围,添加行为,比如日志,安全访问,或者是事务管理。拦截器实现相同的服务接口作为服务。控制放弃被应用给服务的修饰器内的顺序。
服务可以有一个配置。这个配置可以是一个
map
,或者是一个
collection
,或者是一个有序的
list
。这个服务定义的对象类型允许被贡献在配置中。这个配置的构造来自于一个或多个模块提供的贡献。服务构造器方法被调用给配置中的贡献对象。
注意:在HiveMind中,服务和配置是隔离的,这往往导致连接一对相同名字的服务和配置。对于Tapestry IoC,每个服务都被允许有一个单独的配置,这通常足够了。
服务被实例化是被需要的(need)。在这种情形下,“need”转变为“当一个服务的方法被调用时”。一个服务被表现(对于外界,或对于其他服务)为一个实现了服务接口的代理。首先一个方法在代理上被调用,整个服务(由实现了任何包裹了的拦截器的核心服务组成)被构造。这发生在一个完全线程安全的方式下。JIT(即使生产Just-in-time)实例允许更复杂,更细粒级的网络服务,并且改善启动时间。
当这个服务被构造又可见时,服务要定义一个控制范围。默认的范围是单例
(singleton),
意味着当被需要时一个单独的目标实例被创建。其他的范围允许服务实现被绑定的当前线程(
即目前要求在一个
servlet
应用内
)。
依赖是一个服务实现需要其他的服务(或其他的对象)。这些依赖可以被注入在服务构建器方法中并给予提供,从那,经由他的构造器或经由他的服务实现上的方法给一个服务实现。
这些也可以作为合作者被查阅
,
尤其在写的单元测试的上下文内。