Webx是一套基于Java Servlet API的通用Web框架。它在Alibaba集团内部被广泛使用。从2010年底,向社会开放源码。
Webx框架是一个稳定、强大的Web框架。建立在SpringExt的基础上,具有超强的扩展能力。你可以使用全部的Webx,也可以使用部分Webx。你也可以比较容易地用SpringExt做出自己的可扩展组件。
Webx的优势
成熟可靠性
Webx在阿里巴巴和淘宝用了很多年。对于这种超大访问量的电子商务网站,Webx经受了考验,被证明是成熟可靠的。
开放和扩展性
对Spring的直接支持 —— Spring是当今主流的轻量级框架。Webx 3.0和Spring MVC一样,完全建立在Spring框架之上,故可运用Spring的所有特性。
扩展性 —— Webx 3.0对Spring做了扩展,使Spring Bean不再是“bean”,而是升级成“组件”。一个组件可以扩展另一个组件,也可以被其它组件扩展。这种机制造就了Webx的非常好的扩展性,且比未经扩展的Spring更易使用。
开放性 —— Webx被设计成多个层次,层次间的分界线很清晰。每个层次都足够开放和易于扩展。你可以使用全部的Webx,也可以仅仅使用到Webx的任何一个层次。
框架设计理念
框架的本质就是"扩展". 一个软件框架必须符合以下要素:
- Inversion of Control. 控制反转. [应用的流程不是由应用控制, 而是由框架控制.]
- Default Behavior. 默认行为. [框架会定义一系列默认的行为]
- Extensibility. 扩展性. [应用可以扩展框架的功能, 也可以修改框架的默认行为.]
- Non-modifiable Framework Code. 框架自身的代码不可更改. [框架可扩展, 但不需要改变原本的代码]
一个Web框架的好坏, 往往不是由它所实现的具体功能的好坏决定的, 而是由其所用的基础框架的好坏决定的. Webx建立在SpringExt的基础上, SpringExt是对Spring的扩展. 没有损失任何Spring的功能, 但能提供比Spring自身更强大的扩展能力.
设计良好的模块, 应该是层次化的.
- 上层定义规则, 下层定义细节. 上层是抽象的, 下层是具体的.
- 越上层, 越稳定(少改变). 越下层, 较易变.
- 依赖倒转: 下层依赖于上层, 具体依赖于抽象, 而不是上层依赖下层.
- 开闭原则: 下层扩展上层时, 不需要修改上层的任何代码和配置.
- 每一层均可被替换.
Webx鼓励层次化的模块设计, 而SpringExt提供了创建和配置层次化组件的机制.
设计一个层次化的组件, 可以从下面几方面来考虑.
- 切分功能. 每个组件专心做一件事.
- 分析哪些会变, 哪些不会变. 不变的部分固化在组件中, 可能会改变的部分抽象成接口, 以便扩展.
- 考虑默认值和默认扩展. 默认值和默认扩展应该是最安全, 最常用的选择. 对于默认值和默认扩展, 用户在使用时不需要额外的配置.
Webx的层次
SpringExt
SpringExt完全兼容Spring原来schema的概念和风格,但是却可以让schema像程序代码一样被扩展。Webx完全建立在SpringExt的基础上。这个基础决定了Webx是一个高度可扩展的框架,其配置虽然灵活,却又不失方便和直观。
用SpringExt装配服务
以ResourceLoadingService为例。尝试在Spring容器中装配ResourceLoadingService服务。
1. Sring2.0之前, 只能通过装配beans的方式
简单易行, 很好地体现了IOC。 ResourceLoadingServiceImpl并不依赖于具体实现, 它只依赖于它们的接口 ResourceLoader。 如果创建这些具体实现的对象, 由Spring负责。而需要哪些参数、如何装配和注入, 由应用程序的装配者Assembler通过上述配置文件来通知Spring。
在本例中, 装配者负责把ResourceLoadingService和ResourceLoader等其他服务装配在一起, 使它们可以协同工作。
缺点:
- 没有验证机制。错误必须等到运行时才会被发现。装配者仅从spring配置文件中, 无法直观地了解该文件是否正确地书写,是否使用了正确的注入方式。
- 无法了解更多的约束条件。比如,是否漏写了某些属性, 是否使用了互斥的属性等。
- 当服务的具体实现被修改时, spring配置文件可能会失败。 因为spring配置文件是直接依赖于具体实现的, 而非接口。 这阻碍了服务提供者修改他们的服务实现。
2. Spring 2.0 支持用XML Schema来定义配置文件
优点:
- 简单易读。语言是领域相关的, 避免了bean, property等编程术语。
- 可验证的。通过Schema。
- 包含更多的约束条件。通过Schema。
- 服务的实现细节对装配者隐藏。装配者不需要知道ResourceLoadingService的实现类是什么, 需要什么参数, 这些由对应于schema的解释器来负责解读。
解释器 BeanDefinitionParser。负责将符合schema定义的xml配置, 转换成spring能解读的beans定义。 解释器是由服务的开发者来提供的。——本例中, 由ResourceLoadingService的开发者来提供对应的解释器。
有了解释器,装配者不需要了解服务的具体实现类的API, 它只要遵循标准的XML SCHEMA定义来书写spring配置文件, 就可以得到正确的配置。 如此, 服务提供者就可以自由地改变服务的实现类, 只要服务的接口和SCHEMA不变,装配者就不会受到影响。
将和具体实现相关的工作, 例如提供类名、property名 和类型等工作, 交还给服务的提供者, 使装配者可以用它能理解的语言来装配服务, 这是spring schema所带来的核心价值。
缺点:不可扩展。尽管在API层面, ResourceLoadingService支持任何对ResourceLoader接口的扩展,但在配置文件上,你无法自由地添加新的元素。也就是说,用户不能往spring配置文件里增加自定义的ResourceLoader类型,除非通知服务提供者修改schema,但这就显然违反了面向对象设计中的基本原则 ocp。
从本质意义上讲, schema是API的另一种表现形式。你可以把schema看作一种接口,而接口的实质是服务提供者和使用者之间的合约。可惜,这里只能在传统API层面来贯彻OCP,无法在schema上支持。
无法在不修改老的schema的前提下,添加新的元素, 这大大削弱了spring schema的作用。
3. SpringExt Schema
SpringExt改进了Spring,使得Spring Schema可以被扩展。
这里的配置文件和spring schema中的配置文件差别不大,唯一的区别是引入了一个新的名称空间,把ResourceLoader的名称空间和ResourceLoading的名称空间分开了。这样,就可以在不修改<resource-loading>
的schema的前提下, 添加新的ResourceLoader实现。
比如,要添加一个新的实现 DatabaseResourceLoader:无须通知ResourceLoadingService的作者去修改它的schema。
SpringExt原理
1. 两个重要的概念:扩展点和捐献
上例中的<resource-loading>是由 resource-loading.xsd 这个schema来定义的。它对resourceLoader的具体实现一无所知。在schema中, <resource-loading>关于loaders的定义如下:
(1) 扩展点, ConfigurationPoint, 将名称空间和可扩展的ResourceLoader接口关联起来。代表一个可被扩展的接口。
(2) 捐献,Contribution, 将元素element和ResourceLoader的具体实现关联起来。代表一个对扩展点的具体实现。
2. 组件和包
在上图中,resource-loading是一个服务,它调用了loaders扩展点。而file-loader, webapp-loader等则扩展了loaders扩展点。事实上,resource-loading服务本身也是对另一个扩展点“services”的扩展。services扩展点是webx内部定义的一个顶级扩展点。
在SpringExt中,一个组件既可以扩展其他扩展点,又可以作为扩展点被其他组件扩展。
当你需要增加一个新的扩展时,不需要修改原有的包中的任何内容,只需要把新的jar包加入依赖表中。
3. 获取schema
3.1 XML编辑器读取schemas
普通的XML编辑器获取SCHEMA内容,一种方式是访问schemaLocation指示的网址。但是,在外部服务器上维护一套schema是很困难的。你可能没有外部服务器的控制权,或者很难让服务器上的版本和你的组件版本一致,又或者当你离线时无法使用。
另一种方式是把schema转换成静态文件,然后定义一个标准的XML Catalog来访问这些文件。但这很难让静态文件和你的组件版本一致。
因此,SpringExt提供了两个解决方案。——使用maven或eclipse插件。
使用springExt提供的maven插件,在localhost本机上启动一个监听8080端口的schema server,通过它可以访问到所有schemas:
3.2 SpringExt读取schemas
SpringExt永远不需要通过网络来访问schemas。即使在前面的配置文件中,不是使用localhost,而是外部网址,springExt也不会真的去访问它们。
Spring是通过SpringExt定制的EntityResolver来访问schemas的。SpringExt只关注网址中的后半部分,而忽略前面部分。
SpringExt推荐,总是以“http://localhost:8080/schema”作为你的schemaLoaction网址的前缀。
SpringExt的其它特性
SpringExt实际上是一个增强的Spring ApplicationContext容器。
除了schema扩展机制,SpringExt还提供了一个增强的Resource Loading机制。它能完全取代Spring原有的ResourceLoader功能。
应用程序可以直接使用Spring本身的ResourceLoader功能,其背后的ResourceLoading机制会默默地工作。
如果不加额外的配置,SpringExt context使用的ResourceLoader实现和Spring自带的完全相同。但只要添加类似下面的配置,就可以增强该功能。