`
junli0411
  • 浏览: 135977 次
  • 来自: ...
社区版块
存档分类
最新评论

Spring 2.0的新特性和应用实践

阅读更多

Spring 2.0的新特性和应用实践


主题
Web框架,
事务处理,
AOP

主题

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变得对企业应用开发者更加友好的同时,也要迎合这些苛刻的用户。

从35000英尺

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配置扩展,AOP增强,以及特定于Java 5的特征。

在第二部分,我们将会谈到消息,对动态语言的支持,Java持久化API以及Web层的增强。也会看一下表面以下的一些改进。

现在就让我们更深入地研究一些新的特性,并且使用一些代码范例来描述它们。

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定义只是在一起使用时才有意义呢?

这样,一种新的抽象形式就能带来重要的好处。

考虑一下下面这个例子,关于JNDI的lookup:

<bean class="org.springframework.jndi.JndiObjectFactoryBean" id="dataSource">
<property value="jdbc/jpetsore" name="jndiName">
</property></bean>

这当然要好过以前那些实现Service Locator的令人厌恶的日子,但它并不完美。我们会一直使用这个相同的类。并且(除非我们使用Java 5)没有一种机制来指明“jndiName”属性是必需的,而其他的属性都是可选的。

Spring 2.0添加了一个方便的“jee”命名空间,其中包括允许我们执行同样的JNDI lookup的标签,就像下面这样:

<jee:jndi-lookup jndi-name="jdbc/jpetstore" id="dataSource"></jee:jndi-lookup>

这个比较简单而且清楚。它清晰地表达了意图而且更加简洁。而且schema抓住了jndiName属性是必需的这样一个事实,并有方便的工具支持。其他可选的属性同样在这个schema中得以表达,就像下面这个例子:

<jee:jndi-lookup id="simple">
jndi-name="jdbc/MyDataSource"
cache="true"
resource-ref="true"
lookup-on-startup="false"
expected-type="com.myapp.DefaultFoo"
proxy-interface="com.myapp.Foo"/></jee:jndi-lookup>

请注意在这样一个相对简单的示例中,attribute的名称几乎和被配置的类中property名称一样,这不是必需的。没有必要做一个命名上的 对应,或者在attribute和property之间做一对一的对应。我们同样能够处理子元素内容。而且,正如我早先提到的,我们可以从一个扩展标签产 生我们想要的任意数目的bean定义。

现在让我们来考虑一个更加复杂的例子,其中我们要使用自定义标签的能力来产生不止一个bean定义。

从Spring 1.2开始,Spring就能够识别@Transactional注解,并通过代理来使受影响的bean变成事务。这导致了一种简单的部署模型——简单地 添加注解过的bean,它们就能自动变得可以处理事务——但是建立这样模型需要一些令人心烦的戏法。为了所需的合作对象,需要三个bean定义——一个 Spring AOP Advisor,一个TransactionInterceptor,以及一个DefaultAdvisorAutoProxyCreator来引发自动 的代理。这些bean定义组成了一个咒语,能够在不同的应用程序以不变的状态使用,但是暴露了多过用户不需要知道的细节:

<bean>
class="org.springframework...DefaultAdvisorAutoProxyCreator"/>

<bean class="org.springframework...&amp;lt;b&amp;gt;TransactionAttributeSourceAdvisor&lt;/b&gt;">
<property transactioninterceptor="" name="transactionInterceptor&amp;lt;br /&gt; ref=">
</property>

<bean id="transactionInterceptor">
class="org.springframework...TransactionInterceptor">
<property name="transactionManager">
ref="transactionManager"/>
<property name="transactionAttributeSource">
<bean>
class="org.springframework...AnnotationsTransactionAttributeSource">
</bean>
</property>
</property> </bean></bean></bean>

这是错误的抽象层次。你并不需要看到Spring AOP框架如何控制事务管理这种层次的细节,本意不需要这样的清晰。Spring 2.0在新的“tx”命名空间中提供了一个标签,来替换所有这些定义,像下面这样:

<tx:annotation-driven></tx:annotation-driven>

这个简便的标签达到了相同的效果。这个标签清楚地表达了意图——自动识别事务注解。那三个bean定义仍然通过底层的扩展标签来创建,但那现在是框架的事情了,而不是用户的。

Spring 2.0扩展标签可以在必要时定义它自己的attribute以及子元素结构。定义命名空间的开发者完全可控。处理Spring 2.0扩展标签的NamespaceHandler可以从一个扩展标签创建任意数目的bean定义。

为了使用扩展标签,要使用XML schema而不是DTD,并且导入相关的命名空间。缺省的命名空间应该是beans schema。下面的“tx”命名空间的例子,允许使用<tx:annotation-driven>:</tx:annotation-driven>


<beans xmlns="http://www.springframework.org/schema/beans">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"></beans>
扩展标签可以和正规的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扩展

实现命名空间相对简单。它分三步:

  1. 定义你的XML schema。这是最困难的一步,需要有合适的工具。对于schema没有限制,当然你需要明白它是如何在运行时引导BeanDefinition的生成的。
  2. 实现NamespaceHandler接口,从你的schema中的元素和属性来产生BeanDefinition。
  3. 编辑一个专门的注册文件,spring.handlers,来让Spring知道新建的NamespaceHandler类。

Spring配备的spring.handlers文件显示了“标准”的命名空间是如何配置的:

http\://www.springframework.org/schema/util=org.springframework.beans.factory.xml.UtilNamespaceHandler
http\://www.springframework.org/schema/aop=org.springframework.aop.config.AopNamespaceHandler
http\://www.springframework.org/schema/lang=org.springframework.scripting.config.LangNamespaceHandler
http\://www.springframework.org/schema/tx=org.springframework.transaction.config.TxNamespaceHandler
http\://www.springframework.org/schema/jee=org.springframework.ejb.config.JeeNamespaceHandler
http\://www.springframework.org/schema/p=org.springframework.beans.factory.xml.SimplePropertyNamespaceHandler

你可以在不同的/META-INF目录拥有多个spring.handlers文件。Spring会在运行时合并它们。

一旦遵循了这些步骤,就可以使用你的新的扩展。

NameSpaceHandler接口并不难实现。它采用W3C DOM元素,并通过处理它们来生成BeanDefinition元数据。Spring解析XML:你的代码仅仅需要遍历XML树。

public interface NamespaceHandler {
void init();
BeanDefinition parse(Element element, ParserContext parserContext);

BeanDefinitionHolder decorate(Node source, BeanDefinitionHolder definition, ParserContext parserContext);
}

parse()方法是最重要的,它负责将BeanDefinitions添加到提供的上下文中。

同样要注意decorate()方法。NamespaceHandler同样可以装饰一个包含的bean定义,就像下面这个创建作用域内的代理的语法所显示的:

<bean id="&lt;/font">"scopedList" class="java.util.ArrayList" scope="request">
<aop:scoped-proxy>
</aop:scoped-proxy></bean>

<aop:scoped-proxy>标签用来修饰包含它的标准的<bean>元素;如果它能够访问BeanDefinition,它就能对BeanDefinition进行修改。<aop:scoped-proxy></aop:scoped-proxy></bean></aop:scoped-proxy>

为了简化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结构的。

考虑一下下面这个Java对象,它有两个简单的property,以及对一个关联对象的依赖:

public class Person {
private int age;
private String name;
private House house;
public void setAge(int age) {
this.age = age;
}

public void setName(String name) {
this.name = name;
}

public void setHouse(House house) {
this.house = house;
}

}

可以像下面这样使用XML进行配置:

"1.0" encoding="UTF-8"?>
<beans xmlns="&lt;/font">"http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean class="&lt;/font">"com.interface21.spring2.ioc.Person"
p:name="Tony"
p:age="53"
p:house-ref="number10"
/>

<bean class="&lt;/font">"com.interface21.spring2.ioc.House"
id="number10"
p:name="10 Downing Street"
/>

</bean></bean></beans>

请注意property是如何通过使用attribute来提供的,而不是元素。这样使用了特殊命名空间“p”的魔力。这个命名空间并没有验证,但允许使用attribute名称来和Java property名称进行匹配。

通过简单的几句,我们就简化了使用“p”命名空间中的property名称。就像“p:name”。当注入对其他Spring bean的引用时,使用“-ref”后缀,就像“p:house-ref”。

这种快捷的语法在你想要使用autowiring时尤其方便。比如,考虑下面的变量:

<bean class="&amp;lt;font color=">com.interface21.spring2.ioc.Person"
autowire="byType"
p:name="Tony"
p:age="53"
/></bean>

这里我们没有设置“house”property,因为autowiring会考虑它。你甚至可以使用在<beans>元素层次使用default-autowire来在整个文件使用autowiring。</beans>

下面这个来自于Spring 1.0或者 1.1的用法演示了Spring配置在最近两个主要的发布版本中(1.2和2.0)减少了多少数目的尖括号。

<bean class="&lt;/font">"com.interface21.spring2.ioc.Person">
<property name="name"><value>"Tony"</value></property>
<property name="age"><value>"53"</value></property>
<property name="house"><ref local="&lt;/font">"number10" /></ref>
</property> </bean>

在Spring1.2中,我们引入了“value”和“ref”attribute,在大多数情况下不需要子元素,而在Spring 2.0中则可以更纯粹和简单地使用attribute。

当然,传统的XML格式可以继续工作。当property值很复杂,而且不合法或者不能读作一个attribute值时,就可以使用它们(传统的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中得到很方便的支持。因为这种需求很普通,所以举例会很容易:

考虑下面这个bean的定义:

<bean scope="session" class="com.foo.UserPreferences" id="userPreferences"></bean>

这里我们指定了“session”作用域而不是缺省的“singleton”。作用域可以指定成任何名字,但在Web应用程序中提供“session”和“request”便于使用。

当我们通过名称“userPreferences”调用getBean()时,Spring会很明显地从当前HTTP Session中找出UserPreference对象。如果没找到UserPreferences对象,Spring就会创建一个。注入使得可以为用户 描绘一个可以自定义的预配置的UserPreferences。

为了让它工作起来,你需要在你的web.xml文件中像下面这样定义一个过滤器。它会在底层处理一个ThreadLocal绑定,以便Spring能够知道去哪个HTTP Session对象中去查找。

<web-app>
...
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener></listener-class>
</listener>
...
</web-app>

这样就解决了查找的问题。但通常我们宁愿使用API尽量少的注入风格。如果我们想要把“userPreferences”bean注入到其他 bean中去会发生什么呢,哪个会有更长的生命周期呢?比如,如果我们想在像下面这样的一个单例Spring MVC控制器中采用单独的UserPreferences对象时,又会发生什么呢:

public class UserController extends AbstractController {
private UserPreferences userPreferences;
public void setUserPreferences(UserPreferences userPreferences) { this.userPreferences = userPreferences;
}

@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// do work with this.userPreferences
// Will relate to current user }
}

在这个案例中,我们想要“非常及时”的注入,对短期存在的UserPreferences对象的引用是在被注入者使用的时候解析的。

通常有这样的误解,就是Spring注入是静态的,因此必然是无状态的。这并不正确。因为Spring有一个复杂的基于代理的AOP框架,它能在运行时通过提供这样“非常及时”的功能来隐藏查找的动作。因为Ioc容器控制着注入的内容,它能注入一个隐藏了需要查找的代理。

我们可以很容易地通知容器来执行这样的代理,像下面这样。我们使用一个子元素来修饰userPreferences bean定义,<aop:scoped-proxy>:</aop:scoped-proxy>

<bean class="com.foo.UserPreferences" id="userPreferences">scope="session">
<aop:scoped-proxy></aop:scoped-proxy>
</bean>

<!---->
<bean class="com.mycompany.web.UserController" id="userController">
<!---->
<property ref="userPreferences" name="userPreferences">

</property>
</bean>

现在解析会如期动态地发生;UserController中的userPreferences实例变量就会是从HTTP Session中解析出正确UserPreferences对象的代理。我们不需要直接跟HTTP Session对象打交道,而且可以轻松地对UserController进行单元测试,而不需要一个mock HttpSession对象。

另外一种获得“非常及时”的注入的方式是使用一个查找方法。这是一个自从Spring 1.1就存在的技术,它使生命周期长的bean(通常是单例)能依赖一个潜在的生命周期短的bean。容器能够重载一个抽象(或者具体)的方法来在方法被 调用时返回执行getBean()调用的结果。

在这个例子中,我们没有在被注入者中定义一个实例变量,而是定义了一个返回所需对象的抽象方法。方法必须是公有的或者受保护的:

public abstract class UserController extends AbstractController {
protected abstract UserPreferences getUserPreferences();

@Override protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response)
throws Exception {
// do work with object returned by getUserPreferences()
// Will relate to current user
}
}

在这个例子中,没有必要使用一个作用域内的代理。我们改变了被注入对象的bean定义,而不是被注入的bean。XML配置看起来应该是这样:

<bean class="com.foo.UserPreferences" id="userPreferences">scope="session" />

<!---->
<bean class="com.mycompany.web.UserController" id="userController">
<!---->
<lookup-method bean="userPreferences" name="getUserPreferences">

</lookup-method>
</bean></bean>

这种机制需要类路径中包含CGLIB。

3.其他可能性

无疑,在真实的Spring风格中,底层的机制是可插拔的,而不是绑定到Web层的。比如,Tangosol Coherence作用域可以像下面这样使用,通过Tangosol和Interface21提供的“datagrid”命名空间。


<beans xmlns="http://www.springframework.org/schema/beans">
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"

xmlns:datagrid="http://schemas.tangosol.com/schema/datagrid-for-spring"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://schemas.tangosol.com/schema/datagrid-for-spring
http://schemas.tangosol.com/schema/datagrid-for-spring/datagrid-for-spring.xsd">

<datagrid:member>

<bean scope="datagrid" class="Person" id="brian">
<aop:scoped-proxy>

<property value="brian" name="firstName">
<property value="oliver" name="lastName">
<property value="21" name="age">
</property>
</property> </property></aop:scoped-proxy></bean></datagrid:member></beans>

在一个“datagrid”作用域内声明一个bean,意味着bean的状态管理是由Coherence来执行,在一个Clustered Cache中,而不是在本地的服务器上。当然,bean是由Spring来实例化和注入的,而且可以从Spring服务中受益。我们预见特定作用域内的 bean会在SOA和批处理的环境中非常有用,并且期望能在将来Spring的版本中添加更多方便的bean作用域。

4.自定义作用域

定义你自己的作用域很简单。参考手册详细解释了这个过程。你将需要一种策略来辨别如何在当前的作用域内解析出对象。典型的情况下,这会涉及到ThreadLocal,就像Web作用域在底层所做的那样。

类型推断

如果你运行的是Java 5,Spring 2.0会从它新的能力中获益,比如泛型。比如,考虑一下下面这个类:

public class DependsOnLists {
private List plainList;

private List<float> floatList;

public List<float> getFloatList() {

return floatList;
}

public void setFloatList(List<float> floatList) {
this.floatList = floatList;
}
public List getPlainList() {
return plainList;
}

public void setPlainList(List plainList) {
this.plainList = plainList;
}

} </float></float></float>

“plainList”属性是个旧式风格的集合,而“flodatList”属性有个类型化的说明。

考虑下面的Spring配置:

<bean color="#0000ff" class="&amp;lt;font">"com.interface21.spring2.ioc.DependsOnLists">
<property color="#0000ff" name="&amp;lt;font">"plainList"></property>
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</bean>
<property color="#0000ff" name="&amp;lt;font">"floatList"></property>
<list>
<value>1</value>
<value>2</value>
<value>3</value>
</list>

Spring会正确地为“floatList”属性填上浮点数,而不是字符串,因为它能非常聪明地意识到它需要执行类型转换。

下面的测试演示了这种情况:

public class GenericListTest extends
AbstractDependencyInjectionSpringContextTests {

private DependsOnLists dependsOnLists;

public void setDependsOnLists(DependsOnLists dependsOnLists) {
this.dependsOnLists = dependsOnLists;
}

@Override
protected String[] getConfigLocations() {
return new String[] { "/com/interface21/spring2/ioc/inference.xml" };
}

public void testLists() {
List plainList = dependsOnLists.getPlainList();
List<float> floatList = dependsOnLists.getFloatList();
for (Object o : plainList) {
assertTrue(o instanceof String);
System.out.println("Value='" + o + "', class=" +
o.getClass().getName());
}
for (Float f : floatList) {
System.out.println("Value='" + f + "', class=" +
f.getClass().getName());
}
}

}</float>

输出看起来会像下面这样:

Value='1', class=java.lang.String
Value='2', class=java.lang.String
Value='3', class=java.lang.String
Value='1.0', class=java.lang.Float
Value='2.0', class=java.lang.Float
Value='3.0', class=java.lang.Float

新扩展点

在Ioc容器中有一些新的扩展点,包括:

  • 额外的PostPostProcessor钩子,为像Pitchfork这样的项目提供更强大的能力,来处理自定义注解,或者在Spring bean实例化和配置时执行其他操作。
  • 添加任意元数据到BeanDefinition元数据的能力。添加信息很有用,虽然对Spring本身没有意义,但可以被构建于Spring之上的框架,或者像集群产品这样跟Spring集成的产品来处理。

这些主题主要和高级用户,以及那些使用Spring编写产品的人有关,这同样超出本篇文章的范围。但是理解Spring 2.0并不仅仅是在表面上增强,这很重要;它在底层完成了相当多的有难度的工作。

同样也有大量的增强是支持与OSGi的集成,形成Spring OSGi集成项目,它将OSGi的动态模块管理的能力与Spring组件模型相集成。这个工作会在Spring2.1中继续进行,会把Spring的JAR文件打包成OSGi的bundle。

AOP增强

在Spring 2.0中最激动人心的增强之一是关于Spring AOP,它变得更加便于使用而且更加强大,主要是通过复杂而成熟的AspectJ语言的支持功能来实现,而同时保留纯的基于代理的Java运行时。

我们一直坚信AOP(面向切面编程)很重要。为什么?因为它提供给我们一种新的思考程序结构的方法,能够解决很多纯OOP无法解决的问题——让我们能够在一个模块中实现某些需求,而不是以发散的方式实现。

为了理解这些好处,让我们考虑一些我们可以在需求中表达但无法直接用纯OO代码实现的情况。企业开发者使用一个通常的词汇表来让他们进行清楚的沟通。比如,像服务层,DAO层,Web层或者Web控制器这样的术语,这不需要什么解释。

许多需求是用这个词汇表中的术语来表达的。比如:

  • 服务层应该是可以处理事务的。
  • 当DAO操作失败时,SQLException或者其他特殊持久化技术的异常应该被翻译,以确保DAO接口不会有漏掉的抽象。
  • 服务层对象不应该调用Web层,因为各层应该只依赖直接处在其下方的层。
  • 由于并发相关操作的失败而导致失败的等幂业务服务可以重试。

虽然这些需求都是现实存在的,并来自于经验,但它们并不能用纯OOP来优雅地解决。为什么?主要有两个原因:

  • 这些来自于我们词汇表的术语有意义,但它们并不是抽象。我们不能使用术语编程;我们需要抽象。
  • 所有这些都是所谓横切关注点的例子。一个横切关注点,在用传统OO方法实现时,会分解成很多类和方法。比如,想象一下在跨DAO层遭遇特殊异常时要使用重试逻辑。这个关注点横切许多DAO方法,而且在传统的方式中会需要实现许多单独的修改。

AOP就是通过对横切关注点进行模块化,并让我们从普通的还可以编程的抽象的词汇表来表达术语,来解决这样问题的技术,这些抽象叫做切入点,我很快会再解释一些关于它们的细节。这种方法带来一些主要好处,比如:

  • 因为减少了剪切粘贴风格的复制而减少代码行数。这在像异常转换和性能监测这样的try/catch/finally习惯用法中尤其有效。
  • 在单个代码模块中捕捉这样需求的能力,提升可追踪能力。
  • 在单个地方修补bug的能力,而不需要重新访问应用程序中许多位置。
  • 确保横切关注点不混淆主要的业务逻辑——随着开发的进展,这很有可能成为危险之处。
  • 开发者和团队之间更好的职责分离。比如,重试功能可以有单个开发者或者团队来编码,而不需要由许多开发者跨多个子系统进行编码。

因此AOP很重要,我们想提供最好的解决方案。

Spring AOP无疑是最广泛使用的AOP技术,归功于以下优点:

  • 采用成本几近为零。
  • 提供正确的切入点,这才称得上是AOP而不仅仅是拦截。
  • 提供一个支持许多使用方式的灵活的框架,可编程也可通过XML。

然而,在Spring 2.0之前,Spring中的AOP有一些缺点:

  • 不写Java代码,只能表达简单的切入点。并没有一种切入点表达语言来以字符串形式,简洁表达复杂的切入点,虽然RegexpMethodPointcutAdvisor允许定义简单正规的基于表达的切入点。
  • 当配置复杂AOP使用场景时,XML配置会变得很复杂。泛型元素被用来配置AOP类;虽然这对一致性来说很棒,对切面和类提供DI和其他服务,但它没有一个专门的配置方法来得简洁。
  • Spring AOP不适合通知细粒度的对象——对象需要由Spring管理或者通过编程被代理。
  • 基于代理的方法的性能负载在少数案例中成为问题。
  • 因 为Spring AOP分离了代理和目标(被修饰或者被通知的对象),如果某个目标方法调用了目标上的方法,就不会使用到代理,意味着AOP通知并不适用。AOP使用基于 代理的方法的正反面影响超出了本文的范围:有一些积极的因素(比如能够对同一个类的不同实例应用不同的通知),但主要还是消极的。

为了在Spring 2.0中增强这个重要领域,我们希望在它的优势上构建,同时解决缺点。

目标

最先的两个缺点也是最显著的。它们都跟切入点相关。后面的三个缺点在Spring用户的正常使用中很少发生,如果它们证明是的确有问题的,我们建议使用AspectJ。(就像你会看到的,这是Spring AOP直接的进步。)

XML配置扩展解决了关键的挑战之一。因为我们想要保持Spring模块的设计,我们过去不能在Spring DTD中提供特定于AOP的标签——因此在这种情况下需要依赖可以详细一点的通用配置。随着Spring 2.0的出现,这样的问题没有了,因为XML schema并不像DTD,它允许扩展。我们可以提供一个AOP命名空间,看起来能让Ioc容器识别AOP结构,但不会影响模块化。

AOP术语101:理解切入点和通知

让我们简要地修正一下某些AOP术语。如果你使用过AOP这些概念,可能对你来说很熟悉——这些概念是相同的,仅仅有一点不同,即更加优雅和强大的表达方式。

切入点是匹配规则。它在程序执行中确定应该应用某个切面的点的集合。这些点叫做连接点。在应用程序运行时,连接点随时会有,比如对象的实例化和方法的调用。在Spring AOP(所有版本)的案例中,唯一支持的连接点是公有方法的执行。

通知是可以被切面应用到连接点的行为。通知能在连接点之前或之后应用。通知的所有类型包括:

  • Before advice:在连接点之前调用的通知。比如,记录方法调用即将发生的日志。
  • After returning adive:如果在连接点的方法正常返回时调用的通知。
  • AfterThrowing advice(在Spring1.x中叫做Throws通知):如果连接点的方法抛出一个特殊的异常时调用的通知。
  • After advice:在连接点之后调用的通知,无论结果是什么。特别像Java中的finally。
  • Around advice:能够完全控制是否执行连接点的通知。比如,用来在事务中封装某个方法调用,或者记录方法的执行时间。

切面是结合切入点和通知成一个模块方案,解决特殊的横切问题。

如果这有点抽象,请不要担心:代码示例会很快解释清楚的。

对在Spring 2.0和AspectJ的环境中关于AOP基础的更深讨论,请参考Adrian在InfoQ上很棒的文章,"Simplifying Enterprise Applications with Spring 2.0 and AspectJ."

为什么会是AspectJ切入点表达式?

迄今为止,我们讨论过的概念都是基本的AOP概念,对于Spring AOP或者AspectJ而且这并不特别,在Spring1.x中已经是存在的。那么为什么我们选择在Spring 2.0中采用AspectJ呢?

如果我们需要一种切入点表达语言,那么选择就会很简单。AspectJ有个思路很好,严格定义和充足文档的切入点语言。它最近实现了一个当在Java 5上运行时,能对采用Java 5语法的编码全面检查。它不仅有很棒的参考材料,而且很多书籍和文章都对它进行了介绍。

我们不相信重新发明的轮子,而且定义我们自己的切入点表达语言是不合理的。进一步而言,自从AspectWerkz在2005年早期和冰岛 AspectJ项目之后,很明显AspectJ是除了Spring 2.0之外唯一一个主流的AOP技术。因此关键的合并既是一种考虑也是一种技术优势。

新的XML语法

新的AOP命名空间允许Spring XML配置指定AspectJ切入点表达式,通过由切入点匹配的方法将通知指向任何Spring bean。

考虑一下我们上面看到的Person类。它有个age属性,以及一个增加age的birthday方法:

public void birthday() {
++age;
}

我们假设有这样的需求,任何时候调用birthday方法,我们都应该发送一张生日贺卡

分享到:
评论

相关推荐

    Live在线书店

    《Spring 2.0核心技术与最佳实践》一书由浅入深,详细介绍了Spring 2.0框架的几乎全部内容,并重点突出2.0版本的新特性,是一本注重实践而又深入理论的书,力图为读者展示如何应用Spring 2.0框架创建灵活高效的J2EE...

    spring.net中文手册在线版

    Spring.NET是一个应用程序框架,其目的是协助开发人员创建企业级的.NET应用程序。它提供了很多方面的功能,比如依赖注入、面向方面编程(AOP)、数据访问抽象及ASP.NET扩展等等。Spring.NET以Java版的Spring框架为...

    Spring Framework 开发参考手册

    Spring中使用了很多被实践证明的最佳实践和正规的设计模式,并且进行了编码实现。如果你是一个,构架师或者开发人员完全可以取出它们集成到你自己的应用之中。这对于那些使用了Spring Framework的组织和机构来说,在...

    轻量级Java_EE企业应用实战_(第三版).part1.rar

    轻量级Java_EE企业应用实战_(第三... 国内知名的高端IT技术作家,已出版《Spring 2.0宝典》、《基于J2EE的Ajax宝典》、《轻量级J2EE企业应用实战》、《Struts 2权威指南》、《Ruby On Rails敏捷开发最佳实践》等著作。

    轻量级Java_EE企业应用实战_(第三版).part2.rar

    轻量级Java_EE企业应用实战_(第三... 国内知名的高端IT技术作家,已出版《Spring 2.0宝典》、《基于J2EE的Ajax宝典》、《轻量级J2EE企业应用实战》、《Struts 2权威指南》、《Ruby On Rails敏捷开发最佳实践》等著作。

    轻量级Java_EE企业应用实战_(第三版).part3.rar

    轻量级Java_EE企业应用实战_(第三... 国内知名的高端IT技术作家,已出版《Spring 2.0宝典》、《基于J2EE的Ajax宝典》、《轻量级J2EE企业应用实战》、《Struts 2权威指南》、《Ruby On Rails敏捷开发最佳实践》等著作。

    JAVA\轻量级Java_EE企业应用实战_(第三版).part4.rar

    轻量级Java_EE企业应用实战_(第三... 国内知名的高端IT技术作家,已出版《Spring 2.0宝典》、《基于J2EE的Ajax宝典》、《轻量级J2EE企业应用实战》、《Struts 2权威指南》、《Ruby On Rails敏捷开发最佳实践》等著作。

    基于机器学习+Spark2.0+MongoDB实现的协同过滤推荐系统.zip

    我学会了使用Spring Boot快速搭建Java Web应用程序,并且能够运用Spring Boot的特性来简化开发流程。在学习的过程中,我遇到了一些挑战,比如配置文件的理解和注解的正确使用,但通过查阅官方文档和阅读相关书籍,我...

    asp.net知识库

    ASP.NET 2.0构建动态导航的Web应用程序(TreeView和Menu ) 体验.net2.0的优雅(3) -- 为您的 SiteMap 添加 控制转发功能 GridView控件使用经验 ASP.NET 2.0:弃用 DataGrid 吧,有新的网格控件了! ASP.NET2.0控件...

    基于J2EE框架的个人博客系统项目毕业设计论文(源码和论文)

    在web 2.0应用中,博客(Blog)是web 2.0核心应用中最典型、最流行的代表之一,也是web 2.0技术应用的最直观的表现,是web 2.0精神和理念的具体体现。 1.2. 问题的提出 Blog记载了日常发生的事情和自己的兴趣爱好,把...

    基于J2EE框架的个人博客系统项目毕业设计论...

    在web 2.0应用中,博客(Blog)是web 2.0核心应用中最典型、最流行的代表之一,也是web 2.0技术应用的最直观的表现,是web 2.0精神和理念的具体体现。 1.2. 问题的提出 Blog记载了日常发生的事情和自己的兴趣爱好,把...

    Grails权威指南

     1.2 webc2.0时代  1.3 java的力量  1.4 什么是grails  1.4.1 与java集成  1.4.2 简单而强大  1.4.3 吸取的经验教训  1.5 使用grails的原因  1.6 grails入门  1.6.1 运行grails命令...

    iuhyiuhkjh908u0980

    PrettyFaces: EL API访问PrettyContext 支持JSF 1.1 增强了错误页面和servlet重定向 PrettyFaces是一个JSF1.2和JSF2.0的扩展,用来创建便于书签收藏、漂亮的网址。 PrettyFaces优雅的解决了这个问题,包括诸如功能:...

    Maven权威指南 很精典的学习教程,比ANT更好用

    添加新的依赖 4.6. Simple Weather源码 4.7. 添加资源 4.8. 运行Simple Weather项目 4.8.1. Maven Exec 插件 4.8.2. 浏览你的项目依赖 4.9. 编写单元测试 4.10. 添加测试范围依赖 4.11. 添加单元测试资源...

    网络架构师148讲视频课程

    │ 第128节:应用建议及最佳实践.avi │ 第129节:MongoDB结合应用开发一.avi │ 第130节:MongoDB结合应用开发二.avi │ 第131节:应用MongoDB后体系结构.avi │ 第132节:MogileFS简介和入门.avi │ 第133节:...

Global site tag (gtag.js) - Google Analytics