﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-lanxin1020-随笔分类-spring</title><link>http://www.blogjava.net/lanxin1020/category/38969.html</link><description /><language>zh-cn</language><lastBuildDate>Sat, 11 Apr 2009 04:33:37 GMT</lastBuildDate><pubDate>Sat, 11 Apr 2009 04:33:37 GMT</pubDate><ttl>60</ttl><item><title>Introducing to Spring Framework (转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/07/264363.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Tue, 07 Apr 2009 15:30:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/07/264363.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/264363.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/07/264363.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/264363.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/264363.html</trackback:ping><description><![CDATA[<div class="description">Introducing to Spring Framework <br />
<br />
作者：Rod Johnson <br />
译者：yanger，taowen <br />
校对：taowen <br />
<br />
关于Spring Framework，今年夏天你可能已经听见很多的议论。在本文中,我将试图解释Spring能完成什么，和我怎么会认为它能帮助你开发J2EE应用程序。 <br />
<br />
又来一个framework？ <br />
<br />
你可能正在想&#8220;不过是另外一个的framework&#8221;。当已经有许多开放源代码(和专有) J2EE framework时，为什么你还要耐下心子读这篇文章或去下载Spring Framework？ <br />
<br />
我相信Spring是独特的，有几个原因： <br />
<br />
<br />
<br />
它关注的领域是其他许多流行的Framework未曾关注的。Spring要提供的是一种管理你的业务对象的方法。 <br />
<br />
Spring既是全面的又是模块化的。Spring有分层的体系结构，这意味着你能选择仅仅使用它任何一个独立的部分，而它的架构又是内部一致。因此你能从你的学习中，得到最大的价值。例如，你可能选择仅仅使用Spring来简单化JDBC的使用，或用来管理所有的业务对象。 <br />
<br />
它的设计从一开始就是要帮助你编写易于测试的代码。Spring是使用测试驱动开发的工程的理想框架。 <br />
<br />
<br />
Spring不会给你的工程添加对其他的框架依赖。Spring也许称得上是个一站式解决方案，提供了一个典型应用所需要的大部分基础架构。它还涉及到了其他framework没有考虑到的内容。 <br />
<br />
尽管它仅仅是一个从2003年2月才开始的开源项目，但Spring有深厚的历史根基。这个开源工程是起源自我在2002年晚些时候出版的《Expert One-on-One J2EE设计与开发》书中的基础性代码。这本书展示了Spring背后的基础性架构思想。然而，对这个基础架构的概念可以追溯到2000年的早些时候,并且反映了我为一系列商业工程开发基础结构的成功经验。 <br />
<br />
2003年1月，Spring已经落户于SourceForge上了。现在有10个开发人员，其中6个是高度投入的积极分子。 <br />
<br />
Spring架构上的好处 <br />
<br />
在我们进入细节之前，让我们来看看Spring能够给工程带来的种种好处： <br />
<br />
<br />
<br />
Spring能有效地组织你的中间层对象，不管你是否选择使用了EJB。如果你仅仅使用了Struts或其他为J2EE的 API特制的framework，Spring致力于解决剩下的问题。 <br />
<br />
Spring能消除在许多工程中常见的对Singleton的过多使用。根据我的经验，这是一个很大的问题，它降低了系统的可测试性和面向对象的程度。 <br />
<br />
通过一种在不同应用程序和项目间一致的方法来处理配置文件，Spring能消除各种各样自定义格式的属性文件的需要。曾经对某个类要寻找的是哪个魔法般的属性项或系统属性感到不解，为此不得不去读Javadoc甚至源编码？有了Spring，你仅仅需要看看类的JavaBean属性。 Inversion of Control的使用（在下面讨论）帮助完成了这种简化。 <br />
<br />
通过把对接口编程而不是对类编程的代价几乎减少到没有，Spring能够促进养成好的编程习惯。 <br />
<br />
Spring被设计为让使用它创建的应用尽可能少的依赖于他的APIs。在Spring应用中的大多数业务对象没有依赖于Spring。 <br />
<br />
使用Spring构建的应用程序易于单元测试。 <br />
<br />
Spring能使EJB的使用成为一个实现选择,而不是应用架构的必然选择。你能选择用POJOs或local EJBs来实现业务接口，却不会影响调用代码。 <br />
<br />
Spring帮助你解决许多问题而无需使用EJB。Spring能提供一种EJB的替换物，它们适用于许多web应用。例如，Spring能使用AOP提供声明性事务管理而不通过EJB容器，如果你仅仅需要与单个数据库打交道，甚至不需要一个JTA实现。 <br />
<br />
Spring为数据存取提供了一个一致的框架,不论是使用的是JDBC还是O/R mapping产品（如Hibernate）。 <br />
<br />
<br />
Spring确实使你能通过最简单可行的解决办法来解决你的问题。而这是有有很大价值的。 <br />
<br />
Spring做了些什么？ <br />
<br />
Spring提供许多功能，在此我将依次快速地展示其各个主要方面。 <br />
<br />
任务描述 <br />
<br />
首先,让我们明确Spring范围。尽管Spring覆盖了许多方面，但我们对它应该涉什么，什么不应该涉及有清楚的认识。 <br />
<br />
Spring的主要目的是使J2EE易用和促进好编程习惯。 <br />
<br />
Spring不重新轮子。因此，你发现在Spring中没有logging，没有连接池，没有分布式事务调度。所有这些东西均有开源项目提供(例如我们用于处理所有日志输出的Commons Logging以及Commons DBCP)，或由你的应用程序服务器提供了。出于同样的的原因，我们没有提供 O/R mapping层。对于这个问题已经有了像Hibernate和JDO这样的优秀解决方案。 <br />
<br />
Spring的目标就是让已有的技术更加易用。例如，尽管我们没有底层事务协调处理，但我们提供了一个抽象层覆盖了JTA或任何其他的事务策略。 <br />
<br />
Spring没有直接和其他的开源项目竞争，除非我们感到我们能提供新的一些东西。例如，象许多开发人员一样，我们从来没有对Struts感到高兴过，并且觉得到在MVC web framework中还有改进的余地。在某些领域，例如轻量级的IoC容器和AOP框架，Spring确实有直接的竞争，但是在这些领域还没有已经较为流行的解决方案。(Spring在这些领域是开路先锋。) <br />
<br />
Spring也得益于内在的一致性。所有的开发者都在唱同样的的赞歌，基础想法依然与Expert One-on-One J2EE设计与开发中提出的差不多。 并且我们已经能够在多个领域中使用一些中心的概念，例如Inversion of Control。 <br />
<br />
Spring在应用服务器之间是可移植的。当然保证可移植性总是一种挑战，但是我们避免使用任何平台特有或非标准的东西，并且支持在WebLogic，Tomcat，Resin，JBoss，WebSphere和其他的应用服务器上的用户。 <br />
<br />
Inversion of Control 容器 <br />
<br />
Spring设计的核心是 org.springframework.beans 包, 它是为与JavaBeans一起工作而设计的。 这个包一般不直接被用户使用，而是作为许多其他功能的基础。 <br />
<br />
下一个层面高一些的抽象是"Bean Factory"。一个Spring bean factory 是一个通用的Factory，它使对象能够按名称获取，并且能管理对象之间的关系。 <br />
<br />
Bean factories 支持两种模式的对象： <br />
<br />
<br />
<br />
Singleton：在此模式中，有一个具有特定名称的共享对象实例，它在查找时被获取。这是默认的，而且是最为经常使用的。它对于无状态对象是一种理想的模式。 <br />
<br />
Prototype：在此模式中，每次获取将创建一个独立的对象。例如，这可以被用于让用户拥有他们自己的对象。 <br />
<br />
<br />
<br />
由于 org.springframwork.beans.factory.BeanFactory是一个简单的接口，它能被大量底层存储方法实现。你能够方便地实现你自己的BeanFactory，尽管很少用户需要这么做。最为常用的BeanFactory定义是： <br />
<br />
<br />
<br />
XmlBeanFactory： 可解析简单直观的定义类和命名对象属性的XML结构。 我们提供了一个DTD来使编写更容易。 <br />
<br />
ListableBeanFactoryImpl：提供了解析存放在属性文件中的bean定义的能力，并且可通过编程创建BeanFactories。 <br />
<br />
<br />
每个bean定义可能是一个POJO（通过类名和JavaBean初始属性定义），或是一个FactoryBean。FactoryBean接口添加了一个间接层。通常，这用于创建使用AOP或其他方法的代理对象：例如，添加声明性事务管理的代理。（这在概念上和EJB的interception相似，但实现得更简单。） <br />
<br />
BeanFactories能在一个层次结构中选择性地参与，继承ancestor（祖先）的定义。这使得在整个应用中公共配置的共享成为可能，虽然个别资源，如controller servlets，还拥有他们自己的独立的对象集合。 <br />
<br />
这种使用JavaBeans的动机在《Expert One-on-One J2EE Design and Development》的第四章中有描述，在TheServerSide网站上的有免费的PDF版本(http://www.theserverside.com/resources/article.jsp?l=RodJohnsonInterview)。 <br />
<br />
通过BeanFactory概念，Spring成为一个Inversion of Control的容器。(我不怎么喜欢container这个词，因为它使人联想到重量级容器，如EJB容器。Spring的BeanFactory是一个可通过一行代码创建的容器，并且不需要特殊的部署步骤。) <br />
<br />
Inversion of Control背后的概念经常表述为Hollywood原则的：&#8220;Don&#8217;t call me，&nbsp; I&#8217;ll call you。&#8221; IoC将控制创建的职责搬进了框架中，并把它从应用代码脱离开来。涉及到配置的地方，意思是说在传统的容器体系结构中，如EJB，一个组件可以调用容器并问&#8220;我需要它给我做工作的对象X在哪里?&#8221;；使用IoC容器则只需指出组件需要X对象，在运行时容器会提供给它。容器是通过查看方法的参数表（例如JavaBean的属性）做到的，也可能根据配置数据如XML。 <br />
<br />
IoC有几个重要的好处，例如： <br />
<br />
<br />
<br />
因为组件不需要在运行时间寻找合作者，所以他们可以更简单的编写和维护。在Spring版的IoC里，组件通过暴露JavaBean的setter方法表达他们依赖的其他组件。这相当于EJB通过JNDI来查找，EJB查找需要开发人员编写代码。 <br />
<br />
同样原因，应用代码更容易测试。JavaBean属性是简单的，属于Java核心的，并且是容易测试的：仅编写一个自包含的Junit测试方法用来创建对象和设置相关属性即可。 <br />
<br />
一个好的IoC实现保留了强类型。如果你需要使用一个通用的factory来寻找合作者，你必须通过类型转换将返回结果转变为想要的类型。这不是一个大不了的问题，但是不雅观。使用IoC，你在你的代码中表达了强类型依赖，框架将负责类型转换。这意味着在框架配置应用时，类型不匹配将导致错误；在你的代码中，你无需担心类型转换异常。 <br />
<br />
大部分业务对象不依赖于IoC容器的APIs。这使得很容易使用遗留下来的代码，且很容易的使用对象无论在容器内或不在容器内。例如，Spring用户经常配置Jakarta Commons DBCP数据源为一个Spring bean：不需要些任何定制代码去做这件事。我们说一个IoC容器不是侵入性的：使用它并不会使你的代码依赖于它的APIs。任何JavaBean在Spring bean factory中都能成为一个组件。 <br />
<br />
<br />
最后应该强调的是，IoC 不同于传统的容器的体系结构，如EJB，应用代码最小程度地依靠于容器。这意味着你的业务对象可以潜在的被运行在不同的IoC 框架上——或者在任何框架之外——不需要任何代码的改动。 <br />
<br />
以我和其他Spring用户的经验来说，再怎么强调IoC给应用程序代码带来的好处也不为过。 <br />
<br />
IoC不是一个新概念，但是它在J2EE团体里面刚刚到达黄金时间。 有一些可供选择的IoC 容器: 例如 Apache Avalon,&nbsp; PicoContainer 和 HiveMind。Avalon 从没怎么流行，尽管它很强大而且有很长的历史。Avalon相当的重和复杂，并且看起来比新的IoC解决方案更具侵入性。 PicoContainer是一个轻量级而且更强调通过构造函数表达依赖性而不是JavaBean 属性。 与 Spring不同，它的设计允许每个类型一个对象的定义(可能是因为它拒绝任何Java代码外的元数据导致的局限性)。在Spring，&nbsp; PicoContainer 和其他 IoC frameworks之间做比较，可参看文章Spring网站上的 "The Spring Framework - A Lightweight Container"位于http://www.springframework.org/docs/lightweight_container.html。这个页面里面包含了PicoContainer站点的链接 。 <br />
<br />
Spring BeanFactories 是非常轻量级的。用户已经成功地将他们应用在applets和单独的Swing应用中。(它们也很好地工作在 EJB容器中。) 没有特殊的部署步骤和察觉得到的启动时间。这个能力表明一个容器在应用的任何层面几乎立即可以发挥非常大的价值。 <br />
<br />
Spring BeanFactory 概念贯穿于Spring始终， 而且是Spring如此内在一致的关键原因。在IoC容器中，Spring也是唯一的，它使用IoC作为基础概念贯穿于整个功能丰富的框架。 <br />
<br />
对应用开发人员，最重要的是，一个或多个BeanFactory提供了一个定义明确的业务对象层。这类似于local session bean层，但比它更简单。与EJBs不同，在这个层中的对象可能是相关的，并且他们的关系被拥有它们的factory管理。有一个定义明确的业务对象层对于成功的体系结构是非常重要的。 <br />
<br />
Spring ApplicationContext 是BeanFactory的子接口，为下列东西提供支持： <br />
<br />
<br />
<br />
信息查找，支持着国际化 <br />
<br />
事件机制，允许发布应用对象以及可选的注册以接收到事件 <br />
<br />
可移植的文件和资源访问 <br />
<br />
<br />
XmlBeanFactory 例子 <br />
<br />
Spring用户通常在XML的&#8220;bean定义&#8221;文件中配置他们的应用。Spring的XML bean定义文档的根是&amp;lt;beans&amp;gt; 元素。该元素包含一个或多个 &amp;lt;bean&amp;gt;定义。我们一般给每个bean定义的指定类和属性。我们还必须指定ID作为标识，这将成为在代码中使用该bean的名字。 <br />
<br />
让我们来看一个简单的例子，它配置了三个应用程序对象，之间的关系在J2EE应用中常常能够看到： <br />
<br />
<br />
<br />
J2EE DataSource <br />
<br />
使用DataSource的DAO <br />
<br />
在处理过程中使用DAO的业务对象 <br />
<br />
<br />
在下面的例子中，我们使用一个来自Jakarta Commons DBCP项目的BasicDataSource。这个class（和其他许多已有的 class一样）可以简单地被应用在Spring bean factory中，只要它提供了JavaBean格式的配置。需要在shutdown时被调用的Close方法可通过Spring的"destroy-method"属性被注册，以避免BasicDataSource需要实现任何Spring&nbsp; 的接口。 <br />
<br />
代码: <br />
&amp;lt;beans&amp;gt; <br />
<br />
&nbsp; &amp;lt;bean id="myDataSource" <br />
class="org.apache.commons.dbcp.BasicDataSource" <br />
destroy-method="close"&amp;gt; <br />
&nbsp;&nbsp;&nbsp; &amp;lt;property name="driverClassName"&amp;gt;&amp;lt;value&amp;gt;com.mysql.jdbc.Driver&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt; <br />
&nbsp;&nbsp;&nbsp; &amp;lt;property name="url"&amp;gt;&amp;lt;value&amp;gt;jdbc:mysql://localhost:3306/mydb&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt; <br />
&nbsp;&nbsp;&nbsp; &amp;lt;property name="username"&amp;gt;&amp;lt;value&amp;gt;root&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt; <br />
&nbsp; &amp;lt;/bean&amp;gt; <br />
<br />
BasicDataSource中我们感兴趣的所有属性都是String类型的，因此我们用&amp;lt;value&amp;gt;元素来指定他们的值。如果必要的话，Spring使用标准的 JavaBean属性编辑器机制来把String转换为其他的类型。 <br />
<br />
现在，我们定义DAO，它有一个对DataSource的bean引用。Bean间关系通过&amp;lt;ref&amp;gt;元素来指定： <br />
<br />
代码: <br />
&amp;lt;bean id="exampleDataAccessObject" <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="example.ExampleDataAccessObject"&amp;gt; <br />
&nbsp;&nbsp;&nbsp; &amp;lt;property name="dataSource"&amp;gt;&amp;lt;ref bean="myDataSource"/&amp;gt;&amp;lt;/property&amp;gt; <br />
&nbsp; &amp;lt;/bean&amp;gt; <br />
<br />
The business object has a reference to the DAO, and an int property (exampleParam): <br />
&amp;lt;bean id="exampleBusinessObject" <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class="example.ExampleBusinessObject"&amp;gt; <br />
&nbsp;&nbsp;&nbsp; &amp;lt;property name="dataAccessObject"&amp;gt;&amp;lt;ref bean="exampleDataAccessObject"/&amp;gt;&amp;lt;/property&amp;gt; <br />
&nbsp;&nbsp;&nbsp; &amp;lt;property name="exampleParam"&amp;gt;&amp;lt;value&amp;gt;10&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt; <br />
&nbsp; &amp;lt;/bean&amp;gt; <br />
<br />
&amp;lt;/beans&amp;gt; <br />
<br />
对象间的关系一般在配置中明确地设置，象这个例子一样。我们认为这样做是件好事情。然而Spring还提供了我们称做"autowire"的支持， 一个 la PicoContainer，其中它指出了bean间的依赖关系。这样做的局限性——PicoContainer也是如此——是如果有一个特殊类型的多个Bean，要确定那个类型所依赖的是哪个实例是不可能。好的方面是，不满足的依赖可以在factory初始化后被捕获到。（Spring 也为显式的配置提供了一种可选的依赖检查，它可以完成这个目的） <br />
<br />
在上面的例子中，如果我们不想显式的编写他们的关系，可使用如下的autowire特性： <br />
<br />
代码: <br />
&amp;lt;bean id="exampleBusinessObject" <br />
&nbsp;&nbsp; class="example.ExampleBusinessObject" <br />
&nbsp;&nbsp; autowire="byType"&amp;gt; <br />
<br />
&nbsp;&nbsp;&nbsp; &amp;lt;property name="exampleParam"&amp;gt;&amp;lt;value&amp;gt;10&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt; <br />
&amp;lt;/bean&amp;gt; <br />
<br />
<br />
使用这个特性，Spring会找出exampleBusinessObject的dataSource属性应该被设置为在当前BeanFactory中找到的DataSource实现。在当前的BeanFactory中，如果所需要类型的bean不存在或多于一个，将产生一个错误。我们依然要设置 exampleParam属性，因为它不是一个引用。 <br />
<br />
Autowire支持和依赖检查刚刚加入CVS并将在Spring 1.0 M2(到10/20,2003)中提供。本文中所讨论的所有其他特性都包含在当前1.0 M1版本中。 <br />
<br />
把管理从Java代码中移出来比硬编码有很大的好处，因为这样可以只改变XML文件而无需改变一行Java代码。例如，我们可以简单地改变 myDataSource的bean定义引用不同的bean class以使用别的连接池，或者一个用于测试的数据源。 XML节变成另一种，我们可以用 Spring的JNDI location FactoryBean从应用服务器获取一个数据源。 <br />
<br />
现在让我们来看看例子中业务对象的java 代码。注意下面列出的代码中没有对Spring的依赖。不像EJB容器，Spring BeanFactory不具有侵入性：在应用对象里面你通常不需要对Spring的存在硬编码。 <br />
<br />
代码: <br />
public class ExampleBusinessObject implements MyBusinessObject { <br />
<br />
&nbsp;&nbsp; private ExampleDataAccessObject dao; <br />
&nbsp;&nbsp; private int exampleParam; <br />
<br />
&nbsp;&nbsp; public void setDataAccessObject(ExampleDataAccessObject dao) { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.dao = dao; <br />
&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp; public void setExampleParam(int exampleParam) { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.exampleParam = exampleParam; <br />
&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp; public void myBusinessMethod() { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // do stuff using dao <br />
&nbsp;&nbsp; } <br />
} <br />
<br />
注意那些property setter，它们对应于bean定义文档中的XML引用。这些将在对象被使用之前由Spring调用。 <br />
<br />
这些应用程序的bean不需要依赖于Spring：他们不需要实现任何Spring的接口或者继承Spring的类。他们只需要遵守JavaBeans的命名习惯。在Spring 应用环境之外重用它们是非常简单的，例如，在一个测试环境中。只需要用它们的缺省构造函数实例化它们，并且通过调用 setDataSource()和setExampleParam()手工设置它的属性。如果你想以一行代码支持程序化的创建，只要你有一个无参数的构造器，你就可以自由定义其他需要多个属性的构造函数。 <br />
<br />
注意在业务接口中没有声明将会一起使用的JavaBean属性。 他们是一个实现细节。我们可以&#8220;插入&#8221;带有不同bean属性的不同的实现类而不影响连接着的对象或者调用的代码。 <br />
<br />
当然，Spring XML bean factories 有更多的功能没有在这里描述，但是，应当让你对基本使用有了一些感觉。以及，简单的属性，有 JavaBean属性编辑器的属性，Spring可以自动处理lists，maps和java.util.Properties。 <br />
<br />
Bean factories 和application contexts 通常和J2EE server定义的一个范围相关联，例如： <br />
<br />
<br />
<br />
Servlet context.：在spring 的MVC 框架里, 每一个包含common objects的web 应用都定义有一个应用程序的 context。Spring提供了通过listener或者servlet实例化这样的context的能力而不需要依赖于Spring 的MVC 框架，因而它也可以用于Struts，WebWork 或者其他的web框架之中。 <br />
<br />
A Servlet：在Spring MVC 框架里每一个servlet控制器都有它自己的应用程序context，派生于根（全应用程序范围的）应用程序context。在Struts或者其他MVC框架中实现这些也很容意。 <br />
<br />
EJB：Spring 为EJB提供方便的超类，它们简化了EJB的创建并且提供了一个从EJB Jar 文件中的XML文档载入的BeanFactory。 <br />
<br />
<br />
这些J2EE规范提供的hook通常避免了使用Singleton来创造一个bean factory。 <br />
<br />
然而，如果我们愿意的话可以用代码创建一个BeanFactory，虽然是没有什么意义的。例如，我们在以下三行代码中可以创建bean factory并且得到一个业务对象的引用： <br />
<br />
代码: <br />
InputStream is = getClass().getResourceAsStream("myFile.xml"); <br />
XmlBeanFactory bf = new XmlBeanFactory(is); <br />
MyBusinessObject mbo = (MyBusinessObject) bf.getBean("exampleBusinessObject"); <br />
<br />
<br />
<br />
这段代码将能工作在一个应用服务器之外：甚至不依赖J2EE，因为Spring 的IoC容器是纯java的。 <br />
<br />
JDBC 抽象和数据存储异常层次 <br />
<br />
数据访问是Spring 的另一个闪光点。 <br />
<br />
JDBC 提供了还算不错的数据库抽象，但是需要用痛苦的API。这些问题包括： <br />
<br />
<br />
<br />
需要冗长的错误处理代码来确保ResultSets，Statements以及（最重要的）Connections在使用后关闭。这意味着对JDBC的正确使用可以快速地导致大量的代码量。它还是一个常见的错误来源。Connection leak可以在有负载的情况下快速宕掉应用程序。 <br />
<br />
SQLException相对来说不能说明任何问题。JDBC不提供异常的层次，而是用抛出SQLException来响应所有的错误。找出到底哪里出错了——例如，问题是死锁还是无效的SQL？——要去检查SQLState或错误代码。这意味着这些值在数据库之间是变化的。 <br />
<br />
Spring用两种方法解决这些问题： <br />
<br />
<br />
<br />
提供API，把冗长乏味和容易出错的异常处理从程序代码移到框架之中。框架处理所有的异常处理；程序代码能够集中精力于编写恰当的SQL和提取结果上。 <br />
<br />
为你本要处理SQLException程序代码提供有意义的异常层次。当Spring第一次从数据源取得一个连接时，它检查元数据以确定数据库。它使用这些信息把SQLException映射为自己从org.springframework.dao.DataAccessException派生下来的类层次中正确的异常。因而你的代码可以与有意义的异常打交道，并且不需要为私有的SQLState或者错误码担心。Spring的数据访问异常不是JDBC特有的，因而你的DAO并不一定会因为它们可能抛出的异常而绑死在JDBC上。 <br />
<br />
Spring提供两层JDBC API。第一个时，在org.springframework.jdbc.core包中，使用回调机制移动控制权——并且因而把错误处理和连接获取和释放——从程序的代码移到了框架之中。这是一种不同的Inversion of Control，但是和用于配置管理的几乎有同等重要的意义。 <br />
<br />
Spring使用类似的回调机制关注其他包含特殊获取和清理资源步骤的API，例如JDO（获取和释放是由PersistenceManager完成的），事务管理（使用JTA）和JNDI。Spring中完成这些回调的类被称作template。 <br />
<br />
例如，Spring的JdbcTemplate对象能够用于执行SQL查询并且在如下的列表中保存结果： <br />
<br />
代码: <br />
JdbcTemplate template = new JdbcTemplate(dataSource); <br />
final List names = new LinkedList(); <br />
template.query("SELECT USER.NAME FROM USER", <br />
&nbsp;&nbsp; new RowCallbackHandler() { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void processRow(ResultSet rs) throws SQLException { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; names.add(rs.getString(1)); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } <br />
&nbsp;&nbsp; }); <br />
<br />
<br />
注意回调中的程序代码是能够自由抛出SQLException的：Spring将会捕捉到这些异常并且用自己的类层次重新抛出。程序的开发者可以选择哪个异常，如果有的话，被捕捉然后处理。 <br />
<br />
JdbcTemplate提供许多支持不同情景包括prepared statements和批量更新的方法。Spring的JDBC抽象有比起标准JDBC来说性能损失非常小，甚至在当应用中需要的结果集数量很大的时候。 <br />
<br />
在org.springframework.jdbc.object包中是对JDBC的更高层次的抽象。这是建立在核心的JDBC回调功能基础纸上的，但是提供了一个能够对RDBMS操作——无论是查询，更新或者是存储过程——使用Java对象来建模的API。这个API部分是受到JDO查询API的影响，我发现它直观而且非常有用。 <br />
<br />
一个用于返回User对象的查询对象可能是这样的： <br />
<br />
代码: <br />
<br />
class UserQuery extends MappingSqlQuery { <br />
<br />
&nbsp;&nbsp; public UserQuery(DataSource datasource) { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; super(datasource, "SELECT * FROM PUB_USER_ADDRESS WHERE USER_ID = ?"); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; declareParameter(new SqlParameter(Types.NUMERIC)); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; compile(); <br />
&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp; // Map a result set row to a Java object <br />
&nbsp;&nbsp; protected Object mapRow(ResultSet rs, int rownum) throws SQLException { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; User user = new User(); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user.setId(rs.getLong("USER_ID")); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; user.setForename(rs.getString("FORENAME")); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return user; <br />
&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp; public User findUser(long id) { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Use superclass convenience method to provide strong typing <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (User) findObject(id); <br />
&nbsp;&nbsp; } <br />
} <br />
<br />
<br />
这个类可以在下面用上： <br />
代码: <br />
<br />
User user = userQuery.findUser(25); <br />
<br />
<br />
这样的对象经常可以用作DAO的inner class。它们是线程安全的，除非子类作了一些超出常规的事情。 <br />
<br />
在org.springframework.jdbc.object包中另一个重要的类是StoredProcedure类。Spring让存储过程通过带有一个业务方法的Java类进行代理。如果你喜欢的话，你可以定义一个存储过程实现的接口，意味着你能够把你的程序代码从对存储过程的依赖中完全解脱出来。 <br />
<br />
Spring数据访问异常层次是基于unchecked（运行时）exception的。在几个工程中使用了Spring之后，我越来越确信这个决定是正确的。 <br />
<br />
数据访问异常一般是不可恢复的。例如，如果我们不能链接到数据库，某个业务对象很有可能就不能完成要解决的问题了。一个可能的异常是 optimistic locking violation，但是不是所有的程序使用optimistic locking。强制编写捕捉其无法有效处理的致命的异常通常是不好的。让它们传播到上层的handler，比如servlet或者EJB 容器通常更加合适。所有的Spring对象访问异常都是 DataAccessException的子类，因而如果我们确实选择了捕捉所有的Spring数据访问异常，我们可以很容易做到这点。 <br />
<br />
注意如果我们确实需要从unchecked数据访问异常中恢复，我们仍然可以这么做。我们可以编写代码仅仅处理可恢复的情况。例如，如果我们认为只有optimistic locking violation是可恢复的，我们可以在Spring的DAO中如下这么写： <br />
<br />
代码: <br />
<br />
try { <br />
&nbsp;&nbsp; // do work <br />
} <br />
catch (OptimisticLockingFailureException ex) { <br />
&nbsp;&nbsp; // I'm interested in this <br />
} <br />
<br />
<br />
如果Spring的数据访问异常是checked的，我们需要编写如下的代码。注意我们还是可以选择这么写： <br />
代码: <br />
<br />
try { <br />
&nbsp;&nbsp; // do work <br />
} <br />
catch (OptimisticLockingFailureException ex) { <br />
&nbsp;&nbsp; // I'm interested in this <br />
} <br />
catch (DataAccessException ex) { <br />
&nbsp;&nbsp; // Fatal; just rethrow it <br />
} <br />
<br />
<br />
第一个例子的潜在缺陷是——编译器不能强制处理可能的可恢复的异常——这对于第二个也是如此。因为我们被强制捕捉base exception （DataAccessException），编译器不会强制对子类（OptimisticLockingFailureException）的检查。因而编译器可能强制我们编写处理不可恢复问题的代码，但是对于强制我们处理可恢复的问题并未有任何帮助。 <br />
<br />
Spring对于数据访问异常的unchecked使用和许多——可能是大多数——成功的持久化框架是一致的。（确实，它部分是受到JDO的影响。） JDBC是少数几个使用checked exception的数据访问API之一。例如TopLink和JDO大量使用 unchecked exception。Gavin King现在相信Hibernate也应该选择使用unchecked exception。 <br />
<br />
Spring的JDBC能够用以下办法帮助你： <br />
<br />
<br />
<br />
<br />
你决不需要在使用JDBC时再编写finally block。 <br />
<br />
总的来说你需要编写的代码更少了 <br />
<br />
你再也不需要挖掘你的RDBMS的文档以找出它为错误的列名称返回的某个罕见的错误代码。你的程序不再依赖于RDBMS特有的错误处理代码。 <br />
<br />
无论使用的是什么持久化技术，你都会发现容易实现DAO模式，让业务代码无需依赖于任何特定的数据访问API。 <br />
<br />
<br />
在实践中，我们发现所有这些都确实有助于生产力的提高和更少的bug。我过去常常厌恶编写JDBC代码；现在我发现我能够集中精力于我要执行的SQL，而不是烦杂的JDBC资源管理。 <br />
<br />
如果需要的话Spring的JDBC抽象可以独立使用——不强迫你把它们用作Spring的一部分。 <br />
O/R mapping 集成 <br />
<br />
当然你经常需要使用O/R mapping，而不是使用关系数据访问。你总体的应用程序框架也必须支持它。因而提供了对Hibernate 2.x和 JDO的集成支持。它的数据访问架构使得它能和任何底层的数据访问技术集成。Spring和Hibernate集成得尤其好。 <br />
<br />
为什么你要使用Hibernate加Spring，而不是直接使用Hibernate？ <br />
<br />
<br />
<br />
<br />
Session 管理 Spring提供有效率的，简单的以并且是安全的处理Hibernate Session。使用Hibernate的相关代码为了效率和恰当的事务处理一般需要使用相同的Hibernate &#8220;Session&#8221;对象。Spring让它容易透明地创建和绑定Session到当前的线程，要么使用声明式，AOP的method interceptor方法，要么在Java代码层面使用显式的，&#8220;template&#8221;包装类。因而 Spring解决了在Hibernate论坛上经常出现的用法问题。 <br />
<br />
资源管理 Spring的应用程序context能够处理Hiberante SessionFactories的位置和配置，JDBC数据源和其他相关资源。这使得这些值易于管理和改变。 <br />
<br />
集成的事务管理 Spring让你能够把你的Hibernate代码包装起来，要么使用声明式，AOP风格的method interceptor，要么在Java代码层面显式使用&#8220;template&#8221;包装类。在两种做法中，事务语义都为你处理了，并且在异常时也做好了恰当的事务处理（回滚，等）。如下面讨论的，你还获得了能够使用和替换不同transaction manager，而不会让你相关Hibernate代码受到影响的能力。额外的，JDBC 相关的代码能够完全事务性的和Hibernate代码集成。这对于处理没有在Hibernate实现的功能很有用。 <br />
<br />
如上描述的异常包装 Spring能够包装Hibernate异常，把它们从私有的，checked异常转换为一套抽象的运行时异常。这使得你能够仅仅在恰当的层面处理大部分不可恢复的持久化异常，而不影响样板catch/throw，和异常声明。你仍然能够在任何你需要的地方捕捉和处理异常。记住 JDBC异常（包括DB特有的方言）也被转换到相同的层次中，意味着你能在一致的编程模型中对JDBC执行相同的操作。 <br />
<br />
为了避免和厂商绑定 Hibernate是强大的，灵活的，开放源代码并且免费，但是它仍然使用私有的API。给出了一些选择，使用标准或者抽象API实现主要的程序功能通常是你想要的，当你需要因为功能，性能，或者其他考虑要转换到使用其他实现时。 <br />
<br />
让测试变简单 Spring的Inversion of Control方法使得改变Hibernate的session factories，数据源， transaction manager的实现和位置很容易，如果需要的话还能改变mapper object的实现。这使得更加容易分离和测试持久化相关的代码。 <br />
事务管理 <br />
抽象出一个数据访问的API是不够的；我们还需要考虑事务管理。JTA是显而易见的选择，但是它是一个直接用起来很笨重的API，因而许多J2EE开发者感到EJB CMT是对于事务管理唯一合理的选择。 <br />
<br />
Spring提供了它自己对事务管理的抽象。Spring提供了这些： <br />
<br />
<br />
<br />
通过类似于JdbcTemplate的回调模板编程管理事务，比起直接使用JTA要容易多了 <br />
<br />
类似于EJB CMT的声明式事务管理，但是不需要EJB容器 <br />
<br />
<br />
Spring的事务抽象式唯一的，它不绑定到JTA或者任何其他事务管理技术。Spring使用事务策略的概念把程序代码和底层的事务架构（例如JDBC）解藕。 <br />
<br />
为什么你要关心这些？JTA不是所有事务管理的最好答案吗？如果你正在编写仅仅使用一个数据库的程序，你不需要JTA的复杂度。你不关心XA事务或者两阶段提交。你甚至不需要提供这些东西的高端应用服务器。但是另一方面，你不会希望在需要和多个数据源打交道的时候重写你的代码。 <br />
<br />
假定你决定通过直接使用JDBC或者Hibernate的事务以避免JTA带来的额外负担。一旦你需要处理多个数据源，你必须剥开所有的事务管理代码并且使用JTA事务来替代。这不是非常有吸引力的并且导致大部分J2EE程序员，包括我自己，推荐只使用全局JTA事务。然而使用Spring事务抽象，你只需要重新配置Spring让它使用JTA，而不是JDBC或者Hibernate的事务策略，就一切OK了。这是一个配置上的改变，而不是代码的改动。因而，Spring使得你能够自由缩放应用。 <br />
AOP <br />
<br />
最近在应用AOP来解决企业关注点方面大家有了很大的兴趣，例如事务管理，这些都是EJB所要解决的。 <br />
<br />
Spring的AOP支持的首要目标是要给POJOs提供J2EE服务。这类似于JBoss 4的目标，Spring AOP由它能够在应用服务器之间移植的优势，因而没有绑死在厂商身上的风险。它既可以在web或者EJB容器中使用，也能够在WebLogic，Tomcat，JBoss，Resin， Jetty，Orion和许多其他应用服务器和web容器上使用。 <br />
<br />
Spring AOP支持method interception。所支持关键的AOP概念包括： <br />
<br />
<br />
<br />
Interception：自定义行为能够在对接口和类的调用之前和之后插入。这类似于AspectJ术语中类似的&#8220;around advice&#8221;。 <br />
<br />
Introduction：指定advice会导致对象实现额外的接口。这混乱了继承。 <br />
<br />
静态和动态的pointcuts：在interception发生的程序执行处指定points。静态pointcuts concern函数签名；动态 pointcuts也可以在point被求值的地方考虑函数的参数。Pointcuts独立interceptors单独定义，使得标准 interceptor可以应用于不同应用程序和代码上下文。 <br />
<br />
<br />
Spring既支持有状态（一个advised对象一个实例）也支持无状态的interceptors（所有advice使用一个实例）。 <br />
<br />
Spring不支持field interception。这是一个经过深思熟虑的设计决定。我总是感觉field interception违反了封装。我比较倾向于把AOP作为补全物，而不是与OOP冲突的东西。如果在5年或者10年后，我们在AOP学习曲线上走得更远了并且觉得应该在程序设计的桌面上给AOP一个位置，我不会惊讶的。（然而在那个时候基于语言的解决方案例如AspectJ可能比它们今天看来更加具有吸引力。） <br />
<br />
Spring使用动态代理实现AOP（其中存在一个接口）或者在运行时使用CGLIB生成字节码（这使得能够代理类）。两种方法都能够在任何应用服务器中使用。 <br />
<br />
Spring是第一个实现AOP Alliance interfaces的AOP 框架（www.sourceforge.net/projects/aopalliance）。这些是定义在不同AOP框架中能够互操作interceptors的尝试。 <br />
<br />
在TheServerSide和其他地方有一个正在进行但是不是那么引人注目的争论，就是这种interception是不是&#8220;true AOP&#8221;。我倒不怎么在意它叫什么；仅仅需要知道它是否在实践中有用就好了。我也乐于称它为&#8220;declarative middleware&#8221;（声明式中间件）。把 Spring AOP认做简单，轻量级的无状态beans的替代物，这样就不需要monolithic EJB容器了，而这些仅仅是让你能够构建有你需要的服务的容器。我不推荐advising任何一个POJO，对local SLSBs的类比有助于你理解推荐的粒度。（然而，与EJB不同的是，在恰当但是少见的情况下，你可以自由地把Spring的AOP应用到粒度更好的对象上。） <br />
<br />
因为Spring在实例上advises 对象，而不是在class loader层面上，使用有不同advice的同一个类的多个实例是可能的，或者与advised实例一道使用unadvised 实例。 <br />
<br />
可能Spring AOP最常见的应用是声明式事务管理。这是基于前面描述的TansactionTemplate抽象上的，并且可以给任何POJO提供声明式事务管理。取决于事务策略，底层的机制可以是JTA，JDBC，Hibernate或者任何其他提供事务管理的API。 <br />
<br />
Spring的声明式事务管理类似于EJB CMT，在以下方面有些不同： <br />
<br />
<br />
<br />
事务管理能够应用于任何POJO。我们推荐业务对象实现接口，但是这只是一个好的编程习惯的问题，而不是由框架强制的。 <br />
<br />
通过使用Spring的事务API能够在事务性POJO中实现编程回调。我们为此提供静态的方法，使用ThreadLoacal变量，因而你不需要传播诸如EJBContext这样的context对象来确保回滚。 <br />
<br />
你可以声明式地定义&#8220;回滚规则&#8221;。EJB不会在未捕捉程序异常的时候自动回滚（仅仅在unchecked exceptions和其他 Throwables的时候），应用程序开发者经常需要在任何异常发生时回滚。Spring事务管理让你能够声明式地指定什么异常什么子类能够导致自动回滚。缺省的行为和EJB是一致的，但是你能够在checked和unchecked异常时自动回滚。这个在最少化自编程回调代码方面有很大好处，而回调依赖于Spring的事务API（因为EJB的编程回调时在EJBContext中完成的）。 <br />
<br />
事务管理不绑定于JTA。如前面解释过的，Spring的事务管理能够在不同事务策略中使用。 <br />
<br />
<br />
当然还可以使用Spring AOP实现程序特有的aspects。取决于你对AOP概念的接受程度，决定你是否选择这么做，而不是Spring的能力，但是它确实非常有用。我们所见过的成功例子包括： <br />
<br />
<br />
<br />
自定义的security interception，当安全检查的复杂度超出了J2EE安全架构的能力的时候 <br />
<br />
在开发中使用的调试和profiling aspects <br />
<br />
发送email通知管理员用户不寻常的举动的Interceptors <br />
<br />
<br />
程序自定的aspects能够成为消除需要许多函数的样板代码的有利武器。 <br />
<br />
Spring AOP透明地与Spring BeanFactory概念集成。包含一个来自Spring BeanFactory对象地代码不需要知道它是还是不是advised。和任何对象一样，契约实在接口和对象实现中定义的。 <br />
<br />
下面的XML片断展示了如何定义一个AOP代理： <br />
代码: <br />
<br />
&amp;lt;bean id="myTest" <br />
&nbsp;&nbsp; class="org.springframework.aop.framework.ProxyFactoryBean"&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;property name="proxyInterfaces"&amp;gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;value&amp;gt;org.springframework.beans.ITestBean&amp;lt;/value&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;/property&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;property name="interceptorNames"&amp;gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;list&amp;gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;value&amp;gt;txInterceptor&amp;lt;/value&amp;gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;value&amp;gt;target&amp;lt;/value&amp;gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;/list&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;/property&amp;gt; <br />
&amp;lt;/bean&amp;gt; <br />
<br />
<br />
注意bean类的定义总是AOP框架的ProxyFactoryBean，虽然bean的类型在引用中使用或者由BeanFactory的getBean ()方法返回时依赖的是代理接口。（多个代理方法是被支持的。）ProxyFactoryBean的&#8220;interceptorNames&#8221;属性需要一个字符串列表。（因为如果代理是一个&#8220;prototype&#8221;而不是singleton，有状态interceptors可能需要创建新的实例，所以必须使用 Bean的名字而不是bean的引用。）列表中的名字可以是interceptor或者pointcuts（interceptors和有关它们合适被使用的信息）。列表中的&#8220;target&#8221;值自动创建一个&#8220;invoker interceptor&#8221;封装target对象。实现代理接口的是在 factory中的bean的名字。这个例子中的myTest可以和其他bean factory中的bean一样使用。例如，其他对象可以使用&amp;lt; ref&amp;gt;元素引用它而且这些引用是由Spring IoC设置的。 <br />
<br />
还可以不用BeanFactory，编程构建AOP代理，虽然这个很少用得上： <br />
<br />
代码: <br />
<br />
TestBean target = new TestBean(); <br />
DebugInterceptor di = new DebugInterceptor(); <br />
MyInterceptor mi = new MyInterceptor(); <br />
ProxyFactory factory = new ProxyFactory(target); <br />
factory.addInterceptor(0, di); <br />
factory.addInterceptor(1, mi); <br />
// An "invoker interceptor" is automatically added to wrap the target <br />
ITestBean tb = (ITestBean) factory.getProxy(); <br />
<br />
<br />
我们相信最好把程序装配从Java代码中移出来，而AOP也不例外。 <br />
<br />
Spring在它的AOP能力方面的直接竞争者是Jon Tirsen的Nanning Aspects（http://nanning.codehaus.org）。 <br />
<br />
我觉得AOP作为EJB的替代无提供企业服务这个用法方面的进步是重要的。随着时间，这将成为Spring很重要的关注点。 <br />
MVC web 框架 <br />
<br />
Spring包括一个强大而且高度可配置的MVC web 框架。 <br />
<br />
Spring的MVC model类似于Struts。在多线程服务对象这点上，Spring的Controller类似于Struts Action，只有一个实例处理所有客户的请求。然而，我们相信Spring的MVC比起Struts有很多优点，例如： <br />
<br />
<br />
<br />
Spring在controllers，JavaBean，models和views提供了一个非常清晰的划分。 <br />
<br />
Spring的MVC是非常灵活的。不像Struts，它强制你的Action和Form对象进入固化的层次之中（因而你迫使你使用Java的实体继承），Spring MVC完全是基于接口的。而且，通过插入你自己的接口几乎Spring MVC 框架的所有部分都是可配置的。当然我们也提供了方便的类作为实现选择。 <br />
<br />
Spring MVC是真正的view无关的。你不会被强制使用JSP，如果你不想那么做的话。你可以使用Velocity，XSLT或其他view技术。如果你想要使用自定义的view机制——例如，你自己的模板语言——你可以简单实现Spring的View接口并且把它集成进来。 <br />
<br />
和其他对象一样，Spring的Controllers是通过IoC配置的。着使得它们易于测试，并且完美地和其他由Spring管理的对象集成。 <br />
<br />
Web层变成了业务对象层之上的薄薄一层。这鼓励了好的习惯。Struts和其他专门的web框架让你去实现你自己的业务对象；Spring提供了你应用程序所有层的集成。 <br />
<br />
<br />
如在Struts 1.1中所见的，你可以有和你在Spring MVC 应用程序中所需要的一样多的dispatcher servlets。 <br />
<br />
下面的例子展示了一个简单的Spring Controller如何能够访问定义在应用程序context中的业务对象。这个controller在它的handleRequest()方法中执行了Google搜索： <br />
<br />
代码: <br />
<br />
public class GoogleSearchController <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; implements Controller { <br />
<br />
&nbsp;&nbsp; private IGoogleSearchPort google; <br />
<br />
&nbsp;&nbsp; private String googleKey; <br />
<br />
&nbsp;&nbsp; public void setGoogle(IGoogleSearchPort google) { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.google = google; <br />
&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp; public void setGoogleKey(String googleKey) { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.googleKey = googleKey; <br />
&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp; public ModelAndView handleRequest( <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HttpServletRequest request, HttpServletResponse response) <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throws ServletException, IOException { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String query = request.getParameter("query"); <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; GoogleSearchResult result = <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Google property definitions omitted... <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Use google business object <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; google.doGoogleSearch(this.googleKey, query, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; start, maxResults, filter, restrict, <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; safeSearch, lr, ie, oe); <br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return new ModelAndView("googleResults", "result", result); <br />
&nbsp;&nbsp; } <br />
} <br />
<br />
<br />
这段代码使用的prototype中，IGoogleSearchPort是一个GLUE web services代理，由 Spring FActoryBean返回。然而，Spring把controller从底层web service库中分离出来。接口可以使用普通的 Java对象，test stub，mock对象或者如下面要讨论的EJB代理实现。这个contorller不包括资源查找；除了支持它的web交互的必要代码之外没有别的什么了。 <br />
<br />
Spring还提供了数据绑定，forms，wizards和更复杂的工作流的支持。 <br />
<br />
对Spring MVC 框架的优秀简介是Thomas Risberg的Spring MVC 教程（http://www.springframework.org/docs/MVC-step-by-step/Spring-MVC-step-by-step.html）。还可以参见&#8220;Web MVC with the Spring Framework&#8221;（http://www.springframework.org/docs/web_mvc.html）。 <br />
<br />
如果你乐于使用你钟情的MVC框架，Spring的分层架构使得你能够使用Spring的其他部分而不用MVC层。我们有使用Spring做中间层管理和数据访问，但是在web层使用Struts，WebWork或者Tapestry的用户。 <br />
实现EJB <br />
<br />
如果你选择使用EJB，Spring能在EJB实现和客户端访问EJB两方面都提供很大的好处。 <br />
<br />
对业务逻辑进行重构，把它从EJB facades中取出到POJO已经得到了广泛的认同。（不讲别的，这使得业务逻辑更容易单元测试，因为EJB严重依赖于容器而难于分离测试。）Spring为session bean和message driver bean提供了方便的超类，使得通过自动载入基于包含在EJB Jar文件中的XML文档BeanFactory让这变得很容易。 <br />
<br />
这意味着stateless session EJB可以这么获得和使用所需对象： <br />
<br />
代码: <br />
<br />
import org.springframework.ejb.support.AbstractStatelessSessionBean; <br />
<br />
public class MyEJB extends AbstractStatelessSessionBean <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; implements MyBusinessInterface { <br />
&nbsp;&nbsp; private MyPOJO myPOJO; <br />
<br />
&nbsp;&nbsp; protected void onEjbCreate() { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.myPOJO = getBeanFactory().getBean("myPOJO"); <br />
&nbsp;&nbsp; } <br />
<br />
&nbsp;&nbsp; public void myBusinessMethod() { <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.myPOJO.invokeMethod(); <br />
&nbsp;&nbsp; } <br />
} <br />
<br />
<br />
假定MyPOJO是一个接口，它的实现类——以及任何它需要的配置，注入基本的属性和更多的合作者——在XML bean factory 定义中隐藏。 <br />
<br />
我们通过在ejb-jar.xmldeployment descriptor中名为ejb/BeanFactoryPath的环境变量定义告诉Spring去哪儿装载XML文档。如下： <br />
<br />
代码: <br />
<br />
&amp;lt;session&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;ejb-name&amp;gt;myComponent&amp;lt;/ejb-name&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;local-home&amp;gt;com.test.ejb.myEjbBeanLocalHome&amp;lt;/local-home&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;local&amp;gt;com.mycom.MyComponentLocal&amp;lt;/local&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;ejb-class&amp;gt;com.mycom.MyComponentEJB&amp;lt;/ejb-class&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;session-type&amp;gt;Stateless&amp;lt;/session-type&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;transaction-type&amp;gt;Container&amp;lt;/transaction-type&amp;gt; <br />
<br />
&nbsp;&nbsp; &amp;lt;env-entry&amp;gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;env-entry-name&amp;gt;ejb/BeanFactoryPath&amp;lt;/env-entry-name&amp;gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;env-entry-type&amp;gt;java.lang.String&amp;lt;/env-entry-type&amp;gt; <br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &amp;lt;env-entry-value&amp;gt;/myComponent-ejb-beans.xml&amp;lt;/env-entry-value&amp;gt;&amp;lt;/env-entry&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;/env-entry&amp;gt; <br />
&amp;lt;/session&amp;gt; <br />
<br />
<br />
myComponent-ejb-beans.xml 文件将会从classpath装载：在本例中，是EJB Jar文件的根目录。每个EJB都能指定自己的XML文档，因而这个机制能在每个EJB Jar文件中使用多次。 <br />
<br />
Spring 的超类实现了EJB中诸如setSessionContext()和ejbCreate()的生命周期管理的方法，让应用程序开发者只需选择是否实现Spring的onEjbCreate()方法。 <br />
使用EJB <br />
<br />
Spring还让实现EJB变得更加容易。许多EJB程序使用Service Locator和Business Delegate模式。这些比在客户代码中遍布JNDI查找强多了，但是它们常见的实现方式有显著的缺点，例如： <br />
<br />
<br />
<br />
使用EJB的典型代码依赖Service Locator或者Business Delegate singletons，使得测试难于进行。 <br />
<br />
在Service Locator模式没有使用Business Delegate的情况下，程序代码还要在EJB home中调用create()方法，并且处理可能导致的异常。因而仍然绑定在EJB API身上，忍受着EJB 编程模型的复杂度。 <br />
<br />
实现Business Delegate模式通常导致显著的代码重复，其中我们必须编写大量仅仅是调用EJB同等方法的方法。 <br />
<br />
<br />
基于这些和其他原因，传统的EJB访问，如在Sun Adventure Builder和OTN J2EE Virtual Shopping Mall中展示的那样，会降低生产率并且带来显著的复杂度。 <br />
<br />
Spring通过引入codeless business delegate前进了一步。有了Spring，你不再需要再编写另一个 Service Locator，另一个JNDI查找，或者在硬编码的Business Delegate中重复代码，除非你肯定这增加了价值。 <br />
<br />
例如，假定我们有使用local EJB的web controller。我们将遵循最佳实践，使用 EJB Business Methods Interface模式，EJB的local interface extend非EJB专有的业务方法接口。（这么做的主要的一个原因是确保在本地接口和bean实现类中方法签名的自动同步。）让我们调用这个业务方法接口MyComponent。当然我们还需要实现local home接口并且提供实现SessionBean和MyComponent业务方法的bean的实现类。 <br />
<br />
用了Spring EJB 访问，我们把我们的web层controller和EJB实现挂接上所需要进行的Java编码仅仅是在我们的controller中暴露一个类型MyComponent的setter方法。这将如下保存作为实例变量的引用： <br />
<br />
代码: <br />
<br />
private MyComponent myComponent; <br />
<br />
public void setMyComponent(MyComponent myComponent) { <br />
&nbsp;&nbsp; this.myComponent = myComponent; <br />
} <br />
<br />
<br />
我们随后在任何业务方法中使用这个实例变量。 <br />
<br />
Spring自动完称剩下的工作，通过像这样的XML bean定义。LocalStatelessSessionProxyFactoryBean是一个可以用于任何EJB的通用factory bean。它创建的对象能够自动被Spring转型为MyComponent类型。 <br />
<br />
代码: <br />
<br />
&amp;lt;bean id="myComponent" <br />
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean"&amp;gt; <br />
<br />
&nbsp;&nbsp; &amp;lt;property name="jndiName"&amp;gt;&amp;lt;value&amp;gt;myComponent&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;property name="businessInterface"&amp;gt;&amp;lt;value&amp;gt;com.mycom.MyComponent&amp;lt;/value&amp;gt;&amp;lt;/property&amp;gt; <br />
&amp;lt;/bean&amp;gt; <br />
<br />
&amp;lt;bean id="myController" <br />
&nbsp;&nbsp; class = "com.mycom.myController" <br />
&amp;gt; <br />
&nbsp;&nbsp; &amp;lt;property name="myComponent"&amp;gt;&amp;lt;ref bean="myComponent"/&amp;gt;&amp;lt;/property&amp;gt; <br />
&amp;lt;/bean&amp;gt; <br />
<br />
<br />
在幕后有许多魔法般的事情发生，Spring AOP framework的殷勤，虽然不强迫你使用AOP的概念享受这些结果。 &#8220;myComponent&#8221;bean定义为EJB创建一个代理，它实现了业务方法的接口。EJB local home在启动的时候被缓存，因而只需要一次JNDI查找。每次EJB被调用的时候，代理调用local EJB中的create()方法并且调用EJB中对应的业务方法。 <br />
<br />
myController bean定义为这个代理设置controller类的myController属性。 <br />
<br />
这个EJB访问机制极大简化了应用程序的代码： <br />
<br />
<br />
<br />
Web层的代码不依赖于EJB的使用。如果你想要使用POJO，mock object或者其他test stub替代EJB引用，我们可以简单地改动一下myComponent bean定义而不影响一行Java代码 <br />
<br />
我们还不需要写一行JNDI查找或者其他EJB plumbing code。 <br />
<br />
<br />
在实际程序中的性能测试和经验标明这种方法（包括对目标EJB的反射调用）的性能影响是很小的，在典型的应用中检测不出。记住无论如何我们都不希望使用fine-grained的EJB调用，因为会有有关应用服务器上的EJB的底层架构方面的代价。 <br />
<br />
我们可以把相同方法应用于远程EJB，通过类似 org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean factory bean 的方法。然而我们无法隐藏远程EJB的业务方法接口中的RemoteException。 <br />
测试 <br />
<br />
如你可能已经注意到的，我和其他Spring开发这是全面单元测试重要性的坚定支持者。我们相信框架被彻底单元测试过的是非常重要的，而且我们框架设计的主要目标是让建立在框架之上的程序易于单元测试。 <br />
<br />
Spring自身有一个极好的单元测试包。我们的1.0 M1的单元测试覆盖率是75%，而且我们希望在1.0 RC1的时候能够达到80%的单元测试覆盖率。我们发现在这个项目中测试优先的开发带来的好处是实实在在的。例如，它使得作为国际化分布式开发团队的工作极端有效率，而且用户评论 CVS snapshots趋向于稳定和使用安全。 <br />
<br />
因为以下理由，我们相信用Spring构建的应用程序是非常易于测试的： <br />
<br />
<br />
<br />
IoC推动了单元测试 <br />
<br />
应用程序不包括直接使用注入JNDI的J2EE服务的plumbing code，这些代码一般让测试难于进行 <br />
<br />
Spring bean factories和contexts能够在容器外设置 <br />
<br />
<br />
在容器外可以设置Spring bean factory的能力提供了对开发过程有趣的可选项。在几个使用Spring的web应用中，工作是从定义业务接口和在web容器外集成测试开始的。在业务功能已经足够完整之后，web接口不过是添加在其上的薄薄一层。 <br />
谁在使用Spring <br />
<br />
虽然相对来说Spring还是一个新的项目，但是我们已经有了一个令人印象深刻并且不断增长的用户群。它们已经有许多产品使用着Spring。用户包括一个主要的全球投资银行（做大型项目的），一些知名的网络公司，几个web开发顾问机构，卫生保健公司，以及学院机构。 <br />
<br />
许多用户完整地使用Spring，但是一些只单独使用一些组件。例如，大量用户使用我们地JDBC或者其他数据访问功能。 <br />
Roadmap <br />
<br />
在今年晚些时候我们主要要做的是让Spring发布release 1.0。然而，我们还有一些更长远的目标。 <br />
<br />
为1.0 final规划地主要改进式源代码级地元数据支持，它主要用于（但不局限于）AOP框架。这将使得C#风格的attribute驱动的事务管理，并且让声明式企业服务在典型应用情况下非常容易配置。Attribute支持将会在Spring的1.0 final release支持中加入，并且设计的是在发布的那个时候能与JSR-175集成。 <br />
<br />
1.0之后，一些可能的改进地方包括： <br />
<br />
<br />
<br />
通过对我们的JDBC和事务支持的一个相当抽象来支持JMS <br />
<br />
支持bean factories的动态重配置 <br />
<br />
提供web services的能力 <br />
<br />
IDE和其他工具支持 <br />
<br />
<br />
作为一个敏捷项目，我们主要是受到用户需求的驱动。因而我们不会开发没有一个用户需要的特性，并且我们会仔细倾听来自用户群的声音。 <br />
总结 <br />
<br />
Spring是一个解决了许多在J2EE开发中常见的问题的强大框架。 <br />
<br />
Spring提供了管理业务对象的一致方法并且鼓励了注入对接口编程而不是对类编程的良好习惯。Spring的架构基础是基于使用JavaBean属性的 Inversion of Control容器。然而，这仅仅是完整图景中的一部分：Spring在使用IoC容器作为构建完关注所有架构层的完整解决方案方面是独一无二的。 <br />
<br />
Spring提供了唯一的数据访问抽象，包括简单和有效率的JDBC框架，极大的改进了效率并且减少了可能的错误。Spring的数据访问架构还集成了Hibernate和其他O/R mapping解决方案。 <br />
<br />
Spring还提供了唯一的事务管理抽象，它能够在各种底层事务管理技术，例如JTA或者JDBC纸上提供一个一致的编程模型。 <br />
<br />
Spring提供了一个用标准Java语言编写的AOP框架，它给POJOs提供了声明式的事务管理和其他企业事务——如果你需要——还能实现你自己的aspects。这个框架足够强大，使得应用程序能够抛开EJB的复杂性，同时享受着和传统EJB相关的关键服务。 <br />
<br />
Spring还提供了可以和总体的IoC容器集成的强大而灵活的MVC web框架。 <br />
更多信息 <br />
<br />
参见以下资源获得关于Spring的更多信息： <br />
<br />
<br />
<br />
Expert One-on-One J2EE Design and Development（Rod Johnson，Wrox，2002）。虽然Spring在书出版之后已经极大地进步和改进了，它仍然是理解Spring动机的极佳途径。 <br />
<br />
Spring的主页：http://www.springframework.org。这里包括Javadoc和几个教程。 <br />
<br />
在Sourceforge上的论坛和下载 <br />
<br />
Spring用户和Spring开发者的邮件列表 <br />
<br />
<br />
我们正在尽我们可能去改进Spring的文档和示例。我们还为在信件和邮件列表中极好的回复率自豪。我们希望你能快速融入我们的社区！ <br />
关于作者 <br />
<br />
Rod Johnson 作为Java开发者和架构师已经有了7年的经验了并且在J2EE平台出现之初就在其上进行开发了。他是《Expert One- on-One J2EE Design and Development》(Wrox，2002)的作者并且贡献了其他好几本关于J2EE的书。他当前正在为Wiley撰写另外一本有关J2EE架构的书。Rod在两个Java标准委员会服务并且经常师大会发言人。现在他在UK做一个咨询顾问。 </div>
 <img src ="http://www.blogjava.net/lanxin1020/aggbug/264363.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-07 23:30 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/07/264363.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>spring 容器 内部工作机制(转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/07/264360.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Tue, 07 Apr 2009 15:06:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/07/264360.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/264360.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/07/264360.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/264360.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/264360.html</trackback:ping><description><![CDATA[Spring的AbstractApplicationContext是ApplicationContext抽象实现类，该抽象类的refresh()方法定义了Spring容器在加载配置文件后的各项处理过程，这些处理过程清晰刻画了Spring容器启动时所执行的各项操作。下面，我们来看一下refresh()内部定义了哪些执行逻辑：<br />
<table class="ln" border="1" cellspacing="0" bordercolorlight="#999999" bordercolordark="#ffffff" bgcolor="#ddddd" align="center">
    <tbody>
        <tr>
            <td bgcolor="#ffffff"><a href="http://book.51cto.com/files/uploadimg/20070809/133245666.gif" target="_blank"><img style="width: 498px; zoom: 70%; height: 286px" class="fit-image" onmousewheel="javascript:return big(this)" border="0" alt="" src="http://book.51cto.com/files/uploadimg/20070809/133245666.gif" width="458" onload="javascript:if(this.width />498)this.style.width=498;" height=224></a><a href="http://book.51cto.com/files/uploadimg/20060921/153223104.gif" target="_blank"></a></td>
        </tr>
        <tr>
        </tr>
    </tbody>
</table>
1．初始化BeanFactory：根据配置文件实例化BeanFactory，getBeanFactory()方法由具体子类实现。在这一步里，Spring将配置文件的信息装入到容器的Bean定义注册表（BeanDefinitionRegistry）中，但此时Bean还未初始化；<br />
2．调用工厂后处理器：根据反射机制从BeanDefinitionRegistry中找出所有BeanFactoryPostProcessor类型的Bean，并调用其postProcessBeanFactory()接口方法；<br />
3．注册Bean后处理器：根据反射机制从BeanDefinitionRegistry中找出所有BeanPostProcessor类型的Bean，并将它们注册到容器Bean后处理器的注册表中；<br />
4．初始化消息源：初始化容器的国际化信息资源；<br />
5．初始化应用上下文事件广播器；<br />
6．初始化其他特殊的Bean：这是一个钩子方法，子类可以借助这个钩子方法执行一些特殊的操作：如AbstractRefreshableWebApplicationContext就使用该钩子方法执行初始化ThemeSource的操作；<br />
7．注册事件监听器；<br />
8．初始化singleton的Bean：实例化所有singleton的Bean，并将它们放入Spring容器的缓存中；<br />
9．发布上下文刷新事件：创建上下文刷新事件，事件广播器负责将些事件广播到每个注册的事件监听器中。<br />
在第3.4节中，我们观摩了Bean从创建到销毁的生命历程，这些过程都可以在上面的过程中找到对应的步骤。Spring协调多个组件共同完成这个复杂的工程流程，图5-1描述了Spring容器从加载配置文件到创建出一个完整Bean的作业流程以及参与的角色。<br />
<table class="ln" border="1" cellspacing="0" bordercolorlight="#999999" bordercolordark="#ffffff" bgcolor="#ddddd" align="center">
    <tbody>
        <tr>
            <td bgcolor="#ffffff"><a href="http://book.51cto.com/files/uploadimg/20070809/133307508.gif" target="_blank"><img style="width: 498px; zoom: 80%; height: 297px" class="fit-image" onmousewheel="javascript:return big(this)" border="0" alt="" src="http://book.51cto.com/files/uploadimg/20070809/133307508.gif" width="473" onload="javascript:if(this.width />498)this.style.width=498;" height=277></a><a href="http://book.51cto.com/files/uploadimg/20060921/153223104.gif" target="_blank"></a></td>
        </tr>
        <tr>
            <td class="it" bgcolor="#dddddd" align="center">图5-1&nbsp; IoC的流水线</td>
        </tr>
    </tbody>
</table>
1．ResourceLoader从存储介质中加载Spring配置文件，并使用Resource表示这个配置文件的资源；<br />
2．BeanDefinitionReader读取Resource所指向的配置文件资源，然后解析配置文件。配置文件中每一个&lt;bean&gt;解析成一个BeanDefinition对象，并保存到BeanDefinitionRegistry中；<br />
3．容器扫描BeanDefinitionRegistry中的BeanDefinition，使用Java的反射机制自动识别出Bean工厂后处理器（实现BeanFactoryPostProcessor接口）的Bean，然后调用这些Bean工厂后处理器对BeanDefinitionRegistry中的BeanDefinition进行加工处理。主要完成以下两项工作：<br />
1）对使用到占位符的&lt;bean&gt;元素标签进行解析，得到最终的配置值，这意味对一些半成品式的BeanDefinition对象进行加工处理并得到成品的BeanDefinition对象；<br />
2）对BeanDefinitionRegistry中的BeanDefinition进行扫描，通过Java反射机制找出所有属性编辑器的Bean（实现java.beans.PropertyEditor接口的Bean），并自动将它们注册到Spring容器的属性编辑器注册表中（PropertyEditorRegistry）；<br />
4．Spring容器从BeanDefinitionRegistry中取出加工后的BeanDefinition，并调用InstantiationStrategy着手进行Bean实例化的工作；<br />
5．在实例化Bean时，Spring容器使用BeanWrapper对Bean进行封装，BeanWrapper提供了很多以Java反射机制操作Bean的方法，它将结合该Bean的BeanDefinition以及容器中属性编辑器，完成Bean属性的设置工作；<br />
6．利用容器中注册的Bean后处理器（实现BeanPostProcessor接口的Bean）对已经完成属性设置工作的Bean进行后续加工，直接装配出一个准备就绪的Bean。<br />
Spring容器确实堪称一部设计精密的机器，其内部拥有众多的组件和装置。Spring的高明之处在于，它使用众多接口描绘出了所有装置的蓝图，构建好Spring的骨架，继而通过继承体系层层推演，不断丰富，最终让Spring成为有血有肉的完整的框架。所以查看Spring框架的源码时，有两条清晰可见的脉络：<br />
1）接口层描述了容器的重要组件及组件间的协作关系；<br />
2）继承体系逐步实现组件的各项功能。<br />
接口层清晰地勾勒出Spring框架的高层功能，框架脉络呼之欲出。有了接口层抽象的描述后，不但Spring自己可以提供具体的实现，任何第三方组织也可以提供不同实现， 可以说Spring完善的接口层使框架的扩展性得到了很好的保证。纵向继承体系的逐步扩展，分步骤地实现框架的功能，这种实现方案保证了框架功能不会堆积在某些类的身上，造成过重的代码逻辑负载，框架的复杂度被完美地分解开了。<br />
Spring组件按其所承担的角色可以划分为两类：<br />
1）物料组件：Resource、BeanDefinition、PropertyEditor以及最终的Bean等，它们是加工流程中被加工、被消费的组件，就像流水线上被加工的物料；<br />
2）加工设备组件：ResourceLoader、BeanDefinitionReader、BeanFactoryPostProcessor、InstantiationStrategy以及BeanWrapper等组件像是流水线上不同环节的加工设备，对物料组件进行加工处理。<br />
我们在第3章中已经介绍了Resource和ResourceLoader这两个组件。在本章中，我们将对其他的组件进行讲解。<br />
 <img src ="http://www.blogjava.net/lanxin1020/aggbug/264360.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-07 23:06 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/07/264360.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用Java的反射与代理实现AOP(转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/06/264075.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Mon, 06 Apr 2009 03:50:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/06/264075.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/264075.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/06/264075.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/264075.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/264075.html</trackback:ping><description><![CDATA[<p>利用Java的反射与代理实现AOP</p>
<p>&nbsp;</p>
<div><font size="2"><strong>一．AOP</strong><strong>概述</strong></font></div>
<div><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: black">AOP(Aspect&nbsp;Oriented&nbsp;Programing)</span><span style="color: black">，即面向切面编程，它主要用于日志记录、性能统计、</span><span style="color: black"><a href="http://net.rdxx.com/Safe/SafeTech/" target="_blank"><span style="color: black; text-decoration: none; text-underline: none">安<span>全</span></span></a></span><span style="color: black">控制、事务处理、异常处理等方面。它的主要意图就要将日志记录，性能统计，<a style="color: #000000" title="安全" href="http://safe.it168.com/" target="_blank">安全</a>控制、事务处理、异常处理等等代码从业务逻辑代码中清楚地划分出来。通过对这些行为的分离，我们希望可以将它们独立地配置到业务逻辑方法中，而要改变这些行为的时候也不需要影响到业务逻辑方法代码。</span></font></div>
<div><font size="2"><span style="color: black">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="color: black">下面让我们来看一个利用</span><span style="color: black">AOP</span><span style="color: black">来实现日志记录的例子，在没有使用</span><span style="color: black">AOP</span><span style="color: black">之前，我们的代码如下面所讲述。</span></font></div>
<div><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>下面这段代码为业务的接口类代码：<br />
<br />
</font>
<div style="width: 645px; height: 578px; scrollbar-highlight-color: buttonhighlight; overflow: auto">
<pre style="border-bottom: black 1px solid; border-left: black 1px solid; padding-bottom: 4px; background-color: #ededed; padding-left: 4px; padding-right: 4px; border-top: black 1px solid; border-right: black 1px solid; padding-top: 4px">
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><font size="2"><span style="color: #000000">package org.amigo.proxy;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 业务逻辑类接口.
* @author &lt;a href="mailto:xiexingxing1121@126.com"&gt;AmigoXie&lt;/a&gt;
* Creation date: 2007-10-7 - 上午09:09:53
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
</span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">interface</span></font><font size="2"><span style="color: #000000"> BusinessObj {
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 执行业务.
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> process();
}
BusinessObj接口的某个实现类代码如下：
package org.amigo.proxy;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 业务逻辑对象实现类.
* @author &lt;a href="mailto:xiexingxing1121@126.com"&gt;AmigoXie&lt;/a&gt;
* Creation date: 2007-10-7 - 上午09:11:49
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
</span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span></font><font size="2"><span style="color: #000000"> BusinessObjImpl implements BusinessObj {
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 执行业务.
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> process() {
</span><span style="color: #0000ff">try</span></font><font size="2"><span style="color: #000000"> {
System.</span><span style="color: #0000ff">out</span><span style="color: #000000">.println(</span><span style="color: #000000">"</span><span style="color: #000000">before process</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">);
System.</span><span style="color: #0000ff">out</span><span style="color: #000000">.println(</span><span style="color: #000000">"</span><span style="color: #000000">执行业务逻辑</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">);
System.</span><span style="color: #0000ff">out</span><span style="color: #000000">.println(</span><span style="color: #000000">"</span><span style="color: #000000">after process</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">);
} </span><span style="color: #0000ff">catch</span></font><font size="2"><span style="color: #000000"> (Exception e) {
System.err.println(</span><span style="color: #000000">"</span><span style="color: #000000">发生异常:</span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span></font><span style="color: #000000"><font size="2"> e.toString());
}
}
}</font></span></div>
</pre>
</div>
</div>
<div><font size="2">&nbsp;&nbsp;&nbsp; 在上例中我们可以看到，在执行业务方法前、执行业务方法后以及异常发生时的日志记录，我们都是通过在对应的类中写入记录日志的代码来实现的，当有这种日志记录需求的业务逻辑类不断增多时，将会给我们的维护带来很大困难，而且，在上面的例子中，日志代码和业务逻辑代码混合在一起，为日后的维护工作又抹上了一层&#8220;恐怖&#8221;色彩。</font></div>
<div><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>按照AOP的思想，我们首先需要寻找一个切面，在这里我们已经找到，即在业务逻辑执行前后以及异常发生时，进行相应的日志记录。我们需要将这部分日志代码放入一个单独的类中，以便为以后的修改提供方便。</font></div>
<div><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>我们在截获某个业务逻辑方法时，可以采用Java的动态代理机制来实现。</font></div>
<div><font size="2"></font>&nbsp;</div>
<div><font size="2"></font>&nbsp;</div>
<div>
<div><font size="2"><strong>二．Java</strong><strong>的动态代理机制</strong></font></div>
<div style="text-indent: 21pt"><font size="2">代理模式是常用的Java设计模式。代理类主要负责为委托类预处理消息、过滤信息、把消息转发给委托类，以及事后处理信息等。</font></div>
<div style="text-indent: 21pt"><font size="2">动态代理类不仅简化了编程工作，而且提高了<a style="color: #000000" title="软件" href="http://software.it168.com/" target="_blank">软件</a>系统的扩展性和可维护性。<span style="color: black">我们可以通过实现</span><span style="color: black">java.lang.reflect.InvocationHandler</span><span style="color: black">接口提供一个执行处理器，然后通过</span><span style="color: black">java.lang.reflect.Proxy</span><span style="color: black">得到一个代理对象，通过这个代理对象来执行业务逻辑方法</span><span style="color: black">,</span><span style="color: black">在业务逻辑方法被调用的同时，自动调用会执行处理器。</span></font><span style="color: black"><br />
<font size="2">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </font></span><font size="2"><span style="color: black">下面让我们来创建一个日志拦截器类</span><span style="color: black; font-size: 10pt">LogInterceptor.java</span><span style="color: black; font-size: 10pt">文件，该类实现</span><span style="color: black">java.lang.reflect.InvocationHandler</span></font><span style="color: black"><font size="2">接口，其内容如下所示：<br />
<br />
</font>
<div style="width: 669px; height: 945px; scrollbar-highlight-color: buttonhighlight; overflow: auto">
<pre style="border-bottom: black 1px solid; border-left: black 1px solid; padding-bottom: 4px; background-color: #ededed; padding-left: 4px; padding-right: 4px; border-top: black 1px solid; border-right: black 1px solid; padding-top: 4px">
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><font size="2"><span style="color: #000000">package org.amigo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 日志拦截器，用来进行日志处理.
* @author &lt;a href="mailto:xiexingxing1121@126.com"&gt;AmigoXie&lt;/a&gt;
* Creation date: 2007-10-7 - 上午09:31:44
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
</span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span></font><font size="2"><span style="color: #000000"> LogInterceptor implements InvocationHandler {
</span><span style="color: #0000ff">private</span><span style="color: #000000"> Object </span><span style="color: #0000ff">delegate</span></font><font size="2"><span style="color: #000000">;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 构造函数,设置代理对象.
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">     </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> LogInterceptor(Object </span><span style="color: #0000ff">delegate</span></font><font size="2"><span style="color: #000000">){
</span><span style="color: #0000ff">this</span><span style="color: #000000">.</span><span style="color: #0000ff">delegate</span><span style="color: #000000"> </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">delegate</span></font><font size="2"><span style="color: #000000">;
}
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 方法的调用.
* @param proxy
* @param method 对应的方法
* @param args 方法的参信息
* @return 返回操作结果对象
* @throws Throwable
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><span style="color: #0000ff"><font size="2">public</font></span><font size="2"><span style="color: #000000"> Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span></font><font size="2"><span style="color: #000000">;
</span><span style="color: #0000ff">try</span></font><font size="2"><span style="color: #000000"> {
System.</span><span style="color: #0000ff">out</span><span style="color: #000000">.println(</span><span style="color: #000000">"</span><span style="color: #000000">before process</span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span></font><font size="2"><span style="color: #000000"> method);
</span><span style="color: #008000">//</span><span style="color: #008000">调用代理对象delegate的method方法，并将args作为参数信息传入</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">            result </span><span style="color: #000000">=</span><span style="color: #000000"> method.invoke(</span><span style="color: #0000ff">delegate</span></font><font size="2"><span style="color: #000000">, args);
System.</span><span style="color: #0000ff">out</span><span style="color: #000000">.println(</span><span style="color: #000000">"</span><span style="color: #000000">after process</span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span></font><font size="2"><span style="color: #000000"> method);
} </span><span style="color: #0000ff">catch</span></font><font size="2"><span style="color: #000000"> (Exception e){
System.err.println(</span><span style="color: #000000">"</span><span style="color: #000000">发生异常:</span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span></font><font size="2"><span style="color: #000000"> e.toString());
}
</span><span style="color: #0000ff">return</span></font><font size="2"><span style="color: #000000"> result;
}
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 测试方法.
* @param args
* @throws Exception
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> main(String[] args) throws Exception {
BusinessObj obj </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span></font><font size="2"><span style="color: #000000"> BusinessObjImpl();
</span><span style="color: #008000">//</span><span style="color: #008000">创建一个日志拦截器</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">        LogInterceptor interceptor </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span></font><font size="2"><span style="color: #000000"> LogInterceptor(obj);
</span><span style="color: #008000">//</span><span style="color: #008000">通过Proxy类的newProxyInstance(...)方法来获得动态的代理对象</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">        BusinessObj proxy </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> (BusinessObj) Proxy.newProxyInstance(
BusinessObjImpl.</span><span style="color: #0000ff">class</span></font><font size="2"><span style="color: #000000">.getClassLoader(),
BusinessObjImpl.</span><span style="color: #0000ff">class</span></font><font size="2"><span style="color: #000000">.getInterfaces(),
interceptor);
</span><span style="color: #008000">//</span><span style="color: #008000">执行动态代理对象的业务逻辑方法</span></font><span style="color: #008000">
</span><span style="color: #000000"><font size="2">        proxy.process();
}
}</font></span></div>
</pre>
</div>
<div align="left"><font size="2"></font>&nbsp;</div>
<div align="left"><font size="2">此时还需要对BusinessObj的实现类BusinessObjImpl类进行修改，去掉其日志记录等内容，修改后的文件如下：<br />
<br />
</div>
</font>
<div align="left">
<div style="width: 652px; height: 274px; scrollbar-highlight-color: buttonhighlight; overflow: auto">
<pre style="border-bottom: black 1px solid; border-left: black 1px solid; padding-bottom: 4px; background-color: #ededed; padding-left: 4px; padding-right: 4px; border-top: black 1px solid; border-right: black 1px solid; padding-top: 4px">
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><font size="2"><span style="color: #000000">package org.amigo.proxy;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 业务逻辑对象实现类.
* @author &lt;a href="mailto:xiexingxing1121@126.com"&gt;AmigoXie&lt;/a&gt;
* Creation date: 2007-10-7 - 上午09:11:49
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
</span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span></font><font size="2"><span style="color: #000000"> BusinessObjImpl implements BusinessObj {
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 执行业务.
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> process() {
System.</span><span style="color: #0000ff">out</span><span style="color: #000000">.println(</span><span style="color: #000000">"</span><span style="color: #000000">执行业务逻辑</span><span style="color: #000000">"</span></font><span style="color: #000000"><font size="2">);
}
}</font></span></div>
</pre>
</div>
</div>
<div align="left"><font size="2">运行LogInterceptor类我们可以发现，它实现了前面所需要的功能，但是很好的将业务逻辑方法的代码和日志记录的代码分离开来，并且所有的业务处理对象都可以利用该类来完成日志的记录，防止了重复代码的出现，增加了程序的可扩展性和可维护性，从而提高了代码的质量。那么Spring中的AOP的实现是怎么样的呢？接着让我们来对Spring的AOP进行探讨，了解其内部实现原理。</font></div>
</span></div>
<div align="left"><font size="2"><strong>三．Spring</strong><strong>中AOP</strong><strong>的模拟实现</strong></font></div>
<div align="left"><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>在学习了Java的动态代理机制后，我们在本节中将学习Java的动态代理机制在Spring中的应用。首先我们创建一个名为AopHandler的类，该类可生成代理对象，同时可以根据设置的前置或后置处理对象分别在方法执行前后执行一些另外的操作，该类的内容如下所</font><span style="color: black; font-size: 10pt">示：<br />
<br />
</span></div>
<div style="width: 653px; height: 1153px; scrollbar-highlight-color: buttonhighlight; overflow: auto">
<pre style="border-bottom: black 1px solid; border-left: black 1px solid; padding-bottom: 4px; background-color: #ededed; padding-left: 4px; padding-right: 4px; border-top: black 1px solid; border-right: black 1px solid; padding-top: 4px">
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><font size="2"><span style="color: #000000">package org.amigo.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* AOP处理器.
* @author &lt;a href="mailto:xiexingxing1121@126.com"&gt;AmigoXie&lt;/a&gt;
* Creation date: 2007-10-7 - 上午10:13:28
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
</span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span></font><font size="2"><span style="color: #000000"> AopHandler implements InvocationHandler {
</span><span style="color: #008000">//</span><span style="color: #008000">需要代理的目标对象  </span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">    </span><span style="color: #0000ff">private</span></font><font size="2"><span style="color: #000000"> Object target;
</span><span style="color: #008000">//</span><span style="color: #008000">方法前置顾问  </span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">    Advisor beforeAdvisor;
</span><span style="color: #008000">//</span><span style="color: #008000">方法后置顾问</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">    Advisor afterAdvisor;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 设置代理目标对象，并生成动态代理对象.
* @param target 代理目标对象
* @return 返回动态代理对象
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><span style="color: #0000ff"><font size="2">public</font></span><font size="2"><span style="color: #000000"> Object setObject(Object target) {
</span><span style="color: #008000">//</span><span style="color: #008000">设置代理目标对象</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">        </span><span style="color: #0000ff">this</span><span style="color: #000000">.target </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> target;
</span><span style="color: #008000">//</span><span style="color: #008000">根据代理目标对象生成动态代理对象</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">        Object obj </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), </span><span style="color: #0000ff">this</span></font><font size="2"><span style="color: #000000">);
</span><span style="color: #0000ff">return</span></font><font size="2"><span style="color: #000000"> obj;
}
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 若定义了前置处理，则在方法执行前执行前置处理，
* 若定义了后置处理，则在方法调用后调用后置处理.
* @param proxy 代理对象
* @param method 调用的业务方法
* @param args 方法的参数
* @return 返回结果信息
* @throws Throwable
</span><span style="color: #008000">*/</span></font><font size="2"><span style="color: #000000">
</span><span style="color: #0000ff">public</span></font><font size="2"><span style="color: #000000"> Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
</span><span style="color: #008000">//</span><span style="color: #008000">进行业务方法的前置处理</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">        </span><span style="color: #0000ff">if</span><span style="color: #000000"> (beforeAdvisor </span><span style="color: #000000">!=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span></font><font size="2"><span style="color: #000000">) {
beforeAdvisor.doInAdvisor(proxy, method, args);
}
</span><span style="color: #008000">//</span><span style="color: #008000">执行业务方法</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">        Object result </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> method.invoke(target, args);
</span><span style="color: #008000">//</span><span style="color: #008000">进行业务方法的后置处理</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">        </span><span style="color: #0000ff">if</span><span style="color: #000000"> (afterAdvisor </span><span style="color: #000000">!=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span></font><font size="2"><span style="color: #000000">) {
afterAdvisor.doInAdvisor(proxy, method, args);
}
</span><span style="color: #008000">//</span><span style="color: #008000">返回结果对象</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">        </span><span style="color: #0000ff">return</span></font><font size="2"><span style="color: #000000"> result;
}
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 设置方法的前置顾问.
* @param advisor 方法的前置顾问
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> setBeforeAdvisor(Advisor advisor) {
</span><span style="color: #0000ff">this</span><span style="color: #000000">.beforeAdvisor </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> advisor;
}
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 设置方法的后置顾问.
* @param advisor 方法的后置顾问
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> setAfterAdvisor(Advisor advisor) {
</span><span style="color: #0000ff">this</span><span style="color: #000000">.afterAdvisor </span><span style="color: #000000">=</span></font><span style="color: #000000"><font size="2"> advisor;
}
}</font></span></div>
</pre>
</div>
<div align="left"><font size="2">&nbsp;&nbsp;&nbsp; 在上类中，前置和后置顾问对象都继承Advisor接口，接下来让我们来看看顾问接口类的内容，该类定义了doInAdvisor(<span style="color: black; font-size: 10pt">Object proxy, Method method, Object[] <span style="background: silver">args</span></span>)方法，如下所示：<br />
</font>
<div style="width: 645px; height: 834px; scrollbar-highlight-color: buttonhighlight; overflow: auto">
<pre style="border-bottom: black 1px solid; border-left: black 1px solid; padding-bottom: 4px; background-color: #ededed; padding-left: 4px; padding-right: 4px; border-top: black 1px solid; border-right: black 1px solid; padding-top: 4px">
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><font size="2"><span style="color: #000000">package org.amigo.proxy;
import java.lang.reflect.Method;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
*
* 顾问接口类.
* @author &lt;a href="mailto:xiexingxing1121@126.com"&gt;AmigoXie&lt;/a&gt;
* Creation date: 2007-10-7 - 上午10:21:18
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
</span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">interface</span></font><font size="2"><span style="color: #000000"> Advisor {
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 所做的操作.
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> doInAdvisor(Object proxy, Method method, Object[] args);
}
BeforeMethodAdvisor和AfterMethodAdvisor都实现了Advisor接口，分别为方法的前置顾问和后置顾问类。
BeforeMethodAdvisor.java文件（前置顾问类）的内容如下所示：
package org.amigo.proxy;
import java.lang.reflect.Method;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
*
* 方法前置顾问，它完成方法的前置操作.
* @author &lt;a href="mailto:xiexingxing1121@126.com"&gt;AmigoXie&lt;/a&gt;
* Creation date: 2007-10-7 - 上午10:19:57
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
</span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span></font><font size="2"><span style="color: #000000"> BeforeMethodAdvisor implements Advisor {
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 在方法执行前所进行的操作.
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> doInAdvisor(Object proxy, Method method, Object[] args) {
System.</span><span style="color: #0000ff">out</span><span style="color: #000000">.println(</span><span style="color: #000000">"</span><span style="color: #000000">before process </span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span></font><font size="2"><span style="color: #000000"> method);
}
}
AfterMethodAdvisor.java文件（后置顾问类）的内容如下所示：
package org.amigo.proxy;
import java.lang.reflect.Method;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
*
* 方法的后置顾问，它完成方法的后置操作.
* @author &lt;a href="mailto:xiexingxing1121@126.com"&gt;AmigoXie&lt;/a&gt;
* Creation date: 2007-10-7 - 上午10:20:43
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
</span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span></font><font size="2"><span style="color: #000000"> AfterMethodAdvisor implements Advisor {
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 在方法执行后所进行的操作.
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> doInAdvisor(Object proxy, Method method, Object[] args) {
System.</span><span style="color: #0000ff">out</span><span style="color: #000000">.println(</span><span style="color: #000000">"</span><span style="color: #000000">after process </span><span style="color: #000000">"</span><span style="color: #000000"> </span><span style="color: #000000">+</span></font><span style="color: #000000"><font size="2"> method);
}
}     </font></span></div>
</pre>
</div>
<div align="left"><font size="2">这两个类分别在方法执行前和方法执行后做一些额外的操作。</font></div>
<div align="left"><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>对于在配置文件中对某个bean配置前置或后置处理器，我们可以在bean中增加两个属性aop和aopType，aop的值为对应的前置顾问类或后置顾问类的名称，aopType用于指明该顾问类为前置还是后置顾问，为before时表示为前置处理器，为after时表示为后置处理器，这时候我们需要修改上一篇文章中的BeanFactory.java这个文件，添加其对aop和aopType的处理，修改后的该文件内容如下：<br />
<br />
</font>
<div style="width: 663px; height: 1842px; scrollbar-highlight-color: buttonhighlight; overflow: auto">
<pre style="border-bottom: black 1px solid; border-left: black 1px solid; padding-bottom: 4px; background-color: #ededed; padding-left: 4px; padding-right: 4px; border-top: black 1px solid; border-right: black 1px solid; padding-top: 4px">
<div><!-- Code highlighting produced by Actipro CodeHighlighter (freeware)
http://www.CodeHighlighter.com/
--><font size="2"><span style="color: #000000">package org.amigo.proxy;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* bean工厂类.
* @author &lt;a href="mailto:xiexingxing1121@126.com"&gt;AmigoXie&lt;/a&gt;
* Creation date: 2007-10-7 - 下午04:04:34
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
</span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">class</span></font><font size="2"><span style="color: #000000"> BeanFactory {
</span><span style="color: #0000ff">private</span><span style="color: #000000"> Map</span><span style="color: #000000">&lt;</span><span style="color: #000000">String, Object</span><span style="color: #000000">&gt;</span><span style="color: #000000"> beanMap </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span><span style="color: #000000"> HashMap</span><span style="color: #000000">&lt;</span><span style="color: #000000">String, Object</span><span style="color: #000000">&gt;</span></font><font size="2"><span style="color: #000000">();
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* bean工厂的初始化.
* @param xml xml配置文件
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> init(String xml) {
</span><span style="color: #0000ff">try</span></font><font size="2"><span style="color: #000000"> {
</span><span style="color: #008000">//</span><span style="color: #008000">读取指定的配置文件</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">            SAXReader reader </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span></font><font size="2"><span style="color: #000000"> SAXReader();
ClassLoader classLoader </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> Thread.currentThread().getContextClassLoader();
InputStream ins </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> classLoader.getResourceAsStream(xml);
Document doc </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> reader.read(ins);
Element root </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> doc.getRootElement();
Element foo;
</span><span style="color: #008000">//</span><span style="color: #008000">创建AOP处理器</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">            AopHandler aopHandler </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span></font><font size="2"><span style="color: #000000"> AopHandler();
</span><span style="color: #008000">//</span><span style="color: #008000">遍历bean</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">            </span><span style="color: #0000ff">for</span><span style="color: #000000"> (Iterator i </span><span style="color: #000000">=</span><span style="color: #000000"> root.elementIterator(</span><span style="color: #000000">"</span><span style="color: #000000">bean</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">); i.hasNext();) {
foo </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> (Element) i.next();
</span><span style="color: #008000">//</span><span style="color: #008000">获取bean的属性id、class、aop以及aopType</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                Attribute id </span><span style="color: #000000">=</span><span style="color: #000000"> foo.attribute(</span><span style="color: #000000">"</span><span style="color: #000000">id</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">);
Attribute cls </span><span style="color: #000000">=</span><span style="color: #000000"> foo.attribute(</span><span style="color: #000000">"</span><span style="color: #000000">class</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">);
Attribute aop </span><span style="color: #000000">=</span><span style="color: #000000"> foo.attribute(</span><span style="color: #000000">"</span><span style="color: #000000">aop</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">);
Attribute aopType </span><span style="color: #000000">=</span><span style="color: #000000"> foo.attribute(</span><span style="color: #000000">"</span><span style="color: #000000">aopType</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">);
</span><span style="color: #008000">//</span><span style="color: #008000">配置了aop和aopType属性时，需进行拦截操作</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                </span><span style="color: #0000ff">if</span><span style="color: #000000"> (aop </span><span style="color: #000000">!=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span><span style="color: #000000"> </span><span style="color: #000000">&amp;&amp;</span><span style="color: #000000"> aopType </span><span style="color: #000000">!=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span></font><font size="2"><span style="color: #000000">) {
</span><span style="color: #008000">//</span><span style="color: #008000">根据aop字符串获取对应的类</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                    Class advisorCls </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> Class.forName(aop.getText());
</span><span style="color: #008000">//</span><span style="color: #008000">创建该类的对象</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                    Advisor advisor </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> (Advisor) advisorCls.newInstance();
</span><span style="color: #008000">//</span><span style="color: #008000">根据aopType的类型来设置前置或后置顾问</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                    </span><span style="color: #0000ff">if</span><span style="color: #000000"> (</span><span style="color: #000000">"</span><span style="color: #000000">before</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">.equals(aopType.getText())) {
aopHandler.setBeforeAdvisor(advisor);
} </span><span style="color: #0000ff">else</span><span style="color: #000000"> </span><span style="color: #0000ff">if</span><span style="color: #000000"> (</span><span style="color: #000000">"</span><span style="color: #000000">after</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">.equals(aopType.getText())) {
aopHandler.setAfterAdvisor(advisor);
}
}
</span><span style="color: #008000">//</span><span style="color: #008000">利用Java反射机制，通过class的名称获取Class对象</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                Class bean </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> Class.forName(cls.getText());
</span><span style="color: #008000">//</span><span style="color: #008000">获取对应class的信息</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                java.beans.BeanInfo info </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> java.beans.Introspector.getBeanInfo(bean);
</span><span style="color: #008000">//</span><span style="color: #008000">获取其属性描述</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                java.beans.PropertyDescriptor pd[] </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> info.getPropertyDescriptors();
</span><span style="color: #008000">//</span><span style="color: #008000">设置值的方法</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                Method mSet </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span></font><font size="2"><span style="color: #000000">;
</span><span style="color: #008000">//</span><span style="color: #008000">创建一个对象</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                Object obj </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> bean.newInstance();
</span><span style="color: #008000">//</span><span style="color: #008000">遍历该bean的property属性</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                </span><span style="color: #0000ff">for</span><span style="color: #000000"> (Iterator ite </span><span style="color: #000000">=</span><span style="color: #000000"> foo.elementIterator(</span><span style="color: #000000">"</span><span style="color: #000000">property</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">); ite.hasNext();) {
Element foo2 </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> (Element) ite.next();
</span><span style="color: #008000">//</span><span style="color: #008000">获取该property的name属性</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                    Attribute name </span><span style="color: #000000">=</span><span style="color: #000000"> foo2.attribute(</span><span style="color: #000000">"</span><span style="color: #000000">name</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">);
String value </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">null</span></font><font size="2"><span style="color: #000000">;
</span><span style="color: #008000">//</span><span style="color: #008000">获取该property的子元素value的值</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                    </span><span style="color: #0000ff">for</span><span style="color: #000000">(Iterator ite1 </span><span style="color: #000000">=</span><span style="color: #000000"> foo2.elementIterator(</span><span style="color: #000000">"</span><span style="color: #000000">value</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">); ite1.hasNext();) {
Element node </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> (Element) ite1.next();
value </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> node.getText();
</span><span style="color: #0000ff">break</span></font><font size="2"><span style="color: #000000">;
}
</span><span style="color: #0000ff">for</span><span style="color: #000000"> (</span><span style="color: #0000ff">int</span><span style="color: #000000"> k </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #000000">0</span><span style="color: #000000">; k </span><span style="color: #000000">&lt;</span><span style="color: #000000"> pd.length; k</span><span style="color: #000000">++</span></font><font size="2"><span style="color: #000000">) {
</span><span style="color: #0000ff">if</span></font><font size="2"><span style="color: #000000"> (pd[k].getName().equalsIgnoreCase(name.getText())) {
mSet </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> pd[k].getWriteMethod();
</span><span style="color: #008000">//</span><span style="color: #008000">利用Java的反射极致调用对象的某个set方法，并将值设置进去</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                            mSet.invoke(obj, value);
}
}
}
</span><span style="color: #008000">//</span><span style="color: #008000">为对象增加前置或后置顾问</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                obj </span><span style="color: #000000">=</span></font><font size="2"><span style="color: #000000"> (Object) aopHandler.setObject(obj);
</span><span style="color: #008000">//</span><span style="color: #008000">将对象放入beanMap中，其中key为id值，value为对象</span></font><span style="color: #008000">
</span><font size="2"><span style="color: #000000">                beanMap.put(id.getText(), obj);
}
} </span><span style="color: #0000ff">catch</span></font><font size="2"><span style="color: #000000"> (Exception e) {
System.</span><span style="color: #0000ff">out</span></font><font size="2"><span style="color: #000000">.println(e.toString());
}
}
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 通过bean的id获取bean的对象.
* @param beanName bean的id
* @return 返回对应对象
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><span style="color: #0000ff"><font size="2">public</font></span><font size="2"><span style="color: #000000"> Object getBean(String beanName) {
Object obj </span><span style="color: #000000">=</span><span style="color: #000000"> beanMap.</span><span style="color: #0000ff">get</span></font><font size="2"><span style="color: #000000">(beanName);
</span><span style="color: #0000ff">return</span></font><font size="2"><span style="color: #000000"> obj;
}
</span><span style="color: #008000">/*</span></font><font size="2"><span style="color: #008000">*
* 测试方法.
* @param args
</span><span style="color: #008000">*/</span></font><span style="color: #000000">
<font size="2">    </font></span><font size="2"><span style="color: #0000ff">public</span><span style="color: #000000"> </span><span style="color: #0000ff">static</span><span style="color: #000000"> </span><span style="color: #0000ff">void</span></font><font size="2"><span style="color: #000000"> main(String[] args) {
BeanFactory factory </span><span style="color: #000000">=</span><span style="color: #000000"> </span><span style="color: #0000ff">new</span></font><font size="2"><span style="color: #000000"> BeanFactory();
factory.init(</span><span style="color: #000000">"</span><span style="color: #000000">config.xml</span><span style="color: #000000">"</span></font><font size="2"><span style="color: #000000">);
BusinessObj obj </span><span style="color: #000000">=</span><span style="color: #000000"> (BusinessObj) factory.getBean(</span><span style="color: #000000">"</span><span style="color: #000000">businessObj</span><span style="color: #000000">"</span></font><span style="color: #000000"><font size="2">);
obj.process();
}
}</font></span></div>
</pre>
</div>
<div align="left"><font size="2">&nbsp;&nbsp;&nbsp; 观察此类我们可以发现，该类添加了对bean元素的aop和aopType属性的处理。编写完该文件后，我们还需要修改src目录下的配置文件：config.xml文件，在该文件中添加名为businessObj的bean，我们为其配置了aop和aopType属性，增加了方法的前置顾问。增加的部分为：<br />
<br />
</font>
<div align="left"><span style="color: teal; font-size: 10pt">&lt;</span><span style="color: #3f7f7f; font-size: 10pt">bean </span><span style="color: #7f007f; font-size: 10pt">id</span><span style="color: black; font-size: 10pt">=</span><span style="color: #2a00ff; font-size: 10pt">"businessObj" </span><span style="color: #7f007f; font-size: 10pt">class</span><span style="color: black; font-size: 10pt">=</span><span style="color: #2a00ff; font-size: 10pt">"org.amigo.proxy.BusinessObjImpl" </span><span style="color: #7f007f; font-size: 10pt">aop</span><span style="color: black; font-size: 10pt">=</span><span style="color: #2a00ff; font-size: 10pt">"org.amigo.proxy.BeforeMethodAdvisor" </span><span style="color: #7f007f; font-size: 10pt">aopType</span><span style="color: black; font-size: 10pt">=</span><span style="color: #2a00ff; font-size: 10pt">"before"</span><span style="color: teal; font-size: 10pt">/&gt;</span></div>
<div align="left"><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>此时运行BeanFactory.java这个类文件，运行结果如下：</font></div>
<div align="left"><font size="2">before process public abstract void org.amigo.proxy.BusinessObj.process()</font></div>
<div align="left"><font size="2">执行业务逻辑</font></div>
<div align="left"><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>由运行结果可以看出，前置处理已经生效。</font></div>
<div align="left"><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>本节中的例子只是实现了Spring的AOP一小部分功能，即为某个bean添加前置或后置处理，在Spring中，考虑的比这多很多，例如，为多个bean配置动态代理等等。但是究其根源，Spring中AOP的实现，是基于Java中无比强大的反射和动态代理机制。</font></div>
<div align="left"><font size="2"></font>&nbsp;</div>
<div align="left"><strong><font size="2">四．总结</font></strong></div>
<div align="left"><font size="2"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span>本文讲述了AOP的概念等信息，并详细讲解了Java的动态代理机制，接着又通过一个简单的实例讲解了Spring中AOP的模拟实现，使得读者能够更好地学习Java的反射和代理机制。</font></div>
<div style="text-indent: 21pt" align="left"><font size="2">通过这两篇文章，使得我们能够更加深入地理解Java的反射和动态代理机制，同时对Spring中盛行的IOC和AOP的后台实现原理有了更加清晰的理解，Java的反射和动态代理机制的强大功能在这两篇文章中可见一斑。有兴趣的朋友可以通过学习Spring框架的源码来进一步的理解Java的反射和动态代理机制，从而在实际的开发工作中更好地理解</font></div>
</div>
</div>
</div>
</div>
 <img src ="http://www.blogjava.net/lanxin1020/aggbug/264075.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-06 11:50 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/06/264075.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用java的反射实现IOC(转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/06/264072.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Mon, 06 Apr 2009 03:43:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/06/264072.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/264072.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/06/264072.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/264072.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/264072.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: spring IOC 机制模拟实现收藏&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 在Java中，其反射和动态代理机制极其强大，我们可以通过其反射机制在运行时获取信息。而代理是一种基本的设计模式，它是一种为了提供额外的或不同的操作而插入到真实对象中的某个对象。而Java的动态代理在代理上更进一步，既能动态的创建代理对象，又...&nbsp;&nbsp;<a href='http://www.blogjava.net/lanxin1020/archive/2009/04/06/264072.html'>阅读全文</a><img src ="http://www.blogjava.net/lanxin1020/aggbug/264072.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-06 11:43 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/06/264072.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>工厂模式 与 ioc(转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/06/264043.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Sun, 05 Apr 2009 16:13:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/06/264043.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/264043.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/06/264043.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/264043.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/264043.html</trackback:ping><description><![CDATA[<p>Spring的模块化是很强的，各个功能模块都是独立的，我们可以选择的使用。这一章先从Spring的<span class="hilite2">IoC</span>开始。所谓<span class="hilite2">IoC</span>就是一个用XML来定义生成对象的模式，我们看看如果来使用的。<br />
<br />
　　 <strong>数据模型</strong><br />
<br />
　　 1、如下图所示有三个类，Human（人类）是接口，Chinese（中国人）是一个子类，American（美国人）是另外一个子类。</p>
<p>源代码如下：</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar">
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">package</span><span>&nbsp;cn.com.chengang.spring; &nbsp;&nbsp;</span></span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">interface</span><span>&nbsp;Human&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">void</span><span>&nbsp;eat(); &nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">void</span><span>&nbsp;walk(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">package</span><span>&nbsp;cn.com.chengang.spring; &nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Chinese&nbsp;</span><span class="keyword">implements</span><span>&nbsp;Human&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="comment">/*&nbsp;（非&nbsp;Javadoc） </span>&nbsp;</li>
    <li><span><span class="comment">*&nbsp;@see&nbsp;cn.com.chengang.spring.Human#eat() </span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;eat()&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>System.out.println(</span><span class="string">"中国人对吃很有一套"</span><span>); &nbsp;&nbsp;</span></li>
    <li><span>} &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li><span><span class="comment">/*&nbsp;（非&nbsp;Javadoc） </span>&nbsp;</li>
    <li class="alt"><span><span class="comment">*&nbsp;@see&nbsp;cn.com.chengang.spring.Human#walk() </span>&nbsp;</span></li>
    <li><span><span class="comment">*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;walk()&nbsp;{ &nbsp;&nbsp;</span></li>
    <li><span>System.out.println(</span><span class="string">"中国人行如飞"</span><span>); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>} &nbsp;&nbsp;</span></li>
    <li><span>} &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">package</span><span>&nbsp;cn.com.chengang.spring; &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;American&nbsp;</span><span class="keyword">implements</span><span>&nbsp;Human&nbsp;{ &nbsp;&nbsp;</span></li>
    <li><span><span class="comment">/*&nbsp;（非&nbsp;Javadoc） </span>&nbsp;</li>
    <li class="alt"><span><span class="comment">*&nbsp;@see&nbsp;cn.com.chengang.spring.Human#eat() </span>&nbsp;</span></li>
    <li><span><span class="comment">*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;eat()&nbsp;{ &nbsp;&nbsp;</span></li>
    <li><span>System.out.println(</span><span class="string">"美国人主要以面包为主"</span><span>); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>} &nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="comment">/*&nbsp;（非&nbsp;Javadoc） </span>&nbsp;</li>
    <li><span><span class="comment">*&nbsp;@see&nbsp;cn.com.chengang.spring.Human#walk() </span>&nbsp;</span></li>
    <li class="alt"><span><span class="comment">*/</span><span>&nbsp;&nbsp;</span></span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;walk()&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>System.out.println(</span><span class="string">"美国人以车代步，有四肢退化的趋势"</span><span>); &nbsp;&nbsp;</span></li>
    <li><span>} &nbsp;&nbsp;</span></li>
    <li class="alt"><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>&nbsp;</p>
<p>2、对以上对象采用工厂模式的用法如下<br />
<br />
　　 创建一个工厂类Factory，如下。这个工厂类里定义了两个字符串常量，所标识不同的人种。getHuman方法根据传入参数的字串，来判断要生成什么样的人种。<br />
</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar">
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">package</span><span>&nbsp;cn.com.chengang.spring; &nbsp;&nbsp;</span></span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;Factory&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;String&nbsp;CHINESE&nbsp;=&nbsp;</span><span class="string">"Chinese"</span><span>; &nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;String&nbsp;AMERICAN&nbsp;=&nbsp;</span><span class="string">"American"</span><span>; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;Human&nbsp;getHuman(String&nbsp;ethnic)&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">if</span><span>&nbsp;(ethnic.equals(CHINESE)) &nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;Chinese(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">else</span><span>&nbsp;</span><span class="keyword">if</span><span>&nbsp;(ethnic.equals(AMERICAN)) &nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">return</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;American(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">else</span><span>&nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">throw</span><span>&nbsp;</span><span class="keyword">new</span><span>&nbsp;IllegalArgumentException(</span><span class="string">"参数(人种)错误"</span><span>); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>} &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>&nbsp;</p>
<p>下面是一个测试的程序，使用工厂方法来得到了不同的&#8220;人种对象&#8221;，并执行相应的方法。<br />
</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar">
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">package</span><span>&nbsp;cn.com.chengang.spring; &nbsp;&nbsp;</span></span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;ClientTest&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{ &nbsp;&nbsp;</span></li>
    <li><span>Human&nbsp;human&nbsp;=&nbsp;</span><span class="keyword">null</span><span>; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>human&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;Factory().getHuman(Factory.CHINESE); &nbsp;&nbsp;</span></li>
    <li><span>human.eat(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>human.walk(); &nbsp;&nbsp;</span></li>
    <li><span>human&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;Factory().getHuman(Factory.AMERICAN); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>human.eat(); &nbsp;&nbsp;</span></li>
    <li><span>human.walk(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>} &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>&nbsp;</p>
<p>&nbsp;3、采用Spring的IoC的用法如下：<br />
<br />
　　 在项目根目录下创建一个bean.xml文件</p>
<div class="code_title">xml 代码</div>
<div class="dp-highlighter">
<div class="bar">
<ol class="dp-xml">
    <li class="alt"><span><span>＜?xml&nbsp;</span><span class="attribute">version</span><span>=</span><span class="attribute-value">"1.0"</span><span>&nbsp;</span><span class="attribute">encoding</span><span>=</span><span class="attribute-value">"UTF-8"</span><span>?＞ &nbsp;&nbsp;</span></span></li>
    <li><span>＜!DOCTYPE&nbsp;beans&nbsp;PUBLIC&nbsp;"-//SPRING//DTD&nbsp;BEAN//EN"&nbsp;"http://www.springframework.org/dtd/spring-beans.dtd"＞ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>＜beans＞ &nbsp;&nbsp;</span></li>
    <li><span>＜bean&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">"Chinese"</span><span>&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">"cn.com.chengang.spring.Chinese"</span><span>/＞ &nbsp;&nbsp;</span></li>
    <li class="alt"><span>＜bean&nbsp;</span><span class="attribute">id</span><span>=</span><span class="attribute-value">"American"</span><span>&nbsp;</span><span class="attribute">class</span><span>=</span><span class="attribute-value">"cn.com.chengang.spring.American"</span><span>/＞ &nbsp;&nbsp;</span></li>
    <li><span>＜/beans＞&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>修改ClientTest程序如下：<br />
</p>
<div class="code_title">java 代码</div>
<div class="dp-highlighter">
<div class="bar">
<ol class="dp-j">
    <li class="alt"><span><span class="keyword">package</span><span>&nbsp;cn.com.chengang.spring; &nbsp;&nbsp;</span></span></li>
    <li><span><span class="keyword">import</span><span>&nbsp;org.springframework.context.ApplicationContext; &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">import</span><span>&nbsp;org.springframework.context.support.FileSystemXmlApplicationContext; &nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">class</span><span>&nbsp;ClientTest&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;String&nbsp;CHINESE&nbsp;=&nbsp;</span><span class="string">"Chinese"</span><span>; &nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">final</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;String&nbsp;AMERICAN&nbsp;=&nbsp;</span><span class="string">"American"</span><span>; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>&nbsp;&nbsp;</span></li>
    <li><span><span class="keyword">public</span><span>&nbsp;</span><span class="keyword">static</span><span>&nbsp;</span><span class="keyword">void</span><span>&nbsp;main(String[]&nbsp;args)&nbsp;{ &nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="comment">//&nbsp;Human&nbsp;human&nbsp;=&nbsp;null; </span><span>&nbsp;&nbsp;</span></li>
    <li><span><span class="comment">//&nbsp;human&nbsp;=&nbsp;new&nbsp;Factory().getHuman(Factory.CHINESE); </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="comment">//&nbsp;human.eat(); </span><span>&nbsp;&nbsp;</span></li>
    <li><span><span class="comment">//&nbsp;human.walk(); </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="comment">//&nbsp;human&nbsp;=&nbsp;new&nbsp;Factory().getHuman(Factory.AMERICAN); </span><span>&nbsp;&nbsp;</span></li>
    <li><span><span class="comment">//&nbsp;human.eat(); </span><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span><span class="comment">//&nbsp;human.walk(); </span><span>&nbsp;&nbsp;</span></li>
    <li><span>&nbsp;&nbsp;</span></li>
    <li class="alt"><span>ApplicationContext&nbsp;ctx&nbsp;=&nbsp;</span><span class="keyword">new</span><span>&nbsp;FileSystemXmlApplicationContext(</span><span class="string">"bean.xml"</span><span>); &nbsp;&nbsp;</span></li>
    <li><span>Human&nbsp;human&nbsp;=&nbsp;</span><span class="keyword">null</span><span>; &nbsp;&nbsp;</span></li>
    <li class="alt"><span>human&nbsp;=&nbsp;(Human)&nbsp;ctx.getBean(CHINESE); &nbsp;&nbsp;</span></li>
    <li><span>human.eat(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>human.walk(); &nbsp;&nbsp;</span></li>
    <li><span>human&nbsp;=&nbsp;(Human)&nbsp;ctx.getBean(AMERICAN); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>human.eat(); &nbsp;&nbsp;</span></li>
    <li><span>human.walk(); &nbsp;&nbsp;</span></li>
    <li class="alt"><span>} &nbsp;&nbsp;</span></li>
    <li><span>}&nbsp;&nbsp;</span></li>
</ol>
</div>
<p>&nbsp;从这个程序可以看到，ctx就相当于原来的Factory工厂，原来的Factory就可以删除掉了。然后又把Factory里的两个常量移到了ClientTest类里，整个程序结构基本一样。<br />
<br />
　　 再回头看原来的bean.xml文件的这一句：<br />
<br />
</p>
<table border="1" width="90%" align="center">
    <tbody>
        <tr>
            <td>＜bean id="Chinese" class="cn.com.chengang.spring.Chinese"/＞</td>
        </tr>
    </tbody>
</table>
<br />
　　 id就是ctx.getBean的参数值，一个字符串。class就是一个类（包名＋类名）。然后在ClientTest类里获得Chinese对象就是这么一句<br />
<br />
<table border="1" width="90%" align="center">
    <tbody>
        <tr>
            <td>human = (Human) ctx.getBean(CHINESE);</td>
        </tr>
    </tbody>
</table>
<br />
　　 因为getBean方法返回的是Object类型，所以前面要加一个类型转换。<br />
<br />
　　 <strong>总结</strong><br />
<br />
　　 （1）也许有人说，IoC和工厂模式不是一样的作用吗，用IoC好象还麻烦一点。<br />
<br />
　　 举个例子，如果用户需求发生变化，要把Chinese类修改一下。那么前一种工厂模式，就要更改Factory类的方法，并且重新编译布署。而IoC只需要将class属性改变一下，并且由于IoC利用了Java反射机制，这些对象是动态生成的，这时我们就可以热插拨Chinese对象（不必把原程序停止下来重新编译布署）<br />
<br />
　　 （2）也许有人说，即然IoC这么好，那么我把系统所有对象都用IoC方式来生成。<br />
<br />
　　 注意，IoC的灵活性是有代价的：设置步骤麻烦、生成对象的方式不直观、反射比正常生成对象在效率上慢一点。因此使用IoC要看有没有必要，我认为比较通用的判断方式是：用到工厂模式的地方都可以考虑用IoC模式。<br />
<br />
　　 （3）在上面的IoC的方式里，还有一些可以变化的地方。比如，bean.xml不一定要放在项目录下，也可以放在其他地方，比如cn.com.chengang.spring包里。不过在使用时也要变化一下，如下所示：<br />
<br />
<table border="1" width="90%" align="center">
    <tbody>
        <tr>
            <td>new FileSystemXmlApplicationContext("src/cn/com/chengang/spring/bean.xml");</td>
        </tr>
    </tbody>
</table>
<br />
　　 另外，bean.xml也可以改成其他名字。这样我们在系统中就可以分门别类的设置不同的bean.xml。<br />
<br />
　　 （4）关于IoC的低侵入性。<br />
<br />
　　 什么是低侵入性？如果你用过Struts或EJB就会发现，要继承一些接口或类，才能利用它们的框架开发。这样，系统就被绑定在Struts、EJB上了，对系统的可移植性产生不利的影响。如果代码中很少涉及某一个框架的代码，那么这个框架就可以称做是一个低侵入性的框架。<br />
<br />
　　 Spring的侵入性很低，Humen.java、Chinese.java等几个类都不必继承什么接口或类。但在ClientTest里还是有一些Spring的影子：FileSystemXmlApplicationContext类和ctx.getBean方式等。<br />
现在，低侵入性似乎也成了判定一个框架的实现技术好坏的标准之一。<br />
<br />
　　 （5）关于bean.xml的用法<br />
<br />
　　 bean.xml的用法还有很多，其中内容是相当丰富的。假设Chinese类里有一个humenName属性（姓名），那么原的bean.xml修改如下。此后生成Chinese对象时，&#8220;陈刚&#8221;这个值将自动设置到Chinese类的humenName属性中。而且由于singleton为true这时生成Chinese对象将采用单例模式，系统仅存在一个Chinese对象实例。<br />
<br />
<table border="1" width="90%" align="center">
    <tbody>
        <tr>
            <td>＜?xml version="1.0" encoding="UTF-8"?＞<br />
            ＜!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"＞<br />
            ＜beans＞<br />
            ＜bean id="Chinese" class="cn.com.chengang.spring.Chinese" singleton="true"＞<br />
            ＜property name="humenName"＞<br />
            ＜value＞陈刚＜/value＞<br />
            ＜/property＞<br />
            ＜/bean＞<br />
            ＜bean id="American" class="cn.com.chengang.spring.American"/＞<br />
            ＜/beans＞</td>
        </tr>
    </tbody>
</table>
<br />
　　 关于bean.xml的其它用法，不再详细介绍了，大家自己拿Spring的文档一看就明白了。
<p>
<p>　Spring能有效地组织J2EE应用各层的对象。不管是控制层的Action对象，还是业务层的Service对象，还是持久层的DAO对象，都可在Spring的管理下有机地协调、运行。Spring将各层的对象以松耦合的方式组织在一起，Action对象无须关心Service对象的具体实现，Service对象无须关心持久层对象的具体实现，各层对象的调用完全面向接口。当系统需要重构时，代码的改写量将大大减少。<br />
<br />
　　上面所说的一切都得宜于Spring的核心机制，依赖注入。依赖注入让bean与bean之间以配置文件组织在一起，而不是以硬编码的方式耦合在一起。理解依赖注入<br />
<br />
　　依赖注入(Dependency Injection)和控制反转(Inversion of Control)是同一个概念。具体含义是:当某个角色(可能是一个Java实例，调用者)需要另一个角色(另一个Java实例，被调用者)的协助时，在传统的程序设计过程中，通常由调用者来创建被调用者的实例。但在Spring里，创建被调用者的工作不再由调用者来完成，因此称为控制反转;创建被调用者实例的工作通常由Spring容器来完成，然后注入调用者，因此也称为依赖注入。<br />
<br />
　　不管是依赖注入，还是控制反转，都说明Spring采用动态、灵活的方式来管理各种对象。对象与对象之间的具体实现互相透明。在理解依赖注入之前，看如下这个问题在各种社会形态里如何解决:一个人(Java实例，调用者)需要一把斧子(Java实例，被调用者)。<br />
<br />
　　(1)原始社会里，几乎没有社会分工。需要斧子的人(调用者)只能自己去磨一把斧子(被调用者)。对应的情形为:Java程序里的调用者自己创建被调用者。<br />
<br />
　　(2)进入工业社会，工厂出现。斧子不再由普通人完成，而在工厂里被生产出来，此时需要斧子的人(调用者)找到工厂，购买斧子，无须关心斧子的制造过程。对应Java程序的简单工厂的设计模式。<br />
<br />
　　(3)进入&#8220;按需分配&#8221;社会，需要斧子的人不需要找到工厂，坐在家里发出一个简单指令:需要斧子。斧子就自然出现在他面前。对应Spring的依赖注入。<br />
<br />
　　第一种情况下，Java实例的调用者创建被调用的Java实例，必然要求被调用的Java类出现在调用者的代码里。无法实现二者之间的松耦合。<br />
<br />
　　第二种情况下，调用者无须关心被调用者具体实现过程，只需要找到符合某种标准(接口)的实例，即可使用。此时调用的代码面向接口编程，可以让调用者和被调用者解耦，这也是工厂模式大量使用的原因。但调用者需要自己定位工厂，调用者与特定工厂耦合在一起。<br />
<br />
　　第三种情况下，调用者无须自己定位工厂，程序运行到需要被调用者时，系统自动提供被调用者实例。事实上，调用者和被调用者都处于Spring的管理下，二者之间的依赖关系由Spring提供。<br />
<br />
　　所谓依赖注入，是指程序运行过程中，如果需要调用另一个对象协助时，无须在代码中创建被调用者，而是依赖于外部的注入。Spring的依赖注入对调用者和被调用者几乎没有任何要求，完全支持对POJO之间依赖关系的管理。依赖注入通常有两种:<br />
<br />
　　&#183;设值注入。<br />
<br />
　　&#183;构造注入。<br />
.......</p>
</span></span></span></span></span></span></span></span></span></span></span></span></span></div>
</div>
</span></span></div>
</span></span></span></span></span></span></span></span></span></span></div>
</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></div>
 <img src ="http://www.blogjava.net/lanxin1020/aggbug/264043.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-06 00:13 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/06/264043.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>AOP 动态代理模式(转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/05/264034.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Sun, 05 Apr 2009 14:40:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/05/264034.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/264034.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/05/264034.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/264034.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/264034.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 最简单的解决方案我们先写一个接口IHello.java代码如下:&nbsp;1package&nbsp;sinosoft.dj.aop.staticaop;&nbsp;2&nbsp;3public&nbsp;interface&nbsp;IHello&nbsp;{&nbsp;4&nbsp;&nbsp;&nbsp;&nbsp;/**&nbsp;*//**&nbsp;5&nbs...&nbsp;&nbsp;<a href='http://www.blogjava.net/lanxin1020/archive/2009/04/05/264034.html'>阅读全文</a><img src ="http://www.blogjava.net/lanxin1020/aggbug/264034.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-05 22:40 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/05/264034.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>整合 Struts 和 Spring</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263966.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Sun, 05 Apr 2009 02:17:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263966.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/263966.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263966.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/263966.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/263966.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Struts Recipes 的合著者 George Franciscus 将介绍另一个重大的 Struts 整合窍门 —— 这次是将 Struts 应用程序导入 Spring 框架。请跟随 George，他将向您展示如何改变 Struts 动作，使得管理 Struts 动作就像管理 Spring beans 那...&nbsp;&nbsp;<a href='http://www.blogjava.net/lanxin1020/archive/2009/04/05/263966.html'>阅读全文</a><img src ="http://www.blogjava.net/lanxin1020/aggbug/263966.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-05 10:17 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/05/263966.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>三种整合 Struts 应用程序与 Spring 的方式(转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263963.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Sun, 05 Apr 2009 01:37:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263963.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/263963.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263963.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/263963.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/263963.html</trackback:ping><description><![CDATA[<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr valign="top">
            <td width="100%">
            <h1><em>三种整合 Struts 应用程序与 Spring 的方式</em></h1>
            <img class="display-img" alt="" src="//www.ibm.com/i/c.gif" width="1" height="6" /></td>
            <td class="no-print" width="192"></td>
        </tr>
    </tbody>
</table>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr valign="top">
            <td width="10"><img alt="" src="//www.ibm.com/i/c.gif" width="10" height="1" /></td>
            <td width="100%">
            <p>&nbsp;</p>
            <blockquote><em>Struts Recipes</em> 的合著者 George Franciscus 将介绍另一个重大的 Struts 整合窍门 —— 这次是将 Struts 应用程序导入 Spring 框架。请跟随 George，他将向您展示如何改变 Struts 动作，使得管理 Struts 动作就像管理 Spring beans 那样。结果是一个增强的 web 框架，这个框架可以方便地利用 Spring AOP 的优势。</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
            <p>您肯定已经听说过控制反转 (IOC) 设计模式，因为很长一段时间以来一直在流传关于它的信息。如果您在任何功能中使用过 Spring 框架，那么您就知道其原理的作用。在本文中，我利用这一原理把一个 Struts 应用程序注入 Spring 框架，您将亲身体会到 IOC 模式的强大。</p>
            <p>将一个 Struts 应用程序整合进 Spring 框架具有多方面的优点。首先，Spring 是为解决一些关于 JEE 的真实世界问题而设计的，比如复杂性、低性能和可测试性，等等。第二，Spring 框架包含一个 AOP 实现，允许您将面向方面技术应用于面向对象的代码。第三，一些人可能会说 Spring 框架只有<em>处理</em> Struts 比 Struts 处理自己好。但是这是观点问题，我演示三种将 Struts 应用程序整合到 Spring 框架的方法后，具体由您自己决定使用哪一种。</p>
            <p>我所演示的方法都是执行起来相对简单的，但是它们却具有明显不同的优点。我为每一种方法创建了一个独立而可用的例子，这样您就可以完全理解每种方法。请参阅 <a href="#resources" cmimpressionsent="1">下载</a> 部分获得完整例子源代码。请参阅 <a href="#resources" cmimpressionsent="1">参考资料</a>，下载 Struts MVC 和 Spring 框架。</p>
            <p><a name="N1008C"><span class="atitle">为什么 Spring 这么了不起？</span></a></p>
            <p>Spring 的创立者 Rod Johnson 以一种批判的眼光看待 Java&#8482; 企业软件开发，并且提议很多企业难题都能够通过战略地使用 IOC 模式（也称作依赖注入）来解决。当 Rod 和一个具有奉献精神的开放源码开发者团队将这个理论应用于实践时，结果就产生了 Spring 框架。简言之，Spring 是一个轻型的容器，利用它可以使用一个外部 XML 配置文件方便地将对象连接在一起。每个对象都可以通过显示一个 JavaBean 属性收到一个到依赖对象的引用，留给您的简单任务就只是在一个 XML 配置文件中把它们连接好。</p>
            <table border="0" cellspacing="0" cellpadding="0" width="40%" align="right">
                <tbody>
                    <tr>
                        <td width="10"><img alt="" src="//www.ibm.com/i/c.gif" width="10" height="1" /></td>
                        <td>
                        <table border="1" cellspacing="0" cellpadding="5" width="100%">
                            <tbody>
                                <tr>
                                    <td bgcolor="#eeeeee"><a name="N10099"><strong>IOC 和 Spring</strong></a><br />
                                    <p>IOC 是一种使应用程序逻辑外在化的设计模式，所以它是被注入而不是被写入客户机代码中。将 IOC 与接口编程应用结合，就像 Spring 框架那样，产生了一种架构，这种架构能够减少客户机对特定实现逻辑的依赖。请参阅 <a href="#resources" cmimpressionsent="1">参考资料</a> 了解更多关于 IOC 和 Spring 的信息。</p>
                                    </td>
                                </tr>
                            </tbody>
                        </table>
                        </td>
                    </tr>
                </tbody>
            </table>
            <p>依赖注入是一个强大的特性，但是 Spring 框架能够提供更多特性。Spring 支持可插拔的事务管理器，可以给您的事务处理提供更广泛的选择范围。它集成了领先的持久性框架，并且提供一个一致的异常层次结构。Spring 还提供了一种使用面向方面代码代替正常的面向对象代码的简单机制。</p>
            <p>Spring AOP 允许您使用<em>拦截器</em> 在一个或多个执行点上拦截应用程序逻辑。加强应用程序在拦截器中的日志记录逻辑会产生一个更可读的、实用的代码基础，所以拦截器广泛用于日志记录。您很快就会看到，为了处理横切关注点，Spring AOP 发布了它自己的拦截器，您也可以编写您自己的拦截器。</p>
            <br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
                        <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
                    </tr>
                </tbody>
            </table>
            <table class="no-print" cellspacing="0" cellpadding="0" align="right">
                <tbody>
                    <tr align="right">
                        <td><img alt="" src="//www.ibm.com/i/c.gif" width="100%" height="4" /><br />
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N100B0"><span class="atitle">整合 Struts 和 Spring</span></a></p>
            <p>与 Struts 相似，Spring 可以作为一个 MVC 实现。这两种框架都具有自己的优点和缺点，尽管大部分人同意 Struts 在 MVC 方面仍然是最好的。很多开发团队已经学会在时间紧迫的时候利用 Struts 作为构造高品质软件的基础。Struts 具有如此大的推动力，以至于开发团队宁愿整合 Spring 框架的特性，而不愿意转换成 Spring MVC。没必要进行转换对您来说是一个好消息。Spring 架构允许您将 Struts 作为 Web 框架连接到基于 Spring 的业务和持久层。最后的结果就是现在一切条件都具备了。</p>
            <p>在接下来的小窍门中，您将会了解到三种将 Struts MVC 整合到 Spring 框架的方法。我将揭示每种方法的缺陷并且对比它们的优点。 一旦您了解到所有三种方法的作用，我将会向您展示一个令人兴奋的应用程序，这个程序使用的是这三种方法中我最喜欢的一种。</p>
            <br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
                        <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N100BC"><span class="atitle">三个小窍门</span></a></p>
            <p>接下来的每种整合技术（或者窍门）都有自己的优点和特点。我偏爱其中的一种，但是我知道这三种都能够加深您对 Struts 和 Spring 的理解。在处理各种不同情况的时候，这将给您提供一个广阔的选择范围。方法如下：</p>
            <ul>
                <li>使用 Spring 的 <code>ActionSupport</code> 类整合 Structs</li>
                <li>使用 Spring 的 <code>DelegatingRequestProcessor</code> 覆盖 Struts 的 <code>RequestProcessor </code></li>
                <li>将 Struts <code>Action</code> 管理委托给 Spring 框架</li>
            </ul>
            <p><a name="N100E1"><span class="smalltitle">装载应用程序环境</span></a></p>
            <p>无论您使用哪种技术，都需要使用 Spring 的 <code>ContextLoaderPlugin</code> 为 Struts 的 <code>ActionServlet</code> 装载 Spring 应用程序环境。就像添加任何其他插件一样，简单地向您的 struts-config.xml 文件添加该插件，如下所示：</p>
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">&lt;plug-in className=
                        "org.springframework.web.struts.ContextLoaderPlugIn"&gt;
                        &lt;set-property property=
                        "contextConfigLocation" value="/WEB-INF/beans.xml"/&gt;
                        &lt;/plug-in&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>前面已经提到过，在 <a href="#download" cmimpressionsent="1">下载</a> 部分，您能够找到这三个完全可使用的例子的完整源代码。每个例子都为一个书籍搜索应用程序提供一种不同的 Struts 和 Spring 的整合方法。您可以在这里看到例子的要点，但是您也可以下载应用程序以查看所有的细节。</p>
            <br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
                        <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N100FE"><span class="atitle">窍门 1. 使用 Spring 的 ActionSupport</span></a></p>
            <p>手动创建一个 Spring 环境是一种整合 Struts 和 Spring 的最直观的方式。为了使它变得更简单，Spring 提供了一些帮助。为了方便地获得 Spring 环境，<code>org.springframework.web.struts.ActionSupport</code> 类提供了一个 <code>getWebApplicationContext()</code> 方法。您所做的只是从 Spring 的 <code>ActionSupport</code> 而不是 Struts <code>Action</code> 类扩展您的动作，如清单 1 所示：</p>
            <br />
            <a name="N1011C"><strong>清单 1. 使用 ActionSupport 整合 Struts</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">package ca.nexcel.books.actions;
                        import java.io.IOException;
                        import javax.servlet.ServletException;
                        import javax.servlet.http.HttpServletRequest;
                        import javax.servlet.http.HttpServletResponse;
                        import org.apache.struts.action.ActionError;
                        import org.apache.struts.action.ActionErrors;
                        import org.apache.struts.action.ActionForm;
                        import org.apache.struts.action.ActionForward;
                        import org.apache.struts.action.ActionMapping;
                        import org.apache.struts.action.DynaActionForm;
                        import org.springframework.context.ApplicationContext;
                        import org.springframework.web.struts.ActionSupport;
                        import ca.nexcel.books.beans.Book;
                        import ca.nexcel.books.business.BookService;
                        public class SearchSubmit extends ActionSupport {   <span class="boldcode">|(1)</span>
                        public ActionForward execute(
                        ActionMapping mapping,
                        ActionForm form,
                        HttpServletRequest request,
                        HttpServletResponse response)
                        throws IOException, ServletException {
                        DynaActionForm searchForm = (DynaActionForm) form;
                        String isbn = (String) searchForm.get("isbn");
                        //the old fashion way
                        //BookService bookService = new BookServiceImpl();
                        ApplicationContext ctx =
                        getWebApplicationContext();    <span class="boldcode">|(2)</span>
                        BookService bookService =
                        (BookService) ctx.getBean("bookService");   <span class="boldcode">|(3)</span>
                        Book book = bookService.read(isbn.trim());
                        if (null == book) {
                        ActionErrors errors = new ActionErrors();
                        errors.add(ActionErrors.GLOBAL_ERROR,new ActionError
                        ("message.notfound"));
                        saveErrors(request, errors);
                        return mapping.findForward("failure") ;
                        }
                        request.setAttribute("book", book);
                        return mapping.findForward("success");
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>让我们快速思考一下这里到底发生了什么。在 (1) 处，我通过从 Spring 的 <code>ActionSupport</code> 类而不是 Struts 的 <code>Action</code> 类进行扩展，创建了一个新的 <code>Action</code>。在 (2) 处，我使用 <code>getWebApplicationContext()</code> 方法获得一个 <code>ApplicationContext</code>。为了获得业务服务，我使用在 (2) 处获得的环境在 (3) 处查找一个 Spring bean。</p>
            <p>这种技术很简单并且易于理解。不幸的是，它将 Struts 动作与 Spring 框架耦合在一起。如果您想替换掉 Spring，那么您必须重写代码。并且，由于 Struts 动作不在 Spring 的控制之下，所以它不能获得 Spring AOP 的优势。当使用多重独立的 Spring 环境时，这种技术可能有用，但是在大多数情况下，这种方法不如另外两种方法合适。</p>
            <br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
                        <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N10146"><span class="atitle">窍门 2. 覆盖 RequestProcessor</span></a></p>
            <p>将 Spring 从 Struts 动作中分离是一个更巧妙的设计选择。分离的一种方法是使用 <code>org.springframework.web.struts.DelegatingRequestProcessor</code> 类来覆盖 Struts 的 <code>RequestProcessor</code> 处理程序，如清单 2 所示：</p>
            <br />
            <a name="N1015C"><strong>清单 2. 通过 Spring 的 DelegatingRequestProcessor 进行整合</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
                        &lt;!DOCTYPE struts-config PUBLIC
                        "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
                        "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"&gt;
                        &lt;struts-config&gt;
                        &lt;form-beans&gt;
                        &lt;form-bean name="searchForm"
                        type="org.apache.struts.validator.DynaValidatorForm"&gt;
                        &lt;form-property name="isbn"    type="java.lang.String"/&gt;
                        &lt;/form-bean&gt;
                        &lt;/form-beans&gt;
                        &lt;global-forwards type="org.apache.struts.action.ActionForward"&gt;
                        &lt;forward   name="welcome"                path="/welcome.do"/&gt;
                        &lt;forward   name="searchEntry"            path="/searchEntry.do"/&gt;
                        &lt;forward   name="searchSubmit"           path="/searchSubmit.do"/&gt;
                        &lt;/global-forwards&gt;
                        &lt;action-mappings&gt;
                        &lt;action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/&gt;
                        &lt;action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/&gt;
                        &lt;action    path="/searchSubmit"
                        type="ca.nexcel.books.actions.SearchSubmit"
                        input="/searchEntry.do"
                        validate="true"
                        name="searchForm"&gt;
                        &lt;forward name="success" path="/WEB-INF/pages/detail.jsp"/&gt;
                        &lt;forward name="failure" path="/WEB-INF/pages/search.jsp"/&gt;
                        &lt;/action&gt;
                        &lt;/action-mappings&gt;
                        &lt;message-resources parameter="ApplicationResources"/&gt;
                        &lt;controller processorClass="org.springframework.web.struts.
                        DelegatingRequestProcessor"/&gt; <span class="boldcode">|(1)</span>
                        &lt;plug-in className="org.apache.struts.validator.ValidatorPlugIn"&gt;
                        &lt;set-property property="pathnames"
                        value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/&gt;
                        &lt;/plug-in&gt;
                        &lt;plug-in className="org.springframework.web.struts.ContextLoaderPlugIn"&gt;
                        &lt;set-property property="csntextConfigLocation" value="/WEB-INF/beans.xml"/&gt;
                        &lt;/plug-in&gt;
                        &lt;/struts-config&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>我利用了 <code>&lt;controller&gt;</code> 标记来用 <code>DelegatingRequestProcessor</code> 覆盖默认的 Struts <code>RequestProcessor</code>。下一步是在我的 Spring 配置文件中注册该动作，如清单 3 所示：</p>
            <br />
            <a name="N10179"><strong>清单 3. 在 Spring 配置文件中注册一个动作</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
                        &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                        "http://www.springframework.org/dtd/spring-beans.dtd"&gt;
                        &lt;beans&gt;
                        &lt;bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/&gt;
                        &lt;bean name="/searchSubmit"
                        class="ca.nexcel.books.actions.SearchSubmit"&gt; <span class="boldcode">|(1)</span>
                        &lt;property name="bookService"&gt;
                        &lt;ref bean="bookService"/&gt;
                        &lt;/property&gt;
                        &lt;/bean&gt;
                        &lt;/beans&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>注意：在 (1) 处，我使用名称属性注册了一个 bean，以匹配 struts-config 动作映射名称。<code>SearchSubmit</code> 动作揭示了一个 JavaBean 属性，允许 Spring 在运行时填充属性，如清单 4 所示：</p>
            <br />
            <a name="N1018E"><strong>清单 4. 具有 JavaBean 属性的 Struts 动作</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">package ca.nexcel.books.actions;
                        import java.io.IOException;
                        import javax.servlet.ServletException;
                        import javax.servlet.http.HttpServletRequest;
                        import javax.servlet.http.HttpServletResponse;
                        import org.apache.struts.action.Action;
                        import org.apache.struts.action.ActionError;
                        import org.apache.struts.action.ActionErrors;
                        import org.apache.struts.action.ActionForm;
                        import org.apache.struts.action.ActionForward;
                        import org.apache.struts.action.ActionMapping;
                        import org.apache.struts.action.DynaActionForm;
                        import ca.nexcel.books.beans.Book;
                        import ca.nexcel.books.business.BookService;
                        public class SearchSubmit extends Action {
                        private BookService bookService;
                        public BookService getBookService() {
                        return bookService;
                        }
                        public void setBookService(BookService bookService) { <span class="boldcode">| (1)</span>
                        this.bookService = bookService;
                        }
                        public ActionForward execute(
                        ActionMapping mapping,
                        ActionForm form,
                        HttpServletRequest request,
                        HttpServletResponse response)
                        throws IOException, ServletException {
                        DynaActionForm searchForm = (DynaActionForm) form;
                        String isbn = (String) searchForm.get("isbn");
                        Book book = getBookService().read(isbn.trim());  <span class="boldcode">|(2)</span>
                        if (null == book) {
                        ActionErrors errors = new ActionErrors();
                        errors.add(ActionErrors.GLOBAL_ERROR,new ActionError("message.notfound"));
                        saveErrors(request, errors);
                        return mapping.findForward("failure") ;
                        }
                        request.setAttribute("book", book);
                        return mapping.findForward("success");
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>在清单 4 中，您可以了解到如何创建 Struts 动作。在 (1) 处，我创建了一个 JavaBean 属性。<code>DelegatingRequestProcessor</code>自动地配置这种属性。这种设计使 Struts 动作并不知道它正被 Spring 管理，并且使您能够利用 Sping 的动作管理框架的所有优点。由于您的 Struts 动作注意不到 Spring 的存在，所以您不需要重写您的 Struts 代码就可以使用其他控制反转容器来替换掉 Spring。</p>
            <p><code>DelegatingRequestProcessor</code> 方法的确比第一种方法好，但是仍然存在一些问题。如果您使用一个不同的 <code>RequestProcessor</code>，则需要手动整合 Spring 的 <code>DelegatingRequestProcessor</code>。添加的代码会造成维护的麻烦并且将来会降低您的应用程序的灵活性。此外，还有过一些使用一系列命令来代替 Struts <code>RequestProcessor</code> 的传闻。 这种改变将会对这种解决方法的使用寿命造成负面的影响。</p>
            <br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
                        <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N101B4"><span class="atitle">窍门 3. 将动作管理委托给 Spring</span></a></p>
            <p>一个更好的解决方法是将 Strut 动作管理委托给 Spring。您可以通过在 <code>struts-config</code> 动作映射中注册一个代理来实现。代理负责在 Spring 环境中查找 Struts 动作。由于动作在 Spring 的控制之下，所以它可以填充动作的 JavaBean 属性，并为应用诸如 Spring 的 AOP 拦截器之类的特性带来了可能。 </p>
            <p>清单 5 中的 <code>Action</code> 类与清单 4 中的相同。但是 struts-config 有一些不同：</p>
            <br />
            <a name="N101CD"><strong>清单 5. Spring 整合的委托方法</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
                        &lt;!DOCTYPE struts-config PUBLIC
                        "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
                        "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"&gt;
                        &lt;struts-config&gt;
                        &lt;form-beans&gt;
                        &lt;form-bean name="searchForm"
                        type="org.apache.struts.validator.DynaValidatorForm"&gt;
                        &lt;form-property name="isbn"    type="java.lang.String"/&gt;
                        &lt;/form-bean&gt;
                        &lt;/form-beans&gt;
                        &lt;global-forwards type="org.apache.struts.action.ActionForward"&gt;
                        &lt;forward   name="welcome"                path="/welcome.do"/&gt;
                        &lt;forward   name="searchEntry"            path="/searchEntry.do"/&gt;
                        &lt;forward   name="searchSubmit"           path="/searchSubmit.do"/&gt;
                        &lt;/global-forwards&gt;
                        &lt;action-mappings&gt;
                        &lt;action    path="/welcome" forward="/WEB-INF/pages/welcome.htm"/&gt;
                        &lt;action    path="/searchEntry" forward="/WEB-INF/pages/search.jsp"/&gt;
                        &lt;action    path="/searchSubmit"
                        type="org.springframework.web.struts.DelegatingActionProxy" <span class="boldcode">|(1)</span>
                        input="/searchEntry.do"
                        validate="true"
                        name="searchForm"&gt;
                        &lt;forward name="success" path="/WEB-INF/pages/detail.jsp"/&gt;
                        &lt;forward name="failure" path="/WEB-INF/pages/search.jsp"/&gt;
                        &lt;/action&gt;
                        &lt;/action-mappings&gt;
                        &lt;message-resources parameter="ApplicationResources"/&gt;
                        &lt;plug-in className="org.apache.struts.validator.ValidatorPlugIn"&gt;
                        &lt;set-property
                        property="pathnames"
                        value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/&gt;
                        &lt;/plug-in&gt;
                        &lt;plug-in
                        className="org.springframework.web.struts.ContextLoaderPlugIn"&gt;
                        &lt;set-property property="contextConfigLocation" value="/WEB-INF/beans.xml"/&gt;
                        &lt;/plug-in&gt;
                        &lt;/struts-config&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>清单 5 是一个典型的 struts-config.xml 文件，只有一个小小的差别。它注册 Spring 代理类的名称，而不是声明动作的类名，如（1）处所示。DelegatingActionProxy 类使用动作映射名称查找 Spring 环境中的动作。这就是我们使用 <code>ContextLoaderPlugIn</code> 声明的环境。</p>
            <p>将一个 Struts 动作注册为一个 Spring bean 是非常直观的，如清单 6 所示。我利用动作映射使用 <code>&lt;bean&gt;</code> 标记的名称属性（在这个例子中是 "<code>/searchSubmit</code>"）简单地创建了一个 bean。这个动作的 JavaBean 属性像任何 Spring bean 一样被填充： </p>
            <br />
            <a name="N101ED"><strong>清单 6. 在 Spring 环境中注册一个 Struts 动作</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
                        &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                        "http://www.springframework.org/dtd/spring-beans.dtd"&gt;
                        &lt;beans&gt;
                        &lt;bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/&gt;
                        &lt;bean name="/searchSubmit"
                        class="ca.nexcel.books.actions.SearchSubmit"&gt;
                        &lt;property name="bookService"&gt;
                        &lt;ref bean="bookService"/&gt;
                        &lt;/property&gt;
                        &lt;/bean&gt;
                        &lt;/beans&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
                        <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N101F4"><span class="atitle">动作委托的优点</span></a></p>
            <p>动作委托解决方法是这三种方法中最好的。Struts 动作不了解 Spring，不对代码作任何改变就可用于非 Spring 应用程序中。<code>RequestProcessor</code> 的改变不会影响它，并且它可以利用 Spring AOP 特性的优点。 </p>
            <p>动作委托的优点不止如此。一旦让 Spring 控制您的 Struts 动作，您就可以使用 Spring 给动作补充更强的活力。例如，没有 Spring 的话，所有的 Struts 动作都必须是线程安全的。如果您设置 <code>&lt;bean&gt;</code> 标记的 singleton 属性为&#8220;false&#8221;，那么不管用何种方法，您的应用程序都将在每一个请求上有一个新生成的动作对象。您可能不需要这种特性，但是把它放在您的工具箱中也很好。您也可以利用 Spring 的生命周期方法。例如，当实例化 Struts 动作时，<code>&lt;bean&gt;</code> 标记的 init-method 属性被用于运行一个方法。类似地，在从容器中删除 bean 之前，destroy-method 属性执行一个方法。这些方法是管理昂贵对象的好办法，它们以一种与 Servlet 生命周期相同的方式进行管理。</p>
            <br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
                        <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N1020D"><span class="atitle">拦截 Struts</span></a></p>
            <p>前面提到过，通过将 Struts 动作委托给 Spring 框架而整合 Struts 和 Spring 的一个主要的优点是：您可以将 Spring 的 AOP 拦截器应用于您的 Struts 动作。通过将 Spring 拦截器应用于 Struts 动作，您可以用最小的代价处理横切关注点。</p>
            <p>虽然 Spring 提供很多内置拦截器，但是我将向您展示如何创建自己的拦截器并把它应用于一个 Struts 动作。为了使用拦截器，您需要做三件事：</p>
            <ol>
                <li>创建拦截器。</li>
                <li>注册拦截器。</li>
                <li>声明在何处拦截代码。</li>
            </ol>
            <p>这看起来非常简单的几句话却非常强大。例如，在清单 7 中，我为 Struts 动作创建了一个日志记录拦截器。 这个拦截器在每个方法调用之前打印一句话：</p>
            <br />
            <a name="N1022D"><strong>清单 7. 一个简单的日志记录拦截器</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">package ca.nexcel.books.interceptors;
                        import org.springframework.aop.MethodBeforeAdvice;
                        import java.lang.reflect.Method;
                        public class LoggingInterceptor implements MethodBeforeAdvice {
                        public void before(Method method, Object[] objects, Object o) throws Throwable {
                        System.out.println("logging before!");
                        }
                        }
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>这个拦截器非常简单。<code>before()</code> 方法在拦截点中每个方法之前运行。在本例中，它打印出一句话，其实它可以做您想做的任何事。下一步就是在 Spring 配置文件中注册这个拦截器，如清单 8 所示：</p>
            <br />
            <a name="N1023F"><strong>清单 8. 在 Spring 配置文件中注册拦截器</strong></a><br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td class="code-outline">
                        <pre class="displaycode">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
                        &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"
                        "http://www.springframework.org/dtd/spring-beans.dtd"&gt;
                        &lt;beans&gt;
                        &lt;bean id="bookService" class="ca.nexcel.books.business.BookServiceImpl"/&gt;
                        &lt;bean name="/searchSubmit"
                        class="ca.nexcel.books.actions.SearchSubmit"&gt;
                        &lt;property name="bookService"&gt;
                        &lt;ref bean="bookService"/&gt;
                        &lt;/property&gt;
                        &lt;/bean&gt;
                        &lt;!--  Interceptors --&gt;
                        &lt;bean name="logger"
                        class="ca.nexcel.books.interceptors.LoggingInterceptor"/&gt; <span class="boldcode">|(1)</span>
                        &lt;!-- AutoProxies --&gt;
                        &lt;bean name="loggingAutoProxy"
                        class="org.springframework.aop.framework.autoproxy.
                        BeanNameAutoProxyCreator"&gt; <span class="boldcode">|(2)</span>
                        &lt;property name="beanNames"&gt;
                        &lt;value&gt;/searchSubmit&lt;/valuesgt; <span class="boldcode">|(3)</span>
                        &lt;/property&gt;
                        &lt;property name="interceptorNames"&gt;
                        &lt;list&gt;
                        &lt;value&gt;logger&lt;/value&gt; <span class="boldcode">|(4)</span>
                        &lt;/list&gt;
                        &lt;/property&gt;
                        &lt;/bean&gt;
                        &lt;/beans&gt;
                        </pre>
                        </td>
                    </tr>
                </tbody>
            </table>
            <br />
            <p>您可能已经注意到了，清单 8 扩展了 <a href="#code6" cmimpressionsent="1">清单 6</a> 中所示的应用程序以包含一个拦截器。具体细节如下：</p>
            <ul>
                <li>在 (1) 处，我注册了这个拦截器。</li>
                <li>在 (2) 处，我创建了一个 bean 名称自动代理，它描述如何应用拦截器。还有其他的方法定义拦截点，但是这种方法常见而简便。</li>
                <li>在 (3) 处，我将 Struts 动作注册为将被拦截的 bean。如果您想要拦截其他的 Struts 动作，则只需要在 "beanNames" 下面创建附加的 <code>&lt;value&gt;</code> 标记。</li>
                <li>在 (4) 处，当拦截发生时，我执行了在 (1) 处创建的拦截器 bean 的名称。这里列出的所有拦截器都应用于&#8220;beanNames&#8221;。</li>
            </ul>
            <p>就是这样。就像这个例子所展示的，将您的 Struts 动作置于 Spring 框架的控制之下，为处理您的 Struts 应用程序提供了一系列全新的选择。在本例中，使用动作委托可以轻松地利用 Spring 拦截器提高 Struts 应用程序中的日志记录能力。</p>
            <br />
            <table border="0" cellspacing="0" cellpadding="0" width="100%">
                <tbody>
                    <tr>
                        <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
                        <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
                    </tr>
                </tbody>
            </table>
            <br />
            <br />
            <p><a name="N1026F"><span class="atitle">结束语</span></a></p>
            <p>在本文中，您已经学习了将 Struts 动作整合到 Spring 框架中的三种窍门。使用 Spring 的 <code>ActionSupport</code> 来整合 Struts（第一种窍门中就是这样做的）简单而快捷，但是会将 Struts 动作与 Spring 框架耦合在一起。如果您需要将应用程序移植到一个不同的框架，则需要重写代码。第二种解决方法通过委托 <code>RequestProcessor</code> 巧妙地解开代码的耦合，但是它的可扩展性不强，并且当 Struts 的 <code>RequestProcessor</code> 变成一系列命令时，这种方法就持续不了很长时间。第三种方法是这三种方法中最好的：将 Struts 动作委托给 Spring 框架可以使代码解耦，从而使您可以在您的 Struts 应用程序中利用 Spring 的特性（比如日志记录拦截器）。</p>
            <p>&nbsp;</p>
            <br />
            </td>
        </tr>
    </tbody>
</table>
 <img src ="http://www.blogjava.net/lanxin1020/aggbug/263963.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-05 09:37 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/05/263963.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring 的优秀工具类盘点2(转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263962.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Sun, 05 Apr 2009 00:45:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263962.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/263962.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263962.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/263962.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/263962.html</trackback:ping><description><![CDATA[<p><a name="N1005D"><span class="atitle">特殊字符转义</span></a></p>
<p>由于 Web 应用程序需要联合使用到多种语言，每种语言都包含一些特殊的字符，对于动态语言或标签式的语言而言，如果需要动态构造语言的内容时，一个我们经常会碰到的问题就是特殊字符转义的问题。下面是 Web 开发者最常面对需要转义的特殊字符类型：</p>
<ul>
    <li>HTML 特殊字符；</li>
    <li>JavaScript 特殊字符；</li>
    <li>SQL 特殊字符；</li>
</ul>
<p>如果不对这些特殊字符进行转义处理，则不但可能破坏文档结构，还可以引发潜在的安全问题。Spring 为 HTML 和 JavaScript 特殊字符提供了转义操作工具类，它们分别是 HtmlUtils 和 JavaScriptUtils。</p>
<p><a name="N10075"><span class="smalltitle">HTML 特殊字符转义</span></a></p>
<p>HTML 中 &lt;，&gt;，&amp; 等字符有特殊含义，它们是 HTML 语言的保留字，因此不能直接使用。使用这些个字符时，应使用它们的转义序列：</p>
<ul>
    <li>&amp;：&amp;amp;</li>
    <li>" ：&amp;quot;</li>
    <li>&lt; ：&amp;lt;</li>
    <li>&gt; ：&amp;gt;</li>
</ul>
<p>由于 HTML 网页本身就是一个文本型结构化文档，如果直接将这些包含了 HTML 特殊字符的内容输出到网页中，极有可能破坏整个 HTML 文档的结构。所以，一般情况下需要对动态数据进行转义处理，使用转义序列表示 HTML 特殊字符。下面的 JSP 网页将一些变量动态输出到 HTML 网页中：</p>
<br />
<a name="list1"><strong>清单 1. 未进行 HTML 特殊字符转义处理网页</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            &lt;%@ page language="java" contentType="text/html; charset=utf-8"%&gt;
            &lt;%!
            String userName = "&lt;/td&gt;&lt;tr&gt;&lt;/table&gt;";
            String address = " \" type=\"button";
            %&gt;
            &lt;table border="1"&gt;
            &lt;tr&gt;
            &lt;td&gt;姓名：&lt;/td&gt;&lt;td&gt;&lt;%=userName%&gt;&lt;/td&gt; ①
            &lt;/tr&gt;
            &lt;tr&gt;
            &lt;td&gt;年龄：&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;
            &lt;/tr&gt;
            &lt;/table&gt;
            &lt;input value="&lt;%=address%&gt;"  type="text" /&gt; ②
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在 ① 和 ② 处，我们未经任何转义处理就直接将变量输出到 HTML 网页中，由于这些变量可能包含一些特殊的 HTML 的字符，它们将可能破坏整个 HTML 文档的结构。我们可以从以上 JSP 页面的一个具体输出中了解这一问题：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&lt;table border="1"&gt;
            &lt;tr&gt;
            &lt;td&gt;姓名：&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;tr&gt;&lt;/table&gt;&lt;/td&gt;
            ① 破坏了 &lt;table&gt; 的结构
            &lt;/tr&gt;
            &lt;tr&gt;
            &lt;td&gt;年龄：&lt;/td&gt;&lt;td&gt;28&lt;/td&gt;
            &lt;/tr&gt;
            &lt;/table&gt;
            &lt;input value=" " type="button"  type="text" /&gt;
            ② 将本来是输入框组件偷梁换柱为按钮组件
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>融合动态数据后的 HTML 网页已经面目全非，首先 ① 处的 &lt;table&gt; 结构被包含 HTML 特殊字符的 userName 变量截断了，造成其后的 &lt;table&gt; 代码变成无效的内容；其次，② 处 &lt;input&gt; 被动态数据改换为按钮类型的组件（type="button"）。为了避免这一问题，我们需要事先对可能破坏 HTML 文档结构的动态数据进行转义处理。Spring 为我们提供了一个简单适用的 HTML 特殊字符转义工具类，它就是 HtmlUtils。下面，我们通过一个简单的例子了解 HtmlUtils 的具体用法：</p>
<br />
<a name="list2"><strong>清单 2. HtmpEscapeExample</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            package com.baobaotao.escape;
            import org.springframework.web.util.HtmlUtils;
            public class HtmpEscapeExample {
            public static void main(String[] args) {
            String specialStr = "&lt;div id=\"testDiv\"&gt;test1;test2&lt;/div&gt;";
            String str1 = HtmlUtils.htmlEscape(specialStr); ①转换为HTML转义字符表示
            System.out.println(str1);
            String str2 = HtmlUtils.htmlEscapeDecimal(specialStr); ②转换为数据转义表示
            System.out.println(str2);
            String str3 = HtmlUtils.htmlEscapeHex(specialStr); ③转换为十六进制数据转义表示
            System.out.println(str3);
            ④下面对转义后字符串进行反向操作
            System.out.println(HtmlUtils.htmlUnescape(str1));
            System.out.println(HtmlUtils.htmlUnescape(str2));
            System.out.println(HtmlUtils.htmlUnescape(str3));
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>HTML 不但可以使用通用的转义序列表示 HTML 特殊字符，还可以使用以 # 为前缀的数字序列表示 HTML 特殊字符，它们在最终的显示效果上是一样的。HtmlUtils 提供了三个转义方法：</p>
<table class="data-table-1" border="0" cellspacing="0" summary="" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <th>方法</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><code>static String htmlEscape(String input)</code> </td>
            <td>将 HTML 特殊字符转义为 HTML 通用转义序列；</td>
        </tr>
        <tr>
            <td><code>static String htmlEscapeDecimal(String input)</code> </td>
            <td>将 HTML 特殊字符转义为带 # 的十进制数据转义序列；</td>
        </tr>
        <tr>
            <td><code>static String htmlEscapeHex(String input)</code> </td>
            <td>将 HTML 特殊字符转义为带 # 的十六进制数据转义序列；</td>
        </tr>
    </tbody>
</table>
<p>此外，HtmlUtils 还提供了一个能够将经过转义内容还原的方法：htmlUnescape(String input)，它可以还原以上三种转义序列的内容。运行以上代码，您将可以看到以下的输出：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">str1:&amp;lt;div id=&amp;quot;testDiv&amp;quot;&amp;gt;test1;test2&amp;lt;/div&amp;gt;
            str2:&#60;div id=&#34;testDiv&#34;&#62;test1;test2&#60;/div&#62;
            str3:&#x3c;div id=&#x22;testDiv&#x22;&#x3e;test1;test2&#x3c;/div&#x3e;
            &lt;div id="testDiv"&gt;test1;test2&lt;/div&gt;
            &lt;div id="testDiv"&gt;test1;test2&lt;/div&gt;
            &lt;div id="testDiv"&gt;test1;test2&lt;/div&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>您只要使用 HtmlUtils 对代码 <a href="#list1" cmimpressionsent="1">清单 1</a> 的 userName 和 address 进行转义处理，最终输出的 HTML 页面就不会遭受破坏了。</p>
<p><a name="N100F5"><span class="smalltitle">JavaScript 特殊字符转义</span></a></p>
<p>JavaScript 中也有一些需要特殊处理的字符，如果直接将它们嵌入 JavaScript 代码中，JavaScript 程序结构将会遭受破坏，甚至被嵌入一些恶意的程序。下面列出了需要转义的特殊 JavaScript 字符：</p>
<ul>
    <li>' ：\'</li>
    <li>" ：\"</li>
    <li>\ ：\\</li>
    <li>走纸换页： \f</li>
    <li>换行：\n</li>
    <li>换栏符：\t</li>
    <li>回车：\r</li>
    <li>回退符：\b</li>
</ul>

<p>我们通过一个具体例子演示动态变量是如何对 JavaScript 程序进行破坏的。假设我们有一个 JavaScript 数组变量，其元素值通过一个 Java List 对象提供，下面是完成这一操作的 JSP 代码片断：</p>
<br />
<a name="list3"><strong>清单 3. jsTest.jsp：未对 JavaScript 特殊字符进行处理</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            &lt;%@ page language="java" contentType="text/html; charset=utf-8"%&gt;
            &lt;jsp:directive.page import="java.util.*"/&gt;
            &lt;%
            List textList = new ArrayList();
            textList.add("\";alert();j=\"");
            %&gt;
            &lt;script&gt;
            var txtList = new Array();
            &lt;% for ( int i = 0 ; i &lt; textList.size() ; i++) { %&gt;
            txtList[&lt;%=i%&gt;] = "&lt;%=textList.get(i)%&gt;";
            ① 未对可能包含特殊 JavaScript 字符的变量进行处理
            &lt;% } %&gt;
            &lt;/script&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>当客户端调用这个 JSP 页面后，将得到以下的 HTML 输出页面：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&lt;script&gt;
            var txtList = new Array();
            txtList[0] = ""<span class="boldcode">;alert();j=</span>""; ① 本来是希望接受一个字符串，结果被植入了一段JavaScript代码
            &lt;/script&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>由于包含 JavaScript 特殊字符的 Java 变量直接合并到 JavaScript 代码中，我们本来期望 ① 处所示部分是一个普通的字符串，但结果变成了一段 JavaScript 代码，网页将弹出一个 alert 窗口。想像一下如果粗体部分的字符串是&#8220;";while(true)alert();j="&#8221;时会产生什么后果呢？</p>
<p>因此，如果网页中的 JavaScript 代码需要通过拼接 Java 变量动态产生时，一般需要对变量的内容进行转义处理，可以通过 Spring 的 JavaScriptUtils 完成这件工作。下面，我们使用 JavaScriptUtils 对以上代码进行改造：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&lt;%@ page language="java" contentType="text/html; charset=utf-8"%&gt;
            &lt;jsp:directive.page import="java.util.*"/&gt;
            &lt;jsp:directive.page import="org.springframework.web.util.JavaScriptUtils"/&gt;
            &lt;%
            List textList = new ArrayList();
            textList.add("\";alert();j=\"");
            %&gt;
            &lt;script&gt;
            var txtList = new Array();
            &lt;% for ( int i = 0 ; i &lt; textList.size() ; i++) { %&gt;
            ① 在输出动态内容前事先进行转义处理
            txtList[&lt;%=i%&gt;] = "&lt;%=JavaScriptUtils.javaScriptEscape(""+textList.get(i))%&gt;";
            &lt;% } %&gt;
            &lt;/script&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>通过转义处理后，这个 JSP 页面输出的结果网页的 JavaScript 代码就不会产生问题了：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&lt;script&gt;
            var txtList = new Array();
            txtList[0] = "<span class="boldcode">\";alert();j=\"</span>";
            ① 粗体部分仅是一个普通的字符串，而非一段 JavaScript 的语句了
            &lt;/script&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N10142"><span class="smalltitle">SQL特殊字符转义</span></a></p>
<p>应该说，您即使没有处理 HTML 或 JavaScript 的特殊字符，也不会带来灾难性的后果，但是如果不在动态构造 SQL 语句时对变量中特殊字符进行处理，将可能导致程序漏洞、数据盗取、数据破坏等严重的安全问题。网络中有大量讲解 SQL 注入的文章，感兴趣的读者可以搜索相关的资料深入研究。</p>
<p>虽然 SQL 注入的后果很严重，但是只要对动态构造的 SQL 语句的变量进行特殊字符转义处理，就可以避免这一问题的发生了。来看一个存在安全漏洞的经典例子：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">SELECT COUNT(userId)
            FROM t_user
            WHERE userName='"+userName+"' AND password ='"+password+"';
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>以上 SQL 语句根据返回的结果数判断用户提供的登录信息是否正确，如果 userName 变量不经过特殊字符转义处理就直接合并到 SQL 语句中，黑客就可以通过将 userName 设置为 &#8220;1' or '1'='1&#8221;绕过用户名/密码的检查直接进入系统了。</p>
<p>所以除非必要，一般建议通过 PreparedStatement 参数绑定的方式构造动态 SQL 语句，因为这种方式可以避免 SQL 注入的潜在安全问题。但是往往很难在应用中完全避免通过拼接字符串构造动态 SQL 语句的方式。为了防止他人使用特殊 SQL 字符破坏 SQL 的语句结构或植入恶意操作，必须在变量拼接到 SQL 语句之前对其中的特殊字符进行转义处理。Spring 并没有提供相应的工具类，您可以通过 jakarta commons lang 通用类包中（spring/lib/jakarta-commons/commons-lang.jar）的 StringEscapeUtils 完成这一工作：</p>
<br />
<a name="list4"><strong>清单 4. SqlEscapeExample</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">
            package com.baobaotao.escape;
            import org.apache.commons.lang.StringEscapeUtils;
            public class SqlEscapeExample {
            public static void main(String[] args) {
            String userName = "1' or '1'='1";
            String password = "123456";
            userName = StringEscapeUtils.escapeSql(userName);
            password = StringEscapeUtils.escapeSql(password);
            String sql = "SELECT COUNT(userId) FROM t_user WHERE userName='"
            + userName + "' AND password ='" + password + "'";
            System.out.println(sql);
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>事实上，StringEscapeUtils 不但提供了 SQL 特殊字符转义处理的功能，还提供了 HTML、XML、JavaScript、Java 特殊字符的转义和还原的方法。如果您不介意引入 jakarta commons lang 类包，我们更推荐您使用 StringEscapeUtils 工具类完成特殊字符转义处理的工作。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
            <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" width="100%" height="4" /><br />
            <table border="0" cellspacing="0" cellpadding="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img border="0" alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10163"><span class="atitle">方法入参检测工具类</span></a></p>
<p>Web 应用在接受表单提交的数据后都需要对其进行合法性检查，如果表单数据不合法，请求将被驳回。类似的，当我们在编写类的方法时，也常常需要对方法入参进行合法性检查，如果入参不符合要求，方法将通过抛出异常的方式拒绝后续处理。举一个例子：有一个根据文件名获取输入流的方法：InputStream getData(String file)，为了使方法能够成功执行，必须保证 file 入参不能为 null 或空白字符，否则根本无须进行后继的处理。这时方法的编写者通常会在方法体的最前面编写一段对入参进行检测的代码，如下所示：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public InputStream getData(String file) {
            if (file == null || file.length() == 0|| file.replaceAll("\\s", "").length() == 0) {
            throw new IllegalArgumentException("file入参不是有效的文件地址");
            }
            &#8230;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>类似以上检测方法入参的代码是非常常见，但是在每个方法中都使用手工编写检测逻辑的方式并不是一个好主意。阅读 Spring 源码，您会发现 Spring 采用一个 org.springframework.util.Assert 通用类完成这一任务。</p>
<p>Assert 翻译为中文为&#8220;断言&#8221;，使用过 JUnit 的读者都熟知这个概念，它断定某一个实际的运行值和预期想一样，否则就抛出异常。Spring 对方法入参的检测借用了这个概念，其提供的 Assert 类拥有众多按规则对方法入参进行断言的方法，可以满足大部分方法入参检测的要求。这些断言方法在入参不满足要求时就会抛出 IllegalArgumentException。下面，我们来认识一下 Assert 类中的常用断言方法：</p>
<table class="data-table-1" border="0" cellspacing="0" summary="" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <th>断言方法</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><code>notNull(Object object)</code> </td>
            <td>当 object 不为 null 时抛出异常，notNull(Object object, String message) 方法允许您通过 message 定制异常信息。和 notNull() 方法断言规则相反的方法是 isNull(Object object)/isNull(Object object, String message)，它要求入参一定是 null；</td>
        </tr>
        <tr>
            <td><code>isTrue(boolean expression) / isTrue(boolean expression, String message)</code> </td>
            <td>当 expression 不为 true 抛出异常；</td>
        </tr>
        <tr>
            <td><code>notEmpty(Collection collection) / notEmpty(Collection collection, String message)</code> </td>
            <td>当集合未包含元素时抛出异常。notEmpty(Map map) / notEmpty(Map map, String message) 和 notEmpty(Object[] array, String message) / notEmpty(Object[] array, String message) 分别对 Map 和 Object[] 类型的入参进行判断；</td>
        </tr>
        <tr>
            <td><code>hasLength(String text) / hasLength(String text, String message)</code> </td>
            <td>当 text 为 null 或长度为 0 时抛出异常；</td>
        </tr>
        <tr>
            <td><code>hasText(String text) / hasText(String text, String message)</code> </td>
            <td>text 不能为 null 且必须至少包含一个非空格的字符，否则抛出异常；</td>
        </tr>
        <tr>
            <td><code>isInstanceOf(Class clazz, Object obj) / isInstanceOf(Class type, Object obj, String message)</code> </td>
            <td>如果 obj 不能被正确造型为 clazz 指定的类将抛出异常；</td>
        </tr>
        <tr>
            <td><code>isAssignable(Class superType, Class subType) / isAssignable(Class superType, Class subType, String message)</code> </td>
            <td>subType 必须可以按类型匹配于 superType，否则将抛出异常；</td>
        </tr>
    </tbody>
</table>
<p>使用 Assert 断言类可以简化方法入参检测的代码，如 InputStream getData(String file) 在应用 Assert 断言类后，其代码可以简化为以下的形式：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">public InputStream getData(String file){
            Assert.hasText(file,"file入参不是有效的文件地址");
            ① 使用 Spring 断言类进行方法入参检测
            &#8230;
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>可见使用 Spring 的 Assert 替代自编码实现的入参检测逻辑后，方法的简洁性得到了不少的提高。Assert 不依赖于 Spring 容器，您可以大胆地在自己的应用中使用这个工具类。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
            <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" width="100%" height="4" /><br />
            <table border="0" cellspacing="0" cellpadding="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img border="0" alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N101ED"><span class="atitle">小结</span></a></p>
<p>本文介绍了一些常用的 Spring 工具类，其中大部分 Spring 工具类不但可以在基于 Spring 的应用中使用，还可以在其它的应用中使用。</p>
<p>对于 Web 应用来说，由于有很多关联的脚本代码，如果这些代码通过拼接字符串的方式动态产生，就需要对动态内容中特殊的字符进行转义处理，否则就有可能产生意想不到的后果。Spring 为此提供了 HtmlUtils 和 JavaScriptUtils 工具类，只要将动态内容在拼接之前使用工具类进行转义处理，就可以避免类似问题的发生了。如果您不介意引入一个第三方类包，那么 jakarta commons lang 通用类包中的 StringEscapeUtils 工具类可能更加适合，因为它提供了更加全面的转义功能。</p>
<p>最后我们还介绍了 Spring 的 Assert 工具类，Assert 工具类是通用性很强的工具类，它使用面向对象的方式解决方法入参检测的问题，您可以在自己的应用中使用 Assert 对方法入参进行检查。</p>
<br />
 <img src ="http://www.blogjava.net/lanxin1020/aggbug/263962.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-05 08:45 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/05/263962.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring 的优秀工具类盘点1(转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263961.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Sun, 05 Apr 2009 00:44:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263961.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/263961.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/04/05/263961.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/263961.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/263961.html</trackback:ping><description><![CDATA[<blockquote>
<p>Spring 不但提供了一个功能全面的应用开发框架，本身还拥有众多可以在程序编写时直接使用的工具类，您不但可以在 Spring 应用中使用这些工具类，也可以在其它的应用中使用，这些工具类中的大部分是可以在脱离 Spring 框架时使用的。了解 Spring 中有哪些好用的工具类并在程序编写时适当使用，将有助于提高开发效率、增强代码质量。</p>
<p>在这个分为两部分的文章中，我们将从众多的 Spring 工具类中遴选出那些好用的工具类介绍给大家。第 1 部分将介绍与文件资源操作和 Web 相关的工具类。在 <a href="http://www.ibm.com/developerworks/cn/java/j-lo-spring-utils2/" cmimpressionsent="1">第 2 部分</a> 中将介绍特殊字符转义和方法入参检测工具类。</p>
</blockquote><!--start RESERVED FOR FUTURE USE INCLUDE FILES--><!-- include java script once we verify teams wants to use this and it will work on dbcs and cyrillic characters --><!--end RESERVED FOR FUTURE USE INCLUDE FILES-->
<p><a name="N1005D"><span class="atitle">文件资源操作</span></a></p>
<p>文件资源的操作是应用程序中常见的功能，如当上传一个文件后将其保存在特定目录下，从指定地址加载一个配置文件等等。我们一般使用 JDK 的 I/O 处理类完成这些操作，但对于一般的应用程序来说，JDK 的这些操作类所提供的方法过于底层，直接使用它们进行文件操作不但程序编写复杂而且容易产生错误。相比于 JDK 的 File，Spring 的 Resource 接口（资源概念的描述接口）抽象层面更高且涵盖面更广，Spring 提供了许多方便易用的资源操作工具类，它们大大降低资源操作的复杂度，同时具有更强的普适性。这些工具类不依赖于 Spring 容器，这意味着您可以在程序中象一般普通类一样使用它们。</p>
<p><a name="N10066"><span class="smalltitle">加载文件资源</span></a></p>
<p>Spring 定义了一个 org.springframework.core.io.Resource 接口，Resource 接口是为了统一各种类型不同的资源而定义的，Spring 提供了若干 Resource 接口的实现类，这些实现类可以轻松地加载不同类型的底层资源，并提供了获取文件名、URL 地址以及资源内容的操作方法。</p>
<p><strong>访问文件资源</strong> </p>
<p>假设有一个文件地位于 Web 应用的类路径下，您可以通过以下方式对这个文件资源进行访问：</p>
<ul>
    <li>通过 FileSystemResource 以文件系统绝对路径的方式进行访问；</li>
    <li>通过 ClassPathResource 以类路径的方式进行访问；</li>
    <li>通过 ServletContextResource 以相对于Web应用根目录的方式进行访问。</li>
</ul>
<p>相比于通过 JDK 的 File 类访问文件资源的方式，Spring 的 Resource 实现类无疑提供了更加灵活的操作方式，您可以根据情况选择适合的 Resource 实现类访问资源。下面，我们分别通过 FileSystemResource 和 ClassPathResource 访问同一个文件资源：</p>
<br />
<a name="N10087"><strong>清单 1. FileSourceExample</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">package com.baobaotao.io;
            import java.io.IOException;
            import java.io.InputStream;
            import org.springframework.core.io.ClassPathResource;
            import org.springframework.core.io.FileSystemResource;
            import org.springframework.core.io.Resource;
            public class FileSourceExample {
            public static void main(String[] args) {
            try {
            String filePath =
            "D:/masterSpring/chapter23/webapp/WEB-INF/classes/conf/file1.txt";
            // ① 使用系统文件路径方式加载文件
            Resource res1 = new FileSystemResource(filePath);
            // ② 使用类路径方式加载文件
            Resource res2 = new ClassPathResource("conf/file1.txt");
            InputStream ins1 = res1.getInputStream();
            InputStream ins2 = res2.getInputStream();
            System.out.println("res1:"+res1.getFilename());
            System.out.println("res2:"+res2.getFilename());
            } catch (IOException e) {
            e.printStackTrace();
            }
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>在获取资源后，您就可以通过 Resource 接口定义的多个方法访问文件的数据和其它的信息：如您可以通过 getFileName() 获取文件名，通过 getFile() 获取资源对应的 File 对象，通过 getInputStream() 直接获取文件的输入流。此外，您还可以通过 createRelative(String relativePath) 在资源相对地址上创建新的资源。</p>
<p>在 Web 应用中，您还可以通过 ServletContextResource 以相对于 Web 应用根目录的方式访问文件资源，如下所示：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&lt;%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%&gt;
            &lt;jsp:directive.page import="
            org.springframework.web.context.support.ServletContextResource"/&gt;
            &lt;jsp:directive.page import="org.springframework.core.io.Resource"/&gt;
            &lt;%
            // ① 注意文件资源地址以相对于 Web 应用根路径的方式表示
            Resource res3 = new ServletContextResource(application,
            "/WEB-INF/classes/conf/file1.txt");
            out.print(res3.getFilename());
            %&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>对于位于远程服务器（Web 服务器或 FTP 服务器）的文件资源，您则可以方便地通过 UrlResource 进行访问。</p>
<p>为了方便访问不同类型的资源，您必须使用相应的 Resource 实现类，是否可以在不显式使用 Resource 实现类的情况下，仅根据带特殊前缀的资源地址直接加载文件资源呢？Spring 提供了一个 ResourceUtils 工具类，它支持&#8220;classpath:&#8221;和&#8220;file:&#8221;的地址前缀，它能够从指定的地址加载文件资源，请看下面的例子：</p>
<br />
<a name="N100A0"><strong>清单 2. ResourceUtilsExample</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">package com.baobaotao.io;
            import java.io.File;
            import org.springframework.util.ResourceUtils;
            public class ResourceUtilsExample {
            public static void main(String[] args) throws Throwable{
            File clsFile = ResourceUtils.getFile("classpath:conf/file1.txt");
            System.out.println(clsFile.isFile());
            String httpFilePath = "file:D:/masterSpring/chapter23/src/conf/file1.txt";
            File httpFile = ResourceUtils.getFile(httpFilePath);
            System.out.println(httpFile.isFile());
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>ResourceUtils 的 getFile(String resourceLocation) 方法支持带特殊前缀的资源地址，这样，我们就可以在不和 Resource 实现类打交道的情况下使用 Spring 文件资源加载的功能了。</p>
<p><strong>本地化文件资源</strong> </p>
<p>本地化文件资源是一组通过本地化标识名进行特殊命名的文件，Spring 提供的 LocalizedResourceHelper 允许通过文件资源基名和本地化实体获取匹配的本地化文件资源并以 Resource 对象返回。假设在类路径的 i18n 目录下，拥有一组基名为 message 的本地化文件资源，我们通过以下实例演示获取对应中国大陆和美国的本地化文件资源：</p>
<br />
<a name="N100B5"><strong>清单 3. LocaleResourceTest</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">package com.baobaotao.io;
            import java.util.Locale;
            import org.springframework.core.io.Resource;
            import org.springframework.core.io.support.LocalizedResourceHelper;
            public class LocaleResourceTest {
            public static void main(String[] args) {
            LocalizedResourceHelper lrHalper = new LocalizedResourceHelper();
            // ① 获取对应美国的本地化文件资源
            Resource msg_us = lrHalper.findLocalizedResource("i18n/message", ".properties",
            Locale.US);
            // ② 获取对应中国大陆的本地化文件资源
            Resource msg_cn = lrHalper.findLocalizedResource("i18n/message", ".properties",
            Locale.CHINA);
            System.out.println("fileName(us):"+msg_us.getFilename());
            System.out.println("fileName(cn):"+msg_cn.getFilename());
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>虽然 JDK 的 java.util.ResourceBundle 类也可以通过相似的方式获取本地化文件资源，但是其返回的是 ResourceBundle 类型的对象。如果您决定统一使用 Spring 的 Resource 接表征文件资源，那么 LocalizedResourceHelper 就是获取文件资源的非常适合的帮助类了。</p>
<p><a name="N100C1"><span class="smalltitle">文件操作</span></a></p>
<p>在使用各种 Resource 接口的实现类加载文件资源后，经常需要对文件资源进行读取、拷贝、转存等不同类型的操作。您可以通过 Resource 接口所提供了方法完成这些功能，不过在大多数情况下，通过 Spring 为 Resource 所配备的工具类完成文件资源的操作将更加方便。</p>
<p><strong>文件内容拷贝</strong> </p>
<p>第一个我们要认识的是 FileCopyUtils，它提供了许多一步式的静态操作方法，能够将文件内容拷贝到一个目标 byte[]、String 甚至一个输出流或输出文件中。下面的实例展示了 FileCopyUtils 具体使用方法：</p>
<br />
<a name="N100D3"><strong>清单 4. FileCopyUtilsExample</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">package com.baobaotao.io;
            import java.io.ByteArrayOutputStream;
            import java.io.File;
            import java.io.FileReader;
            import java.io.OutputStream;
            import org.springframework.core.io.ClassPathResource;
            import org.springframework.core.io.Resource;
            import org.springframework.util.FileCopyUtils;
            public class FileCopyUtilsExample {
            public static void main(String[] args) throws Throwable {
            Resource res = new ClassPathResource("conf/file1.txt");
            // ① 将文件内容拷贝到一个 byte[] 中
            byte[] fileData = FileCopyUtils.copyToByteArray(res.getFile());
            // ② 将文件内容拷贝到一个 String 中
            String fileStr = FileCopyUtils.copyToString(new FileReader(res.getFile()));
            // ③ 将文件内容拷贝到另一个目标文件
            FileCopyUtils.copy(res.getFile(),
            new File(res.getFile().getParent()+ "/file2.txt"));
            // ④ 将文件内容拷贝到一个输出流中
            OutputStream os = new ByteArrayOutputStream();
            FileCopyUtils.copy(res.getInputStream(), os);
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>往往我们都通过直接操作 InputStream 读取文件的内容，但是流操作的代码是比较底层的，代码的面向对象性并不强。通过 FileCopyUtils 读取和拷贝文件内容易于操作且相当直观。如在 ① 处，我们通过 FileCopyUtils 的 copyToByteArray(File in) 方法就可以直接将文件内容读到一个 byte[] 中；另一个可用的方法是 copyToByteArray(InputStream in)，它将输入流读取到一个 byte[] 中。</p>
<p>如果是文本文件，您可能希望将文件内容读取到 String 中，此时您可以使用 copyToString(Reader in) 方法，如 ② 所示。使用 FileReader 对 File 进行封装，或使用 InputStreamReader 对 InputStream 进行封装就可以了。</p>
<p>FileCopyUtils 还提供了多个将文件内容拷贝到各种目标对象中的方法，这些方法包括：</p>
<table class="data-table-1" border="0" cellspacing="0" summary="" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <th>方法</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><code>static void copy(byte[] in, File out)</code> </td>
            <td>将 byte[] 拷贝到一个文件中</td>
        </tr>
        <tr>
            <td><code>static void copy(byte[] in, OutputStream out) </code></td>
            <td>将 byte[] 拷贝到一个输出流中</td>
        </tr>
        <tr>
            <td><code>static int copy(File in, File out) </code></td>
            <td>将文件拷贝到另一个文件中</td>
        </tr>
        <tr>
            <td><code>static int copy(InputStream in, OutputStream out)</code> </td>
            <td>将输入流拷贝到输出流中</td>
        </tr>
        <tr>
            <td><code>static int copy(Reader in, Writer out)</code> </td>
            <td>将 Reader 读取的内容拷贝到 Writer 指向目标输出中</td>
        </tr>
        <tr>
            <td><code>static void copy(String in, Writer out)</code> </td>
            <td>将字符串拷贝到一个 Writer 指向的目标中</td>
        </tr>
    </tbody>
</table>
<p>在实例中，我们虽然使用 Resource 加载文件资源，但 FileCopyUtils 本身和 Resource 没有任何关系，您完全可以在基于 JDK I/O API 的程序中使用这个工具类。</p>
<p><strong>属性文件操作</strong> </p>
<p>我们知道可以通过 java.util.Properties的load(InputStream inStream) 方法从一个输入流中加载属性资源。Spring 提供的 PropertiesLoaderUtils 允许您直接通过基于类路径的文件地址加载属性资源，请看下面的例子：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">package com.baobaotao.io;
            import java.util.Properties;
            import org.springframework.core.io.support.PropertiesLoaderUtils;
            public class PropertiesLoaderUtilsExample {
            public static void main(String[] args) throws Throwable {
            // ① jdbc.properties 是位于类路径下的文件
            Properties props = PropertiesLoaderUtils.loadAllProperties("jdbc.properties");
            System.out.println(props.getProperty("jdbc.driverClassName"));
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>一般情况下，应用程序的属性文件都放置在类路径下，所以 PropertiesLoaderUtils 比之于 Properties#load(InputStream inStream) 方法显然具有更强的实用性。此外，PropertiesLoaderUtils 还可以直接从 Resource 对象中加载属性资源：</p>
<table class="data-table-1" border="0" cellspacing="0" summary="" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <th>方法</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><code>static Properties loadProperties(Resource resource)</code> </td>
            <td>从 Resource 中加载属性</td>
        </tr>
        <tr>
            <td><code>static void fillProperties(Properties props, Resource resource)</code> </td>
            <td>将 Resource 中的属性数据添加到一个已经存在的 Properties 对象中</td>
        </tr>
    </tbody>
</table>
<p><strong>特殊编码的资源</strong> </p>
<p>当您使用 Resource 实现类加载文件资源时，它默认采用操作系统的编码格式。如果文件资源采用了特殊的编码格式（如 UTF-8），则在读取资源内容时必须事先通过 EncodedResource 指定编码格式，否则将会产生中文乱码的问题。</p>
<br />
<a name="N1018D"><strong>清单 5. EncodedResourceExample</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">package com.baobaotao.io;
            import org.springframework.core.io.ClassPathResource;
            import org.springframework.core.io.Resource;
            import org.springframework.core.io.support.EncodedResource;
            import org.springframework.util.FileCopyUtils;
            public class EncodedResourceExample {
            public static void main(String[] args) throws Throwable  {
            Resource res = new ClassPathResource("conf/file1.txt");
            // ① 指定文件资源对应的编码格式（UTF-8）
            EncodedResource encRes = new EncodedResource(res,"UTF-8");
            // ② 这样才能正确读取文件的内容，而不会出现乱码
            String content  = FileCopyUtils.copyToString(encRes.getReader());
            System.out.println(content);
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>EncodedResource 拥有一个 getResource() 方法获取 Resource，但该方法返回的是通过构造函数传入的原 Resource 对象，所以必须通过 EncodedResource#getReader() 获取应用编码后的 Reader 对象，然后再通过该 Reader 读取文件的内容。</p>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
            <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" width="100%" height="4" /><br />
            <table border="0" cellspacing="0" cellpadding="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img border="0" alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10199"><span class="atitle">Web 相关工具类</span></a></p>
<p>您几乎总是使用 Spring 框架开发 Web 的应用，Spring 为 Web 应用提供了很多有用的工具类，这些工具类可以给您的程序开发带来很多便利。在这节里，我们将逐一介绍这些工具类的使用方法。</p>
<p><a name="N101A2"><span class="smalltitle">操作 Servlet API 的工具类</span></a></p>
<p>当您在控制器、JSP 页面中想直接访问 Spring 容器时，您必须事先获取 WebApplicationContext 对象。Spring 容器在启动时将 WebApplicationContext 保存在 ServletContext的属性列表中，通过 WebApplicationContextUtils 工具类可以方便地获取 WebApplicationContext 对象。</p>
<p><strong>WebApplicationContextUtils</strong> </p>
<p>当 Web 应用集成 Spring 容器后，代表 Spring 容器的 WebApplicationContext 对象将以 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE 为键存放在 ServletContext 属性列表中。您当然可以直接通过以下语句获取 WebApplicationContext：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">WebApplicationContext wac = (WebApplicationContext)servletContext.
            getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>但通过位于 org.springframework.web.context.support 包中的 WebApplicationContextUtils 工具类获取 WebApplicationContext 更方便：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">WebApplicationContext wac =WebApplicationContextUtils.
            getWebApplicationContext(servletContext);
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>当 ServletContext 属性列表中不存在 WebApplicationContext 时，getWebApplicationContext() 方法不会抛出异常，它简单地返回 null。如果后续代码直接访问返回的结果将引发一个 NullPointerException 异常，而 WebApplicationContextUtils 另一个 getRequiredWebApplicationContext(ServletContext sc) 方法要求 ServletContext 属性列表中一定要包含一个有效的 WebApplicationContext 对象，否则马上抛出一个 IllegalStateException 异常。我们推荐使用后者，因为它能提前发现错误的时间，强制开发者搭建好必备的基础设施。</p>
<p><strong>WebUtils</strong> </p>
<p>位于 org.springframework.web.util 包中的 WebUtils 是一个非常好用的工具类，它对很多 Servlet API 提供了易用的代理方法，降低了访问 Servlet API 的复杂度，可以将其看成是常用 Servlet API 方法的门面类。</p>
<p>下面这些方法为访问 HttpServletRequest 和 HttpSession 中的对象和属性带来了方便：</p>
<table class="data-table-1" border="0" cellspacing="0" summary="" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <th>方法</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><code>Cookie getCookie(HttpServletRequest request, String name)</code> </td>
            <td>获取 HttpServletRequest 中特定名字的 Cookie 对象。如果您需要创建 Cookie， Spring 也提供了一个方便的 CookieGenerator 工具类；</td>
        </tr>
        <tr>
            <td><code>Object getSessionAttribute(HttpServletRequest request, String name) </code></td>
            <td>获取 HttpSession 特定属性名的对象，否则您必须通过request.getHttpSession.getAttribute(name) 完成相同的操作；</td>
        </tr>
        <tr>
            <td><code>Object getRequiredSessionAttribute(HttpServletRequest request, String name)</code> </td>
            <td>和上一个方法类似，只不过强制要求 HttpSession 中拥有指定的属性，否则抛出异常；</td>
        </tr>
        <tr>
            <td><code>String getSessionId(HttpServletRequest request) </code></td>
            <td>获取 Session ID 的值；</td>
        </tr>
        <tr>
            <td><code>void exposeRequestAttributes(ServletRequest request, Map attributes)</code> </td>
            <td>将 Map 元素添加到 ServletRequest 的属性列表中，当请求被导向（forward）到下一个处理程序时，这些请求属性就可以被访问到了；</td>
        </tr>
    </tbody>
</table>
<p>此外，WebUtils还提供了一些和ServletContext相关的方便方法：</p>
<table class="data-table-1" border="0" cellspacing="0" summary="" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <th>方法</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><code>String getRealPath(ServletContext servletContext, String path)</code> </td>
            <td>获取相对路径对应文件系统的真实文件路径；</td>
        </tr>
        <tr>
            <td><code>File getTempDir(ServletContext servletContext) </code></td>
            <td>获取 ServletContex 对应的临时文件地址，它以 File 对象的形式返回。</td>
        </tr>
    </tbody>
</table>
<p>下面的片断演示了使用 WebUtils 从 HttpSession 中获取属性对象的操作：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">protected Object formBackingObject(HttpServletRequest request) throws Exception {
            UserSession userSession = (UserSession) WebUtils.getSessionAttribute(request,
            "userSession");
            if (userSession != null) {
            return new AccountForm(this.petStore.getAccount(
            userSession.getAccount().getUsername()));
            } else {
            return new AccountForm();
            }
            }
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p><a name="N10257"><span class="smalltitle">Spring 所提供的过滤器和监听器</span></a></p>
<p>Spring 为 Web 应用提供了几个过滤器和监听器，在适合的时间使用它们，可以解决一些常见的 Web 应用问题。</p>
<p><strong>延迟加载过滤器</strong> </p>
<p>Hibernate 允许对关联对象、属性进行延迟加载，但是必须保证延迟加载的操作限于同一个 Hibernate Session 范围之内进行。如果 Service 层返回一个启用了延迟加载功能的领域对象给 Web 层，当 Web 层访问到那些需要延迟加载的数据时，由于加载领域对象的 Hibernate Session 已经关闭，这些导致延迟加载数据的访问异常。</p>
<p>Spring 为此专门提供了一个 OpenSessionInViewFilter 过滤器，它的主要功能是使每个请求过程绑定一个 Hibernate Session，即使最初的事务已经完成了，也可以在 Web 层进行延迟加载的操作。</p>
<p>OpenSessionInViewFilter 过滤器将 Hibernate Session 绑定到请求线程中，它将自动被 Spring 的事务管理器探测到。所以 OpenSessionInViewFilter 适用于 Service 层使用HibernateTransactionManager 或 JtaTransactionManager 进行事务管理的环境，也可以用于非事务只读的数据操作中。</p>
<p>要启用这个过滤器，必须在 web.xml 中对此进行配置：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&#8230;
            &lt;filter&gt;
            &lt;filter-name&gt;hibernateFilter&lt;/filter-name&gt;
            &lt;filter-class&gt;
            org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
            &lt;/filter-class&gt;
            &lt;/filter&gt;
            &lt;filter-mapping&gt;
            &lt;filter-name&gt;hibernateFilter&lt;/filter-name&gt;
            &lt;url-pattern&gt;*.html&lt;/url-pattern&gt;
            &lt;/filter-mapping&gt;
            &#8230;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
上面的配置，我们假设使用 .html 的后缀作为 Web 框架的 URL 匹配模式，如果您使用 Struts 等 Web 框架，可以将其改为对应的&#8220;*.do&#8221;模型。
<p><strong>中文乱码过滤器</strong> </p>
<p>在您通过表单向服务器提交数据时，一个经典的问题就是中文乱码问题。虽然我们所有的 JSP 文件和页面编码格式都采用 UTF-8，但这个问题还是会出现。解决的办法很简单，我们只需要在 web.xml 中配置一个 Spring 的编码转换过滤器就可以了：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&lt;web-app&gt;
            &lt;!---listener的配置--&gt;
            &lt;filter&gt;
            &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;
            &lt;filter-class&gt;
            org.springframework.web.filter.CharacterEncodingFilter ① Spring 编辑过滤器
            &lt;/filter-class&gt;
            &lt;init-param&gt; ② 编码方式
            &lt;param-name&gt;encoding&lt;/param-name&gt;
            &lt;param-value&gt;UTF-8&lt;/param-value&gt;
            &lt;/init-param&gt;
            &lt;init-param&gt; ③ 强制进行编码转换
            &lt;param-name&gt;forceEncoding&lt;/param-name&gt;
            &lt;param-value&gt;true&lt;/param-value&gt;
            &lt;/init-param&gt;
            &lt;/filter&gt;
            &lt;filter-mapping&gt; ② 过滤器的匹配 URL
            &lt;filter-name&gt;encodingFilter&lt;/filter-name&gt;
            &lt;url-pattern&gt;*.html&lt;/url-pattern&gt;
            &lt;/filter-mapping&gt;
            &lt;!---servlet的配置--&gt;
            &lt;/web-app&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>这样所有以 .html 为后缀的 URL 请求的数据都会被转码为 UTF-8 编码格式，表单中文乱码的问题就可以解决了。</p>
<p><strong>请求跟踪日志过滤器</strong> </p>
<p>除了以上两个常用的过滤器外，还有两个在程序调试时可能会用到的请求日志跟踪过滤器，它们会将请求的一些重要信息记录到日志中，方便程序的调试。这两个日志过滤器只有在日志级别为 DEBUG 时才会起作用：</p>
<table class="data-table-1" border="0" cellspacing="0" summary="" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <th>方法</th>
            <th>说明</th>
        </tr>
        <tr>
            <td><code>org.springframework.web.filter.ServletContextRequestLoggingFilter</code> </td>
            <td>该过滤器将请求的 URI 记录到 Common 日志中（如通过 Log4J 指定的日志文件）；</td>
        </tr>
        <tr>
            <td><code>org.springframework.web.filter.ServletContextRequestLoggingFilter</code> </td>
            <td>该过滤器将请求的 URI 记录到 ServletContext 日志中。</td>
        </tr>
    </tbody>
</table>
<p>以下是日志过滤器记录的请求跟踪日志的片断：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">(JspServlet.java:224) -     JspEngine --&gt; /htmlTest.jsp
            (JspServlet.java:225) - 	     ServletPath: /htmlTest.jsp
            (JspServlet.java:226) - 	        PathInfo: null
            (JspServlet.java:227) - 	        RealPath: D:\masterSpring\chapter23\webapp\htmlTest.jsp
            <!-- code sample is too wide -->(JspServlet.java:228) - 	      RequestURI: /baobaotao/htmlTest.jsp
            &#8230;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>通过这个请求跟踪日志，程度调试者可以详细地查看到有哪些请求被调用，请求的参数是什么，请求是否正确返回等信息。虽然这两个请求跟踪日志过滤器一般在程序调试时使用，但是即使程序部署不将其从 web.xml 中移除也不会有大碍，因为只要将日志级别设置为 DEBUG 以上级别，它们就不会输出请求跟踪日志信息了。</p>
<p><strong>转存 Web 应用根目录监听器和 Log4J 监听器</strong> </p>
<p>Spring 在 org.springframework.web.util 包中提供了几个特殊用途的 Servlet 监听器，正确地使用它们可以完成一些特定需求的功能。比如某些第三方工具支持通过 ${key} 的方式引用系统参数（即可以通过 System.getProperty() 获取的属性），WebAppRootListener 可以将 Web 应用根目录添加到系统参数中，对应的属性名可以通过名为&#8220;webAppRootKey&#8221;的 Servlet 上下文参数指定，默认为&#8220;webapp.root&#8221;。下面是该监听器的具体的配置：</p>
<br />
<a name="list6"><strong>清单 6. WebAppRootListener 监听器配置</strong></a><br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&#8230;
            &lt;context-param&gt;
            &lt;param-name&gt;webAppRootKey&lt;/param-name&gt;
            &lt;param-value&gt;baobaotao.root&lt;/param-value&gt; ① Web 应用根目录以该属性名添加到系统参数中
            &lt;/context-param&gt;
            &#8230;
            ② 负责将 Web 应用根目录以 webAppRootKey 上下文参数指定的属性名添加到系统参数中
            &lt;listener&gt;
            &lt;listener-class&gt;
            org.springframework.web.util.WebAppRootListener
            &lt;/listener-class&gt;
            &lt;/listener&gt;
            &#8230;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>这样，您就可以在程序中通过 System.getProperty("baobaotao.root") 获取 Web 应用的根目录了。不过更常见的使用场景是在第三方工具的配置文件中通过${baobaotao.root} 引用 Web 应用的根目录。比如以下的 log4j.properties 配置文件就通过 ${baobaotao.root} 设置了日志文件的地址：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">log4j.rootLogger=INFO,R
            log4j.appender.R=org.apache.log4j.RollingFileAppender
            log4j.appender.R.File=${baobaotao.root}/WEB-INF/logs/log4j.log ① 指定日志文件的地址
            log4j.appender.R.MaxFileSize=100KB
            log4j.appender.R.MaxBackupIndex=1
            log4j.appender.R.layout.ConversionPattern=%d %5p [%t] (%F:%L) - %m%n
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<p>另一个专门用于 Log4J 的监听器是 Log4jConfigListener。一般情况下，您必须将 Log4J 日志配置文件以 log4j.properties 为文件名并保存在类路径下。Log4jConfigListener 允许您通过 log4jConfigLocation Servlet 上下文参数显式指定 Log4J 配置文件的地址，如下所示：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">① 指定 Log4J 配置文件的地址
            &lt;context-param&gt;
            &lt;param-name&gt;log4jConfigLocation&lt;/param-name&gt;
            &lt;param-value&gt;/WEB-INF/log4j.properties&lt;/param-value&gt;
            &lt;/context-param&gt;
            &#8230;
            ② 使用该监听器初始化 Log4J 日志引擎
            &lt;listener&gt;
            &lt;listener-class&gt;
            org.springframework.web.util.Log4jConfigListener
            &lt;/listener-class&gt;
            &lt;/listener&gt;
            &#8230;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<table border="0" cellspacing="0" cellpadding="0" width="40%" align="right">
    <tbody>
        <tr>
            <td width="10"><img alt="" src="//www.ibm.com/i/c.gif" width="10" height="1" /></td>
            <td>
            <table border="1" cellspacing="0" cellpadding="5" width="100%">
                <tbody>
                    <tr>
                        <td bgcolor="#eeeeee"><a name="N102E7"><strong>提示</strong></a><br />
                        <p>一些Web应用服务器（如 Tomcat）不会为不同的Web应用使用独立的系统参数，也就是说，应用服务器上所有的 Web 应用都共享同一个系统参数对象。这时，您必须通过webAppRootKey 上下文参数为不同Web应用指定不同的属性名：如第一个 Web 应用使用 webapp1.root 而第二个 Web 应用使用 webapp2.root 等，这样才不会发生后者覆盖前者的问题。此外，WebAppRootListener 和 Log4jConfigListener 都只能应用在 Web 应用部署后 WAR 文件会解包的 Web 应用服务器上。一些 Web 应用服务器不会将Web 应用的 WAR 文件解包，整个 Web 应用以一个 WAR 包的方式存在（如 Weblogic），此时因为无法指定对应文件系统的 Web 应用根目录，使用这两个监听器将会发生问题。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<p>Log4jConfigListener 监听器包括了 WebAppRootListener 的功能，也就是说，Log4jConfigListener 会自动完成将 Web 应用根目录以 webAppRootKey 上下文参数指定的属性名添加到系统参数中，所以当您使用 Log4jConfigListener 后，就没有必须再使用 WebAppRootListener了。</p>
<p><strong>Introspector 缓存清除监听器</strong> </p>
<p>Spring 还提供了一个名为 org.springframework.web.util.IntrospectorCleanupListener 的监听器。它主要负责处理由 JavaBean Introspector 功能而引起的缓存泄露。IntrospectorCleanupListener 监听器在 Web 应用关闭的时会负责清除 JavaBean Introspector 的缓存，在 web.xml 中注册这个监听器可以保证在 Web 应用关闭的时候释放与其相关的 ClassLoader 的缓存和类引用。如果您使用了 JavaBean Introspector 分析应用中的类，Introspector 缓存会保留这些类的引用，结果在应用关闭的时候，这些类以及Web 应用相关的 ClassLoader 不能被垃圾回收。不幸的是，清除 Introspector 的唯一方式是刷新整个缓存，这是因为没法准确判断哪些是属于本 Web 应用的引用对象，哪些是属于其它 Web 应用的引用对象。所以删除被缓存的 Introspection 会导致将整个 JVM 所有应用的 Introspection 都删掉。需要注意的是，Spring 托管的 Bean 不需要使用这个监听器，因为 Spring 的 Introspection 所使用的缓存在分析完一个类之后会马上从 javaBean Introspector 缓存中清除掉，并将缓存保存在应用程序特定的 ClassLoader 中，所以它们一般不会导致内存资源泄露。但是一些类库和框架往往会产生这个问题。例如 Struts 和 Quartz 的 Introspector 的内存泄漏会导致整个的 Web 应用的 ClassLoader 不能进行垃圾回收。在 Web 应用关闭之后，您还会看到此应用的所有静态类引用，这个错误当然不是由这个类自身引起的。解决这个问题的方法很简单，您仅需在 web.xml 中配置 IntrospectorCleanupListener 监听器就可以了：</p>
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td class="code-outline">
            <pre class="displaycode">&lt;listener&gt;
            &lt;listener-class&gt;
            org.springframework.web.util.IntrospectorCleanupListener
            &lt;/listener-class&gt;
            &lt;/listener&gt;
            </pre>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<table border="0" cellspacing="0" cellpadding="0" width="100%">
    <tbody>
        <tr>
            <td><img alt="" src="//www.ibm.com/i/v14/rules/blue_rule.gif" width="100%" height="1" /><br />
            <img border="0" alt="" src="//www.ibm.com/i/c.gif" width="8" height="6" /></td>
        </tr>
    </tbody>
</table>
<table class="no-print" cellspacing="0" cellpadding="0" align="right">
    <tbody>
        <tr align="right">
            <td><img alt="" src="//www.ibm.com/i/c.gif" width="100%" height="4" /><br />
            <table border="0" cellspacing="0" cellpadding="0">
                <tbody>
                    <tr>
                        <td valign="middle"><img border="0" alt="" src="//www.ibm.com/i/v14/icons/u_bold.gif" width="16" height="16" /><br />
                        </td>
                        <td valign="top" align="right"><a class="fbox" href="#main" cmimpressionsent="1"><strong>回页首</strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br />
<br />
<p><a name="N10300"><span class="atitle">小结</span></a></p>
<p>本文介绍了一些常用的 Spring 工具类，其中大部分 Spring 工具类不但可以在基于 Spring 的应用中使用，还可以在其它的应用中使用。使用 JDK 的文件操作类在访问类路径相关、Web 上下文相关的文件资源时，往往显得拖泥带水、拐弯抹角，Spring 的 Resource 实现类使这些工作变得轻松了许多。</p>
<p>在 Web 应用中，有时你希望直接访问 Spring 容器，获取容器中的 Bean，这时使用 WebApplicationContextUtils 工具类从 ServletContext 中获取 WebApplicationContext 是非常方便的。WebUtils 为访问 Servlet API 提供了一套便捷的代理方法，您可以通过 WebUtils 更好的访问 HttpSession 或 ServletContext 的信息。</p>
<p>Spring 提供了几个 Servlet 过滤器和监听器，其中 ServletContextRequestLoggingFilter 和 ServletContextRequestLoggingFilter 可以记录请求访问的跟踪日志，你可以在程序调试时使用它们获取请求调用的详细信息。WebAppRootListener 可以将 Web 应用的根目录以特定属性名添加到系统参数中，以便第三方工具类通过 ${key} 的方式进行访问。Log4jConfigListener 允许你指定 Log4J 日志配置文件的地址，且可以在配置文件中通过 ${key} 的方式引用 Web 应用根目录，如果你需要在 Web 应用相关的目录创建日志文件，使用 Log4jConfigListener 可以很容易地达到这一目标。</p>
<p>Web 应用的内存泄漏是最让开发者头疼的问题，虽然不正确的程序编写可能是这一问题的根源，也有可能是一些第三方框架的 JavaBean Introspector 缓存得不到清除而导致的，Spring 专门为解决这一问题配备了 IntrospectorCleanupListener 监听器，它只要简单在 web.xml 中声明该监听器就可以了。</p>
  <img src ="http://www.blogjava.net/lanxin1020/aggbug/263961.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-04-05 08:44 <a href="http://www.blogjava.net/lanxin1020/archive/2009/04/05/263961.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>配置Spring数据源 (转)</title><link>http://www.blogjava.net/lanxin1020/archive/2009/03/25/261822.html</link><dc:creator>lanxin1020</dc:creator><author>lanxin1020</author><pubDate>Wed, 25 Mar 2009 03:14:00 GMT</pubDate><guid>http://www.blogjava.net/lanxin1020/archive/2009/03/25/261822.html</guid><wfw:comment>http://www.blogjava.net/lanxin1020/comments/261822.html</wfw:comment><comments>http://www.blogjava.net/lanxin1020/archive/2009/03/25/261822.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/lanxin1020/comments/commentRss/261822.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/lanxin1020/services/trackbacks/261822.html</trackback:ping><description><![CDATA[<h3 class="type_reprint" title="转载"><a href="http://yangzb.javaeye.com/blog/351387">配置Spring数据源 </a></h3>
<div class="blog_content">
<div>配置一个数据源&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Spring在第三方依赖包中包含了两个数据源的实现类包，其一是Apache的DBCP，其二是&nbsp;C3P0。可以在Spring配置文件中利用这两者中任何一个配置数据源。</div>
<div>DBCP数据源&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;DBCP类包位于&nbsp;/lib/jakarta-commons/commons-dbcp.jar，DBCP 是一个依赖&nbsp;Jakarta&nbsp;commons-pool对象池机制的数据库连接池，所以在类路径下还必须包括/lib/jakarta-&nbsp;commons /commons-pool.jar。下面是使用DBCP配置MySql数据源的配置片断：</div>
<p>xml&nbsp;代码<br />
&lt;bean&nbsp;id="dataSource"&nbsp;class="<span class="hilite2">org</span>.apache.commons.dbcp.BasicDataSource"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;destroy-method="close"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="driverClassName"&nbsp;value="com.mysql.jdbc.Driver"&nbsp;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="url"&nbsp;value="jdbc:mysql://localhost:3309/sampledb"&nbsp;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="username"&nbsp;value="root"&nbsp;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="password"&nbsp;value="1234"&nbsp;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
bean&gt;&nbsp;&nbsp;&nbsp;<br />
BasicDataSource提供了close()方法关闭数据源，所以必须设定destroy-method=&#8221;close&#8221;属性，&nbsp;以便Spring容器关闭时，数据源能够正常关闭。除以上必须的数据源属性外，还有一些常用的属性：&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;defaultAutoCommit：设置从数据源中返回的连接是否采用自动提交机制，默认值为&nbsp;true；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;defaultReadOnly：设置数据源是否仅能执行只读操作，&nbsp;默认值为&nbsp;false；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;maxActive：最大连接数据库连接数，设置为0时，表示没有限制；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;maxIdle：最大等待连接中的数量，设置为0时，表示没有限制；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;maxWait：最大等待秒数，单位为毫秒，&nbsp;超过时间会报出错误信息；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;validationQuery：用于验证连接是否成功的查询SQL语句，SQL语句必须至少要返回一行数据，&nbsp;如你可以简单地设置为：&#8220;select&nbsp;count(*)&nbsp;from&nbsp;user&#8221;；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;removeAbandoned：是否自我中断，默认是&nbsp;false&nbsp;；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;removeAbandonedTimeout：几秒后数据连接会自动断开，在removeAbandoned为true，提供该值；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;logAbandoned：是否记录中断事件，&nbsp;默认为&nbsp;false；&nbsp;<br />
<br />
C3P0数据源&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;C3P0 是一个开放源代码的JDBC数据源实现项目，它在lib目录中与Hibernate一起发布，实现了JDBC3和JDBC2扩展规范说明的&nbsp;Connection&nbsp;和Statement&nbsp;池。C3P0类包位于/lib/c3p0/c3p0-0.9.0.4.jar。下面是使用C3P0配置一个&nbsp;oracle数据源：<br />
<br />
xml&nbsp;代码<br />
&lt;bean&nbsp;id="dataSource"&nbsp;class="com.mchange.v2.c3p0.ComboPooledDataSource"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;destroy-method="close"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="driverClass"&nbsp;value="&nbsp;oracle.jdbc.driver.OracleDriver&nbsp;"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="jdbcUrl"&nbsp;value="&nbsp;jdbc:oracle:thin:@localhost:1521:ora9i&nbsp;"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="user"&nbsp;value="admin"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="password"&nbsp;value="1234"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
bean&gt;&nbsp;&nbsp;&nbsp;<br />
<br />
ComboPooledDataSource和BasicDataSource一样提供了一个用于关闭数据源的close()方法，这样我们就可以保证Spring容器关闭时数据源能够成功释放。&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;C3P0拥有比DBCP更丰富的配置属性，通过这些属性，可以对数据源进行各种有效的控制：&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;acquireIncrement：当连接池中的连接用完时，C3P0一次性创建新连接的数目；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;acquireRetryAttempts：定义在从数据库获取新连接失败后重复尝试获取的次数，默认为30；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;acquireRetryDelay：两次连接中间隔时间，单位毫秒，默认为1000；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;autoCommitOnClose：连接关闭时默认将所有未提交的操作回滚。默认为false；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;automaticTestTable：&nbsp;C3P0 将建一张名为Test的空表，并使用其自带的查询语句进行测试。如果定义了这个参数，那么属性preferredTestQuery将被忽略。你&nbsp;不能在这张Test表上进行任何操作，它将中为C3P0测试所用，默认为null；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;breakAfterAcquireFailure：获取连接失败将会引起所有等待获取连接的线程抛出异常。但是数据源仍有效保留，并在下次调&nbsp;&nbsp;&nbsp;用getConnection()的时候继续尝试获取连接。如果设为true，那么在尝试获取连接失败后该数据源将申明已断开并永久关闭。默认为&nbsp;false；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;checkoutTimeout：当连接池用完时客户端调用getConnection()后等待获取新连接的时间，超时后将抛出SQLException，如设为0则无限期等待。单位毫秒，默认为0；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;connectionTesterClassName：&nbsp;通过实现ConnectionTester或QueryConnectionTester的类来测试连接，类名需设置为全限定名。默认为&nbsp;com.mchange.v2.C3P0.impl.DefaultConnectionTester；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;idleConnectionTestPeriod：隔多少秒检查所有连接池中的空闲连接，默认为0表示不检查；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;initialPoolSize：初始化时创建的连接数，应在minPoolSize与maxPoolSize之间取值。默认为3；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;maxIdleTime：最大空闲时间，超过空闲时间的连接将被丢弃。为0或负数则永不丢弃。默认为0；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;maxPoolSize：连接池中保留的最大连接数。默认为15；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;maxStatements：JDBC 的标准参数，用以控制数据源内加载的PreparedStatement数量。但由于预缓存的Statement属&nbsp;于单个Connection而不是整个连接池。所以设置这个参数需要考虑到多方面的因素，如果maxStatements与&nbsp;maxStatementsPerConnection均为0，则缓存被关闭。默认为0；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;maxStatementsPerConnection：连接池内单个连接所拥有的最大缓存Statement数。默认为0；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;numHelperThreads：C3P0是异步操作的，缓慢的JDBC操作通过帮助进程完成。扩展这些操作可以有效的提升性能，通过多线程实现多个操作同时被执行。默认为3；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;preferredTestQuery：定义所有连接测试都执行的测试语句。在使用连接测试的情况下这个参数能显著提高测试速度。测试的表必须在初始数据源的时候就存在。默认为null；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;propertyCycle：&nbsp;用户修改系统配置参数执行前最多等待的秒数。默认为300；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;testConnectionOnCheckout：因性能消耗大请只在需要的时候使用它。如果设为true那么在每个connection提交的时候都&nbsp;将校验其有效性。建议使用 idleConnectionTestPeriod或automaticTestTable&nbsp;<br />
等方法来提升连接测试的性能。默认为false；&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;testConnectionOnCheckin：如果设为true那么在取得连接的同时将校验连接的有效性。默认为false。&nbsp;<br />
<br />
读配置文件的方式引用属性：&nbsp;<br />
<br />
&lt;bean&nbsp;id="propertyConfigurer"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;class="<span class="hilite2">org</span>.<span class="hilite3">springframework</span>.beans.factory.config.PropertyPlaceholderConfigurer"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="location"&nbsp;value="/WEB-INF/jdbc.properties"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
bean&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&lt;bean&nbsp;id="dataSource"&nbsp;class="<span class="hilite2">org</span>.apache.commons.dbcp.BasicDataSource"&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;destroy-method="close"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="driverClassName"&nbsp;value="${jdbc.driverClassName}"&nbsp;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="url"&nbsp;value="${jdbc.url}"&nbsp;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="username"&nbsp;value="${jdbc.username}"&nbsp;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="password"&nbsp;value="${jdbc.password}"&nbsp;/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
bean&gt;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;在jdbc.properties属性文件中定义属性值：&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;jdbc.driverClassName=&nbsp;com.mysql.jdbc.Driver&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;jdbc.url=&nbsp;jdbc:mysql://localhost:3309/sampledb&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;jdbc.username=root&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;jdbc.password=1234&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;提示&nbsp;经常有开发者在${xxx}的前后不小心键入一些空格，这些空格字符将和变量合并后作为属性的值。如：&nbsp;的属性配置项，在前后都有空格，被解析后，username的值为&#8220;&nbsp;1234&nbsp;&#8221;，这将造成最终的错误，因此需要特别小心。<br />
<br />
&nbsp;获取<span class="hilite4">JNDI</span>数据源&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;如果应用配置在高性能的应用服务器（如WebLogic或Websphere等）上，我们可能更希望使用应用服务器本身提供的数据源。应用服务器的数据源&nbsp;使用<span class="hilite4">JNDI</span>开放调用者使用，Spring为此专门提供引用<span class="hilite4">JNDI</span>资源的<span class="hilite5">JndiObjectFactoryBean</span>类。下面是一个简单的配置：<br />
<br />
xml&nbsp;代码<br />
&lt;bean&nbsp;id="dataSource"&nbsp;class="<span class="hilite2">org</span>.<span class="hilite3">springframework</span>.<span class="hilite4">jndi</span>.<span class="hilite5">JndiObjectFactoryBean</span>"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="jndiName"&nbsp;value="java:comp/env/jdbc/bbt"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
bean&gt;&nbsp;&nbsp;&nbsp;<br />
<br />
通过jndiName指定引用的<span class="hilite4">JNDI</span>数据源名称。&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Spring&nbsp;2.0为获取J2EE资源提供了一个jee命名空间，通过jee命名空间，可以有效地简化J2EE资源的引用。下面是使用jee命名空间引用<span class="hilite4">JNDI</span>数据源的配置：&nbsp;<br />
<br />
xml&nbsp;代码<br />
&lt;beans&nbsp;xmlns=http://www.<span class="hilite3">springframework</span>.<span class="hilite2">org</span>/schema/beans&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
xmlns:xsi=http://www.w3.<span class="hilite2">org</span>/2001/XMLSchema-instance&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
xmlns:jee=http://www.<span class="hilite3">springframework</span>.<span class="hilite2">org</span>/schema/jee&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
xsi:schemaLocation="<a href="http://www.springframework.org/schema/beans" target="_blank">http://www.<span class="hilite3">springframework</span>.<span class="hilite2">org</span>/schema/beans</a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<a href="http://www.springframework.org/schema/beans/spring-beans-2.0.xsd" target="_blank">http://www.<span class="hilite3">springframework</span>.<span class="hilite2">org</span>/schema/beans/spring-beans-2.0.xsd</a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<a href="http://www.springframework.org/schema/jee" target="_blank">http://www.<span class="hilite3">springframework</span>.<span class="hilite2">org</span>/schema/jee</a> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
<a href="http://www.springframework.org/schema/jee" target="_blank">http://www.<span class="hilite3">springframework</span>.<span class="hilite2">org</span>/schema/jee</a> /spring-jee-2.0.xsd"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&lt;jee:<span class="hilite4">jndi</span>-lookup&nbsp;id="dataSource"&nbsp;<span class="hilite4">jndi</span>-name="&nbsp;java:comp/env/jdbc/bbt"/&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
beans&gt;&nbsp;&nbsp;&nbsp;<br />
<br />
Spring的数据源实现类&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;Spring 本身也提供了一个简单的数据源实现类DriverManagerDataSource&nbsp;，它位于 <span class="hilite2">org</span>.<span class="hilite3">springframework</span>.jdbc.datasource包中。这个类实现了javax.sql.DataSource接口，但&nbsp;它并没有提供池化连接的机制，每次调用getConnection()获取新连接时，只是简单地创建一个新的连接。因此，这个数据源类比较适合在单元测试&nbsp;或简单的独立应用中使用，因为它不需要额外的依赖类。&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;下面，我们来看一下DriverManagerDataSource的简单使用：当然，我们也可以通过配置的方式直接使用DriverManagerDataSource。<br />
<br />
java&nbsp;代码<br />
DriverManagerDataSource&nbsp;ds&nbsp;=&nbsp;new&nbsp;DriverManagerDataSource&nbsp;();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
ds.setDriverClassName("com.mysql.jdbc.Driver");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
ds.setUrl("jdbc:mysql://localhost:3309/sampledb");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
ds.setUsername("root");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
ds.setPassword("1234");&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
Connection&nbsp;actualCon&nbsp;=&nbsp;ds.getConnection();&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;<br />
<br />
小结&nbsp;<br />
<br />
&nbsp;&nbsp;&nbsp;&nbsp;不管采用何种持久化技术，都需要定义数据源。Spring附带了两个数据源的实现类包，你可以自行选择进行定义。在实际部署时，我们可能会直接采用应用服&nbsp;务器本身提供的数据源，这时，则可以通过<span class="hilite5">JndiObjectFactoryBean</span>或jee命名空间引用<span class="hilite4">JNDI</span>中的数据源。&nbsp;<br />
<br />
DBCP与C3PO配置的区别：<br />
<br />
C3PO&nbsp;：<br />
<br />
xml&nbsp;代码<br />
<br />
&lt;bean&nbsp;id="dataSource"&nbsp;class="com.mchange.v2.c3p0.ComboPooledDataSource"&nbsp;destroy-method="close"&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="driverClass"&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;oracle.jdbc.driver.Oracle&lt;/Drivervalue&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; &lt;/ property&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="jdbcUrl"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;jdbc:oracle:thin:@10.10.10.6:1521:DataBaseNamevalue&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; &lt;/ property&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="user"&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;testAdmin&lt;/value&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp; &lt;/ property&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="password"&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;123456&lt;/value&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp;<br />
&lt;/bean&gt;&nbsp;&nbsp;&nbsp;<br />
<br />
&nbsp;DBCP：<br />
<br />
xml&nbsp;代码<br />
&lt;bean&nbsp;id="dataSource"&nbsp;class="<span class="hilite2">org</span>.apache.commons.dbcp.BasicDataSource"&nbsp;destroy-method="close"&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="driverClassName"&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;oracle.jdbc.driver.OracleDriver&lt;/value&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="url"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;jdbc:oracle:thin:@10.10.10.6:1521:DataBaseNamevalue&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="username"&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;testAdmin&lt;/value&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&lt;property&nbsp;name="password"&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&lt;value&gt;123456&lt;/value&gt;&nbsp;&nbsp;&nbsp;<br />
&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp;<br />
&lt;/bean&gt;</p>
</div>
 <img src ="http://www.blogjava.net/lanxin1020/aggbug/261822.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/lanxin1020/" target="_blank">lanxin1020</a> 2009-03-25 11:14 <a href="http://www.blogjava.net/lanxin1020/archive/2009/03/25/261822.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>