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服务。
ResourceLoadingService是一个可以从各种输入源中(例如从File System、Classpath、Webapp中)查找和读取资源文件的服务。有点像Linux的文件系统 —— 你可以在一个统一的树形目录结构中,定位(mount)任意文件系统,而应用程序不需要关心它所访问的资源文件属于哪个具体的文件系统。
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的定义如下:
注:这里使用了XML Schema中的<xsd:any>定义。意为在<resource>元素下可以有任意多个符合条件的子元素。
(1) 扩展点, ConfigurationPoint, 将名称空间和可扩展的ResourceLoader接口关联起来。代表一个可被扩展的接口。
唯一的名称,唯一的namespace,唯一的schema
(2) 捐献,Contribution, 将元素element和ResourceLoader的具体实现关联起来。代表一个对扩展点的具体实现。
在对同一扩展点的所有捐献中,拥有一个唯一的名字。
对应一个唯一的schema。
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自带的完全相同。但只要添加类似下面的配置,就可以增强该功能。