InfoQ:Spring 2.0 的新特性和应用实践
主题
Spring开源项目开始于2003年2月,现在框架正变得越来越强大。目前已经达到了超过一百万的下载量;在很多行业范围内成为事实上的标准;并且改变了企业Java应用的开发过程。
最重要的是,它发展了大量而且忠诚的用户,他们理解框架的关键价值并且共享反馈,来帮助框架高速发展。Spring的使命也一直很清晰:
提供一种非入侵性编程模型。应用程序的代码应该尽可能地与框架解耦。
为内部基础设施提供一种优秀的解决方案,以便开发者能将注意力放在交付业务价值,而不是解决普通的问题。
使开发企业应用程序尽可能简单、增强,而不是减弱、机械。
在2006年10月份进入最终版的Spring 2.0,更加提升了这些价值。在核心团队在2005年佛罗里达Spring体验大会前研究发展特性时,我们发布了两个关键的主题——简易性和强大——突出作为Spring 2.0的主线,并且依旧忠实于Spring的使命。
某些决定很容易。从一开始,我们很清楚Spring 2.0将会完全向后兼容,或者说尽可能地完全向后兼容。尤其考虑到Spring在很多企业中作为一个事实上的标准这样一个定位,避免任何对用户体验的破坏是非常重要的。幸运地是,Spring一直致力于非入侵,这样的目标就完全可以达到。
在十个月的Spring 2.0开发过程进行中,我们也需要考虑到一些Spring在2005到2006年的使用中越来越明显的趋势:
Spring越来越多地被一些非常大的组织来使用,从战略的角度而不是只从项目角度来采用。这不仅意味着关于向后兼容的责任,而且是与大量不同类别的用户相关的挑战。
越来越多数目的优秀的第三方软件产品正在内部使用Spring,并需要容器的配置优化和灵活性。这样的例子很多,这里简单列举几个:
即将发布的BEA WebLogic Server 10,使用了Spring和Pitchfork项目来执行注入和拦截。
BEA WebLogic Real Time(WLRT),来自于BEA的一种高端产品,致力于像前端办公交易这样的应用,需要很低的等待时间。
大量广泛使用的开源产品,比如Mule、ServiceMix以及Apache JetSpeed门户容器。
一些企业厂商使用Spring集成他们自己的产品,比如GigaSpaces,Terracotta和Tangosol等。尤其是网格空间的公司,正在逐步投入Spring作为编程模型的选择。
Oracle的SCA实现,以及不同的其他Oracle产品。
因此我们需要确保当Spring变得对企业应用开发者更加友好的同时,也要迎合这些苛刻的用户。
Spring 2.0的最大愿景是什么?
Spring 2.0提供了很大范围内的增强,其中最显著的可能是:
配置扩展:在Spring 2.0中,Spring支持可扩展的XML配置,使得使用自定义元素开发成为可能,它们为生成Spring bean的定义提供一种新层次的抽象。XML扩展机制同样提供了一些新的标签来简化许多普通的任务。
在AOP框架中有重要增强,使得既强大又更易于使用。
增强对Java 5的支持。
提供以动态语言实现Spring bean的能力,比如Groovy、JRuby和Beanshell,同时保留Spring组件模型的所有服务,比如依赖注入,方便的声明性服务以及AOP。
以及许多新的特征,包括一个Portlet MVC框架,“消息驱动POJO”,与新的API的集成,包括JAVA持久化API(JPA),以及一个异步任务执行框架。
有许多表面上不是很明显的特征,但仍然很重要:
对Ioc容器更进一步的扩展,使得在Spring之上构建框架或产品更加容易。
对Spring特有的集成测试支持的改善。
提供AspectJ来暴露Spring核心功能给使用AspectJ和Spring的用户,比如事务管理和依赖注入。
重要的是,这些特性被设计以和谐的整体来一起运行。
现在就让我们更深入地研究一些新的特性,并且使用一些代码范例来描述它们。
XML配置扩展
在Spring 2.0中最明显的增强就是XML配置。
Spring Ioc容器实际上是独立于元数据的表示的,比如XML。Spring以Java对象(BeanDefinition以及它的子接口)的形式拥有自己的内部元数据。有一个对XML配置补充的研究,比如使用注解的Java配置。
然而在今天,XML是被最多用在配置Spring上的,这就是Spring核心中配置改进的焦点。
Spring 2.0中XML的增强巧妙概括了简易性和强大的主题:它们简化执行某些普通的任务,但是也使得一些额外的高级任务成为可能。
目标
传统上,在Spring的XML配置语法和Spring的内部元数据之间有一对一的关系。一个元素产生一个BeanDefinition。
这通常就是我们想要的,并且对于配置那些框架并不了解的应用程序类,也是理想的。
但是,如果框架应该了解一个可能被反复使用的特定的类呢,比如象JndiObjectFactory这样的普通的类,用来从JNDI寻找对象,并将其作为可注入的对象注入Spring容器,或者如果一些Bean定义只是在一起使用时才有意义呢?
这样,一种新的抽象形式就能带来重要的好处。
方便的命名空间
Spring 2.0提供一些方便的命名空间。其中最重要的是:
事务管理(“tx”):在Spring 2.0中使Spring bean能够处理事务变得相当容易,就像我们看到的那样。它同样使定义“transaction attributes”来映射事务行为到方法上变得简单的多。
AOP(“aop”):Spring 2.0中专门的用来AOP配置的标签比以前更加简洁,Ioc容器不再需要依赖AOP框架。
Java EE(“jee”):这样简化了对JNDI和其他Java EE API的使用,正如我们看到的那样。EJB lookup比JNDI lookup获得的简单性还要多。
动态语言(“lang”):以动态语言简化bean的定义——Spring 2.0的一个新特性。
Utils(util):简化加载java.util.Properties对象和其他通用的任务。
在Spring2.1以及往后版本中,将会加入更多的命名空间,来将简单性引入新的领域。简化Spring MVC和JPA使用的命名空间可能会最先加入到核心中去。
第三方配置扩展
作为一种扩展机制,Spring 2.0命名空间最重要的可能性是在Spring核心的外围。
许多产品构建于Spring基础之上,它们的配置可以使用命名空间来变得更加简单。一个很好的例子是Acegi Security for Spring(将在2007年早些时候改名为Spring Security),它需要配置一些协作bean的定义。Spring 2.0的命名空间会使这变得非常简单。再一次更加清楚地表达了简单性的意图。
许多产品和Spring紧密集成,这样的好处不言而喻。Tangosol对Coherence的集成就是现成的案例。
其他潜在的例子包括支持Spring配置的产品,比如IBM的ObjectGrid。虽然ObjectGrid目前没有在内部使用Spring,但它被设计成通过Java来配置,使得能更加容易地集成到基于Spring的应用程序中。扩展schema会让这个变得相当简单。
一个XML文档使用某个扩展标签作为顶层的元素是可能的。这样避免需要通过命名空间给扩展schema元素加上前缀,意味着这样的配置看起来更自然一些,而非以Spring为中心的。(通常,元素是在缺省的命名空间,因此传统的Spring bean定义并不需要前缀。)
随着时间过去,和JSP自定义标签的发展,经验会通过实证明了的价值引出通用目的的标签。我们期望用户来创建命名空间的库,来让这个社区受益。
实现XML扩展
实现命名空间相对简单。它分三步:
定义你的XML schema。这是最困难的一步,需要有合适的工具。对于schema没有限制,当然你需要明白它是如何在运行时引导BeanDefinition的生成的。
实现NamespaceHandler接口,从你的schema中的元素和属性来产生BeanDefinition。
编辑一个专门的注册文件,spring.handlers,来让Spring知道新建的NamespaceHandler类
为了简化BeanDefinition元数据的生成,Spring 2.0引入了一种方便的新的BeanDefinitionBuilder类,提供一种流畅的、构建器风格的API。开始实现NamespaceHandlers的最佳指导是那些存在于Spring核心中的类。其中UtilNamespaceHandler是个相对简单的例子;而AopNamespaceHandler是个比较高级的例子,它解析了一个复杂的子元素结构。
最佳实践:什么时候应该定义你自己的命名空间?
你有锤子并不意味着其他一切都是钉子。正如我们已经看到的,Spring 2.0的XML扩展机制在很多案例中交付了很大的价值。然而,如果没有很好的理由就不应该使用它。因为XML扩展标签是一种新的抽象,它同样提供了一些需要学习的新的内容。Spring的正规的XML格式对成千上万的开发者来说已经很熟悉了,甚至对那些新接触Spring的人都是用直觉就可以判断的。Spring XML文件提供了易于理解的某个应用程序结构的蓝图。如果过度配置使用了不熟悉的自定义标签,就没什么必要了。
让我们在这个领域内考虑一些相关的经验。JSP自定义标签是个很好的例子。最终它们通过设计得很棒的标签库,比如JSTL,Struts和Spring MVC的标签库,产生了真实的价值。但在早些年,它们会引起厌恶,甚至是混乱的JSP页面。(我在这里可以根据经验来解释,因为我自己实现了一两个这样的标签库)。
把命名空间处理器看作是一个重要的新的扩展点,以及对Spring很有价值的新的抽象。它们对于那些在Spring之上构建第三方产品的人来说非常棒;它们对于非常大型的项目也很有用。很快就会发现,没有了它们,很难想象生活会变成什么样子。但是最终用户还是应该对实现它们持谨慎态度,但使用没有问题。
当然,伴随Spring提供的方便的扩展标签,比如aop,tx以及jee命名空间,将很快成为Spring配置词表的核心部分,就跟元素一样被广泛了解。你当然应该优先使用这些,而不是传统的冗长的方式,来完成相同的任务。
语法糖
转向使用schema也允许一点点快捷方式,比如对property值使用attribute而不是子元素。这些attribute不会被验证,但因为我们使用的是XML schema,而不是DTD,我们仍然可以保留所有其他的验证。因为attribute名称就是property名称,XML验证不会再添加任何东西;这是基于Java的验证的问题,而不是XML结构的。
除了XML配置扩展,在Spring Ioc容其中还有很多其他的新的特性。
其他Ioc容器增强
新的bean作用域
和XMl扩展一起,最重要的新的Ioc容器特性就是对于bean生命周期管理的新增的自定义作用域。
1.背景
Spring之前为bean提供了两种作用域:单例和原型(或者叫非单例)。
Singleton bean是一个在所属容器上下文中的单例对象。在容器的生命周期中只会有一个实例存在,当容器关闭,它就会向所有需要知道容器关闭事件的单例bean发送事件通知——比如,关闭任何可以管理的资源,像连接池。
Prototype bean是任何时候通过注入到另外一个bean而被引用,或者是对所属容器上getBean()调用的响应时创建的。在这种情况下,bean定义与一个单个对象没有关系,而是一个用来创建对象的配方。每个创建好的实例会有完全相同的配置,但会有不同的身份。Spring容器不会持有对原型的引用;它的生命周期由获得它的那段代码来负责。
在Spring 2.0中,我们添加了自定义作用域的能力。可以给它们起任何名字。某个自定义作用域通常与能够管理对象实例的后端存储相对应。在这种情况下,Spring提供了它熟悉的编程模型,支持注入和查找,而且后端存储提供了作用域内对象的实例管理。
典型的后端存储有:
HTTP session
Clustered cache
其他持久化存储
2.Web作用域
对于这个特性,最通常的需求是关于在web应用程序HTTP session中透明存储对象。这在Spring 2.0中得到很方便的支持。
3.其他可能性
无疑,在真实的Spring风格中,底层的机制是可插拔的,而不是绑定到Web层的。比如,Tangosol Coherence作用域可以像下面这样使用,通过Tangosol和Interface21提供的“datagrid”命名空间。
在一个“datagrid”作用域内声明一个bean,意味着bean的状态管理是由Coherence来执行,在一个Clustered Cache中,而不是在本地的服务器上。当然,bean是由Spring来实例化和注入的,而且可以从Spring服务中受益。我们预见特定作用域内的bean会在SOA和批处理的环境中非常有用,并且期望能在将来Spring的版本中添加更多方便的bean作用域。
4.自定义作用域
定义你自己的作用域很简单。参考手册详细解释了这个过程。你将需要一种策略来辨别如何在当前的作用域内解析出对象。典型的情况下,这会涉及到ThreadLocal,就像Web作用域在底层所做的那样。
Java 5
Spring 2.0保持向后对Java1.3和1.4的兼容。然而,Java 5带来越来越多的新特性。
其中一些,比如已经讨论过的类型推断,可以随意使用。而其他的需要自己来选择。让我们快速预览其中的一些。
新的API
大量的新的API在核心功能上提供Java 5的功能,这些核心功能继续运行在Java的早些版本上。
尤其是:
SimpleJdbcTemplate:和熟悉的JdbcTemplate类似的新类,这使得JDBC使用更加简单。
AspectJProxyFactory:和ProxyFactory类似的新类,设计用来使用@AspectJ 切面以编程方式创建代理。
随着时间延续,这样的类的数目会越来越多。