<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>Lincoln</title>
    <description></description>
    <link>http://lincoln.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>Spring2.0JINDI集成（3）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/88207" style="color:red;">http://lincoln.javaeye.com/blog/88207</a>&nbsp;
          发表时间: 2007年06月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Spring2.0引入的jndi-lookup内容模式<br /><br />  自从Spring2.0开始，便引入了XML Schema来管理BeanFactory、ApplicationContext的配置。无论是语义，还是表达、复用能力，XML Schema都强于DTD。使用XML Schema定义新的数据类型非常方便，当然，要掌握XML Schema还是要费一番功夫的。在RDBMS领域，存在很多O/R Mapping技术，比如Hibernate、JPA、TopLink、Kodo JDO等。在XML领域，也存在很多O/X Mapping技术，比如XML Beans、JAXB、Castor等。借助于它们，应用操作XML文档会很方便。<br /><br />  Spring引入的jee内容模式中含有jndi-lookup数据类型，使用它能够大大简化JNDI的配置。开发者只需要调整Spring XML配置文件，而应用本身不需要做任何修改。相应的内容模式如下，它定义了jndi-lookup的结构和语义。<br /><br />Jndi-name: 待查找的JNDI名，必填项<br />Resource-ref:是否是资源引用，可选项。如果是，则需要在web.xml和厂商特定的部署描述符中进行映射。默认取值为false。<br />Cache:是否缓存查找到的JNDI对象，可选项。默认取值为true<br />Expected-type:期待的JNDI对象类型，可选项<br />Lookup-on-startup:是否在启动时去JNDI上查找对象，可选项。默认取值为true<br />Proxy-interface:代理接口，可选项。配合cache和lookup-on-startup使用<br />Environment:JNDI环境信息，可选项。如果有多项值对需要给出，则要遵循属性文件的格式。<br /><br />  接下来，我们将重点放在DTD（Spring1.x）到XML Schema(Spring2.0)的移植上。开发者只需要将jee命名空间(spring-jee-2.0.xsd)导入到spring配置文件中，便可以使用到它，具体如下。<br /><pre name="code" class="java">&lt;beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:jee="http://www.springframework.org/schema/jee"
       xsi:schemaLocation=
       		"http://www.springframework.org/schema/beans 
       			http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
			  http://www.springframework.org/schema/jee 
			    http://www.springframework.org/schema/jee/spring-jee-2.0.xsd"></pre><br /><br />  研究这一新特性，示例配置如下，开发者再也看不到JndiObjectFactoryBean、JndiObjectTargetSource的影子，而且配置清晰。<br /><pre name="code" class="java">	&lt;jee:jndi-lookup id="tq" jndi-name="queue/testQueue"
			cache="false" 
			expected-type="javax.jms.Queue" 
			lookup-on-startup="true" 
			proxy-interface="javax.jms.Queue"
			resource-ref="false">
			&lt;jee:environment >
				java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
				java.naming.provider.url=jnp://localhost:1099
				java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
			&lt;/jee:environment>
	&lt;/jee:jndi-lookup></pre>由于该示例运行在web容器之外，因此配置看起来稍微有一点复杂。如果应用运行在web容器之中，而且与本地JNDI服务交互，则不用提供environment属性。下面给出的两个版本都是合法的。<br /><pre name="code" class="java">
&lt;jee:jndi-lookup id="tq" jndi-name="queue/testQueue"/></pre><br /><pre name="code" class="java">&lt;jee:jndi-lookup id="tq" jndi-name="queue/testQueue"
			cache="false" 
			expected-type="javax.jms.Queue" 
			lookup-on-startup="true" 
			proxy-interface="javax.jms.Queue"
			resource-ref="false"/></pre>最后要指出的是Spring2.0兼容Spring1.x的所有配置。
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/88207#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 08 Jun 2007 19:53:31 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/88207</link>
        <guid>http://lincoln.javaeye.com/blog/88207</guid>
      </item>
      <item>
        <title>Spring2.0JINDI集成（2）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/88205" style="color:red;">http://lincoln.javaeye.com/blog/88205</a>&nbsp;
          发表时间: 2007年06月08日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          为了简化 JNDI API 的使用，Spring 2.0框架专门提供了org.springframework.jndi包。其中，JndiTemplateEditor类继承于PropertyEditorSupport类，供实现JndiTemplate对象的属性编辑器使用，即开发者能够通过字符串方式实现对JndiTemplate值得设置(或者在IDE中编辑它)。<br /><br />通常，开发者可以通过如下4种方式对JNDI资源进行CRUD操作。<br /><br />1．  单独使用JndiObjectFactroyBean:这种方式最常见(Spring1.x)。这是负责查找JNDI对象的FactroyBean。<br /><br />2．  同时使用JndiObjectTargetSource和ProxyFactoryBean：这种组合使用较少，因为这同上述单独使用JndiObjectFactoryBean的效果一样，而且在配置上也没有占有优势。<br /><br />3．  使用JndiTemplate:辅助类，类似于JdbcTemplate，以简化对JNDI的CRUD操作。如果用户需要进行高级自定义工作，则还可以使用JndiCallback回调接口。<br /><br />4．  Spring2.0引入的&lt;jndi-lookup/>内容模式。<br /><br />单独使用JndiObjectFactoryBean<br /> <br />  为了展示JndiObjectFactoryBean的功力，我们来使用JBoss应用服务器提供的JNDI服务。通过JBoss的JMX控制台应用，开发者能够获得全局JNDI名。<br /><br />我们以消息队列“queue/testQueue”为例，并结合Jbossjndidemo项目进行阐述。<br /><br /><pre name="code" class="java">    &lt;bean id="tq"

       class="org.springframework.jndi.JndiObjectFactoryBean">

       &lt;!--  指定JNDI模板 -->

       &lt;property name="jndiTemplate" ref="jndiTemplate" />

       &lt;!--  指定JNDI名字 -->

       &lt;property name="jndiName" value="queue/testQueue" />

       &lt;!--  是否缓存查找到的Java对象 -->

       &lt;property name="cache" value="true">&lt;/property>

       &lt;!--  启动时是否去JNDI树查找目标资源(即，由jndiName指定的) -->

       &lt;property name="lookupOnStartup" value="true">&lt;/property>

       &lt;!--  是否为资源引用 -->

       &lt;property name="resourceRef" value="false">&lt;/property>

    &lt;/bean>

 

    &lt;bean id="jndiTemplate"

       class="org.springframework.jndi.JndiTemplate">

       &lt;!--  具体的属性与厂商提供的产品有关 -->

       &lt;property name="environment">

           &lt;props>

              &lt;prop key="java.naming.factory.initial">

                  org.jnp.interfaces.NamingContextFactory

              &lt;/prop>

              &lt;prop key="java.naming.provider.url">

                  jnp://localhost:1099

              &lt;/prop>

              &lt;prop key="java.naming.factory.url.pkgs">

                  org.jboss.naming:org.jnp.interfaces

              &lt;/prop>

           &lt;/props>

       &lt;/property>

    &lt;/bean></pre>
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/88205#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 08 Jun 2007 19:51:25 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/88205</link>
        <guid>http://lincoln.javaeye.com/blog/88205</guid>
      </item>
      <item>
        <title>Spring2.0JNDI集成(1)</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/87712" style="color:red;">http://lincoln.javaeye.com/blog/87712</a>&nbsp;
          发表时间: 2007年06月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          JNDI是JAVAEE 平台技术中最重要的基础支撑技术之一。他为各种Java对象进行命名，并采用目录层次结构管理它们，借助于JNDI API能够操控各种JAVA对象。实际上，JAVAEE5 引入各种Annotation注释(比如，@EJB，@Resource)底层采用JNDI API查找 JavaEE 容器受管资源(比如，EJB3.0组件，JDBC DataSource，JMS ConnectionFactory),并注入到那些应用了Annotation注释的JavaEE组件 （比如，Servlet,JSF后端Bean,EJB3.0组件）中。<br />Spring2.0针对JNDI API的使用提供了JNDI集成，org.springframework.jndi包就是见证。在Spring 1.x使能应用中，JndiObjectFactoryBean是Spring开发者使用最频繁的工厂Bean，而JndiTemplate模板的使用也非常频繁。自从Spirng 2.0开始，&lt;jndi-lookup/>内容模式被引入到JNDI集成工作中。此后，&lt;jidi-lookup/>便代替了JnidObjectFactoryBean的地位，当然JndiObjectFactoryBean是&lt;jndi-lookup/>底层采用的工厂Bean。<br />本章从介绍JNDI背景知识及实例入手，进而进入到Spring提供的JNDI集成支持中。随后，Spring2.0引入的&lt;jndi-lookup/>内容模式被介绍到。<br /><br />背景知识及示例<br />  现有的企业当中，存在各式各样的命名和目录服务。何谓命名和目录服务？命名和目录服务是同过名字，目录形式来管理系统中的对象，无力设备等内容的一种服务。比如，微软活动目录(AD)、OpenDAP服务器。为了通过应用操纵它们，开发者需要借助于响应的客户端API或客户软件<br />  在JNDI出现之前，开发者只能够通过专有方式操作命名和目录服务，这无疑加大了开发者的学习负担。甚至，企业的实现环境都是异构的，哪怕是同一种目录服务器都会因为运行的操作系统的不同而提供多套客户端API。<br />  JNDI，即Java命名目录服务接口(Java Naming and Directory Interface)，它能够对命名和目录服务器进行CRUD操作，这同JDBC与RDBMS的关系类似。只要命名和目录服务器厂商提供对应的JNDI实现，开发者便能够一致地通过接口操纵它们，从而避免了与厂商绑定的风险。<br />  类似于DNS，JNDI能够讲对象赋予有意义的名字。在DNS中，通过逻辑能够实现对IP地址的映射。在JNDI中，通过预先绑定的名字能够找到目标服务或者对象。比如，借助于JNDI，我们能通过“java.:/AgileSpringDS”名字获得javax.sql.DataSource对象。这些都是通过Java命名系统获得的。JNDI在Java SE/Java EE中起到了很重要的作用，正如DNS在当今的因特网中的地位一样，而且会越来越重要。比如，Web服务的兴起、SOA架构的兴起，会使得JNDI在应用中扮演关键角色。<br />  通常，Java EE应用服务器（比如，GlassFish，JBoss）都会提供命名或目录服务实现(或称为服务提供者)。BEA WebLogic内部采用了LDAP服务器完成登录用户的认证。同样地，在Java SE环境开发中也可以使用本地JNDI服务提供者(而不是由Java EE服务器提供的)。通过http://java.sun.com/products/jidi/serviceproviders.html，开发者能够浏览到很多JNDI服务提供者。这里，我们以 File System提供者为例。如果开发者熟悉IBM WebSphere MQ,则应该知道他的JNDI实现这是在File System基础之上开发的。打开Eclipse filesystemjndidemo项目，来分析如下代码。<br /><br /><br /><pre name="code" class="java">Hashtable&lt;String,String> env = new Hashtable&lt;String,string>(2);
//设定上下文工厂
Env.put(Context.INITIAL_CONTEXT_FACTORY，”com.sun.jndi.fscontext.RefFSContextFactroy”);
//设置文件系统路径(注意，开发者需要依据自身的机器修改”file:d://eclipse”)
Env.put(Context.PROVIDER_URL，”file:d://eclipse”);

Context initCtx = null;
Try{
	//获得初始化上下文
	initCtx = new initialContext(env);
	
	//借助于JNDI，查找文件
	Object fc =initCtx.lookup(“eclipse.ini”);
	Log.info(fc);
}
Catch(NamingException ne){
//处理命名异常
Log.error(“NamingException”,ne);
}
Finally{
	Try{
		//关闭上下文
		initCtx.close();
}catch(NamingException ne){;}
}</pre><br /><br />上述代码先构造env，然后传给InitialContext.此时，”file:d://eclipse”成了JNDI树的根，eclipse目录下的所有文件和目录都成为了树的成员。在查找到eclipse.ini后，需要关闭相应的InitialContext。整个过程需要处理NamingException异常。<br />用户可以将应用对象存储在命名和目录服务提供者中，并供JNDI应用访问。比如，开发者可以将数据源存储在命名和目录服务器中；通过JNDI能够获得JTA事务管理其(存储在JavaEE 应用服务器提供的命名和目录服务器中)。<br />实例仅仅展示了JNDI的冰山一角。在实际企业应用中，JNDI的使用更多地发生于Web,EJB,Ear应用场合。比如，在JSF受管组件中访问EJB3.0组件，借助于JNDI访问JMS Topic及ConnectionFactory。在这些场合中，JNDI的使用非常复杂。如果直接通过JNDI API操控目标资源，开发者要处理很多与实际业务无关的开发内容，具体如下：第一，要负责InitialContext的创建、关闭工作;第二，要负责异常处理；第三，要考虑JNDI树上查找目标资源的实际；第四，查找到目标资源后，开发者还要考虑缓存策略(运行时是否需要重新去JNDI树上获取资源，否则他会持有过期资源)；第五，要区别对待开发时和生产环境的JNDI处理。考虑到这些对开发者的不利因素，Spring对JNDI提供了全方位的支持，这也是Spring驱动敏捷开发的又一举措。<br />通过将JNDI查找操作定义在Spring配置文件中，应用能够操作到相关的JNDI对象，开发者再也不用对JNDI进行硬编码。最为重要的一点是，应用对这些JNDI对象的依赖都是通过IoC容器注入的，因此开发者不用关注具体对象是从哪里来。甚至，Spring2.0对于Java EE资源（包括JNDI）的配置做了许多简化工作，比如引入&lt;jee:jndi-lookup/>内容模式。
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/87712#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 07 Jun 2007 20:05:00 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/87712</link>
        <guid>http://lincoln.javaeye.com/blog/87712</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（10）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/86671" style="color:red;">http://lincoln.javaeye.com/blog/86671</a>&nbsp;
          发表时间: 2007年06月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          理解Tapestry引擎<br />Tapestry的核心是一个引擎对象，这个对象支撑和组织了应用程序的所有层面。引擎将所有的子系统绑定在一起形成了Tapestry应用程序。引擎主要涉及对服务器端状态的管理。引擎管理Infrastructure对象，而Infrastructure对象储存了大多数应用程序范围的状态(Infrastructure 对象中存放了多大39种Tapestry底层对象)。引擎同时也是管理持久页面属性的重要组成部分，当请求转移的时候，持久化页面属性允许页面拥有独立的内部状态。<br />每一个Tapestry应用程序都拥有一个独立的Servlet，用于连接Servlet容器和应用程序engine对象。应用程序servlet是ApplicationServlet类的一个实例。<br />Servlet的主要工作是找到并创建应用程序引擎对象的实例，引擎对象被储存在HttpSession中。然后代理所有进程应用程序引擎对象发出请求，引擎对象作为Tapestryservice的代理完成真正的请求响应。<br />引擎对象是应用程序中枢对象，它掌管着应用程序中的所有功能。它管理各种资源，如页面加载器，页面记录器，以及在请求周期内，为各种页面和组件提供服务。<br />引擎对象的一个重要工作是指定Tapestry service响应请求，这些service通常被用来创建URL和响应请求。应用程序引擎对象创建和管理请求周期，并捕捉和报道异常。<br />应用程序引擎对象提供页面记录器给请求周期使用。通过这种方式，它制定了整个应用程序的持久化策略。<br />在默认情况下，Tapestry使用org.apache.tapestry.engine.BaseEngine类作为默认的引擎对象。如果我们需要实现自己定义的引擎对象，可以创建一个子类继承与BaseEngine类，并在应用程序规范文件中进行配置。<br />&lt;application name=”MyTapestry4” engine-class=”com.tapestry4.engine.MyEngine”><br />引擎主要是通过service()方法，这个方法处理传递进来的请求，并将处理结果打印到客户端浏览器。<br />在传统的Servlet应用程序中，一个很大的挑战就是对应用程序产生和解析URLs的管理非常复杂。Tapestry通过引擎服务来处理这个问题。引擎服务实际上是请求与组件之间的真实纽带。<br /><br /><br />传统应用程序的URL有什么问题？<br />应用程序URLs表现了应用程序中的各种操作。最普遍的方式就是每个操作都拥有一个独立的Servlet，并使用质询参数获取请求中的客户端数据。<br />对于小型的应用程序，或者大型应用程序的初期阶段，对URL和质询参数的管理并不显得非常复杂，顶多会受到一些命名空间的限制。但是，随着应用程序规模的增大和改变就会产生一个很大的风险。因为JSP与Servlet之间只有非常细小的联系，JSP构造和表现应用程序URL，而Servlet解析那些相同的URL。如同我们前面讨论的那样，JSP与Servlet之间的弱绑定，如果Servlet的映射改变，则每个引用Servlet的JSP都必须被手工检查或修改，以确保产生新的正确的应用程序URL。同样，任何质询的改变将会产生连带影响。
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/86671#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 04 Jun 2007 18:53:35 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/86671</link>
        <guid>http://lincoln.javaeye.com/blog/86671</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（9）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/86670" style="color:red;">http://lincoln.javaeye.com/blog/86670</a>&nbsp;
          发表时间: 2007年06月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在Tapestry中，每一个应用程序使用一个Servlet:org.apache.tapestry.ApplicationServlet。在传统的servlet应用程序中有多种Servlet，每一种Servlet表现了应用程序中可能发生的不同操作，而每一个servlet都需要编写新的代码，并对代码进行测试和调试。<br />编写Servlet代码比编写其他大多数java应用程序困难的原因之一，就是Servlet应用于不同的环境。Servlet容器会为每一个Servlet创建一个实例。也就是说，可能单一的Servlet会处理十几个并发的请求，而每一个请求又是独立线程。任何当前客户端的信息都将被储存在Servlet实例的变量里面，而其他的线程肯定会马上重写那些变量。所有这些都发生的非常频繁，而一旦程序员违反基本原则，就很容易产生Bug。<br />Tapestry框架提供了ApplicationServlet类，并且不需要继承其他类。在Tapestry中应用程序Servlet仅仅是Tapesyry service与Servlet API之间的门户。<br />当web容器开启Servlet时，Tapestry的ApplicationServlet将会完成下一过程。<br />1．通过读取HiveMind配置文件实例化org.apache.hivemind.Registry类，该类用于启动Hivemind框架，并通过该类访问Hivemind框架的service;<br />2．Tapestry的webContextInitializer对象作为Hivemind服务之一被Register访问，初始化Global对象；<br />3．Tapestry的ApplicationSpecificationInitializer对象作为Hivemid服务之一被Register访问，加载Tapestry应用程序规范，并将其放入Global对象；<br />4．Tapestry的SetupServletApplicationGlobals对象作为Hivemind服务之一被Register访问，初始化Infrastructure;而Infrastructure初始化engine，各种service和Tapestry底层对象，并将它们放入Global对象；<br />5．然后将Register对象放入javax.servlet.servletContext。<br />6．初始化ServletRequestService对象，并初始化该对象的两个属性：WebRequestService对象用于响应请求；RequestGlobalsImpl用于储存请求，以便在需要的时候恢复请求。
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/86670#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 04 Jun 2007 18:51:16 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/86670</link>
        <guid>http://lincoln.javaeye.com/blog/86670</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（8）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/86669" style="color:red;">http://lincoln.javaeye.com/blog/86669</a>&nbsp;
          发表时间: 2007年06月04日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Tapestry的service<br />这是Tapestry框架的核心部分。Tapestry框架面向组件的开发方式，使得我们不再操心Servlet底层细节和URL中质询参数的结构。<br /><br />Tapestry用于Servlet的底层对象<br /><br />为了更好地理解Tapestry的service运作过程，我们首先需要熟悉一下Tapestry service中常用的核心对象。<br />1.	IRequestCycle对象<br />该对象用于控制一个单一的请求周期。一旦客户段浏览器发出请求，Tapestry将做以下处理。<br />通过IEngineService对象响应URL；<br />决定响应页面；<br />释放所有资源。<br />在这个过程中，IRequestCycle对象还会处理：<br />异常操作；<br />加载页面模板；<br />跟踪页面属性的变化并储存页面状态；<br />缓存页面对象。<br />2．IRageRecorder对象<br />	该对象用于观察一个页面及该页面Tapestry属性（页面属性和组件属性）的变化，在请求周期内储存页面状态，并在下一个请求周期到来之前，将页面已经发生变化的状态回复到初始值。<br />	通过实现IRageRecorder接口，可以自定义页面状态持久化策略，可以将页面状态存储内存，客户端cookies文件系统或数据库中。<br />3．IPageSource对象<br />	通过页面规范从缓存池中加载页面对象，如果IPageSource对象在缓存池中找不到页面对象，或者加载的对象不可用，那么IPageLoader对象的实例就会创建页面对象的实例(包括页面所调用的组件)。<br />4．IPageLoader对象<br />	创建页面对象的实例。<br />5．IEngine对象<br />	Tapestry应用程序的核心对象的生命周期与HttpSession相同，每个单一客户端拥有自己的engine对象。Engine对象用于向页面和组件提供Tapestry的核心service.<br />6.IEngineService对象<br />	Tapestry 的核心service通过IEngine对象相页面和组件提供Service.service的作用是构建URL编码以响应请求，或解析请求中包含的URL。<br />7．WebRequest对象<br />包含了当前请求的所有信息。这个对象本质上是对HttpServletRequest的封装。<br />8．WebResponse对象<br />	控制客户端响应，并创建输出流。这个对象本质上是对HttpServiceResponse的封装。<br />9．WebContext对象<br />	对ServletContext的封装。<br />10．WebSession对象<br />对HttpSession的封装。
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/86669#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 04 Jun 2007 18:49:47 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/86669</link>
        <guid>http://lincoln.javaeye.com/blog/86669</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（7）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/85088" style="color:red;">http://lincoln.javaeye.com/blog/85088</a>&nbsp;
          发表时间: 2007年05月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Visit对象与Global对象是Tapestry框架的对象容器，我们可以在Visit对象和Global对象中存放任意信息。Visit对象和Global对象的区别在于他们的生命周期不同。<br /><br />Visit对象<br />  Visit对象是一个Tapestry应用程序的对象容器，用来存储应用程序逻辑和数据。这个对象能被应用程序中所有的页面和组件共享，并且包含Web应用程序中某一个客户端的特殊信息。该对象类似于HttpSession在典型Servlet应用程序中扮演的角色。实际上，Visit对象最终作为一个HttpSession的属性被存储。<br />  为了在应用程序中使用一些与Session相关的数据，Tapestry提供了Visit对象。Tapestry并不知道也不关心Visit对象的类型。Tapestry框架对Visit对象的类型定义没有任何要求，每一个应用程序都可以定义自己的Visit类。不论是通过Tapestry属性注入而获得的HiveMind框架提供的Visit对象，还是通过AbstractPage类的public Object getVisit()方法获取的Visit对象，Tapestry都没有指定其具体类型。因此，我们需要对获取到的Visit对象进行强制类型转换。例如：<br /><pre name="code" class="java">  MyVisit vist =(MyVisit)getVisit();</pre><br />  Visit对象是Tapestry框架自动创建的，在Tapestry框架启动时通过读取应用程序规范文件，将Visit对象的配置信息存入Tapestry框架的引擎对象中。因此，我们可以随意在应用程序规范文件中定义自己的Visit对象类。<br /><pre name="code" class="java">&lt;meta key=”org.apache.tapestry.visit-class” value=”com.tapestry4.engine.MyVisit”/></pre><br />  当客户端发出请求，HttpSession对象被创建时，Tapestry读取应用程序中Visit对象的配置信息，并创建Visit对象实例，然后将其存储在HttpSession中，知道HttpSession对象的生命周期结束。<br />  如果有必要，我们可以通过HiveMind框架来创建Visit对象，只需要在应用程序的HiveMind配置文件hivemodule.xml中以下面的方式进行配置。<br /><pre name="code" class="java">&lt;contribution configuration-id=”tapestry.state.ApplicationObjects”>
	&lt;state-object name=”flowStore” scope=”session”>
		&lt;create-instance class=”com.tapestry.engine.FlowStore”/>
	&lt;/state-object>
&lt;/contribution></pre>配置Hivemind提供的id为tapestry.state.ApplicationObjects的configuration。<br />创建一个Hivemind框架的state状态对象，名称为flowStore，声明周期为session。该状态对象是com.tapestry.engine.FlowStore类的实例。<br />	当我们在页面中需要获取这个名称为flowStore的Visit对象时，可以通过Tapestry属性注入的方式。<br /><pre name="code" class="java">	&lt;inject property=”myVisitobj” type=”state” object=”flowstore”/></pre><br />	&lt;inject> 元素的Type属性标明这里注入的是state状态对象，Object属性标明注入进来的是名称为flowStore的Visit对象。<br /><br /><br />Global对象<br /><br />Global对象是一个应用程序空间，用来存储全局性的应用程序逻辑和数据。Global对象的作用如同Visit对象，不过它的生命周期是应用程序的整个周期。Global对象被所有的应用程序引擎对象和Session会话所共享。实际上，Global对象最终最为一个ServletContext的属性被储存。如果我们不指定Global对象，那么Tapestry将使用一个线程保护的HashMap来替代它。<br />Tapestry并不知道也不关心Global对象的类型。Tapestry框架对Global对象的类定义没有任何要求，每一个应用程序都可以定义自己的Visit类。不论是我们通过Tapestry属性注入而获得的HiveMind框架提供的Global对象，还是通过AbstractPage类的public Object getGlobal()方法获取的Global对象，Tapestry都没有指定具体类型。因此，我们需要对获取到的Global对象进行强制类型转换。例如：<br />MyGlobal global =(MyGlobal)getGlobal();<br />我们可以在应用程序规范文件中随意定义自己的Global对象类。<br /><pre name="code" class="java">&lt;meta key=”org.apache.tapestry.global-class” value=”com.tapestry4.engine.MyGlobal”/></pre>当应用程序被启动，ServletContext对象被创建时，Tapestry读取应用程序规范中Global对象的配置信息，并创建Global对象实例，然后将其储存在ServletContext对象中，直到应用程序被关闭。<br />如果有必要，我们可以通过Hivemind框架来创建Global对象，只需要在应用程序的Hivemind配置文件hivemodule.xml中以下面的方式进行配置。<br /><pre name="code" class="java">&lt;contribution configuration-id=”tapestry.state.applicationObject”>
	&lt;state-object name=”flowstore” scope=”application”>
		&lt;create-instance class=”com.tapestry.engine.FlowStore”/>
&lt;/state-object>
&lt;/contribution></pre>创建一个Hivemind框架的state状态对象，名称为flowStore,生命周期为application。该状态对象是com,tapestry.engine,FlowStore类的实例。<br />当我们需要在页面中获取这个名称为flowStore的Global对象时，可以通过Tapestry属性注入的方式。<br /><pre name="code" class="java">&lt;inject property=”myVisitObj” type=”state” object=”flowStore”/></pre>实际上，就&lt;inject>元素的使用方式而言，向Tapestry属性中注入Visit对象和注入Global对象并没有任何差别。而关键在于这个被注入的对象实例本身的生命周期是session还是application。<br />请一定要注意，不论是Visit对象还是Global对象，它们并非是指某一个特定的对象类，而是代指两种不同生命周期的对象。只要进行了配置，任何Java类都可以成为Visit对象或Global对象。
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/85088#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 31 May 2007 14:33:29 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/85088</link>
        <guid>http://lincoln.javaeye.com/blog/85088</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（6）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/85087" style="color:red;">http://lincoln.javaeye.com/blog/85087</a>&nbsp;
          发表时间: 2007年05月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          页面类和页面事件<br /><br />Tapestry框架是基于事件触发机制。当满足某些事件的触发条件时，Tapestry就会触发事件所对应的页面类中的方法。<br /><br />页面类<br />Tapestry框架的思想是面向组件编程，因此页面也是一种特殊的组件。作为组件，它拥有一个规范文件，一个HTML模板，一个页面类和若干嵌入的组件。页面没有调用参数，因为页面是整个组件层次的最外层。页面与组件的差别在于他们的规范文件不同。<br />  为了能够让Tapestry框架实例化页面实例，需要指定一个Java类作为页面类。通常可以在页面规范中指定页面类的路径，通过页面规范&lt;page-specification>标签的class属性，例如：<br /><pre name="code" class="java">&lt;page-specification class=”com.tapestry4.page.Home”>
&lt;/page-specification></pre>也可以通过应用程序规范文件统一为应用程序的所有页面类指定默认定位包路径。<br /><pre name="code" class="java">&lt;meta key=”org.apache.tapestry.page-class-packages” value=”com.tapestry4.page”/></pre>所有的组件都拥有一个隐藏得名称为page的组件属性，用以指名该组件最终被嵌入的页面，多有页面都有一个隐藏得名称为engine的页面属性，用以指明该页面正在调用的引擎对象。当页面第一次被激活的时候，Tapestry创建页面的单一页面类实例，并放入Tapestry缓存池中，以便同一个Web应用程序的不同session会话能够使用这个页面的页面类。缓存池中存放着满足服务器并发所需要的最少数量的页面实例。<br />	通常情况下，页面类都继承于org.apache.tapestry.html.BasePage类，而BasePage类继承于org.apache.tapestry.AbstractPage类。BasePage类为我们提供了一个public ContentType getResponseContentType()方法，该方法返回用于响应的上下文类型。其实，我们完全可以直接让页面类继承于AbstractPage类。<br />  假如我们的页面没有特殊的逻辑，就可以不为页面定义对应的页面类。这个时候，Tapestry会将BasePage类作为默认的页面类。因此，页面必须要有对应的页面类，只是有时候不用我们自己去创建而已。<br />  如果我们认为BasePage类作为默认的页面类并不能满足我们的特殊需求，那么我们可以在应用程序规范文件中指定默认的页面类。例如：<br /><pre name="code" class="java">&lt;meta key=”org.apache.tapestry.default-page” value=”com.tapestry4.engine.MyBasePage”/></pre><br /><br />页面事件<br />	Tapestry框架是基于事件触发机制的，例如，我们通过submit触发Form组件的监听方法等。Tapestry的页面事件是指Tapestry的页面从Tapestry创建(或丛缓存池中获取页面类对象)类对象起，到销毁(或将页面对象放入缓存池)为止的整个过程所触发的方法。<br />Tapestry有5种页面事件，想要控制这些事件，就必须实现其对应的监听接口，Tapestry的所有页面事件的监听接口都位于org.apache.tapestry.event包下。<br /><br />PageAttachlistener 当页面被当前请求第一次获取时，在页面持久化属性被回退为初始值之后触发     基于当前session状态创建页面属性，从应用程序整体状态对象中获取数据。<br /><br />PageValidateListener 当页面被激活的时候触发，可以通过抛出PageRedirectException异常跳转到其他页面   通常用于检查用户是否有权访问该页面，有助于制定应用程序的安全规则。<br /><br />PageBeginRenderListener 当页面开始表现的时候触发，这是最后一次更新持久化页面属性的机会    通常用于实现页面开始表现是需要实现的业务逻辑<br />PageEndRenderListener 当页面开始表现完毕之后，即便在页面表现是抛出异常，也仍然会触发   通常用于实现页面表现完毕时需要实现的业务逻辑<br /><br />PageDetachListener   当页面与请求分离，页面类实例被Tapestry放回页面缓存池的时候触发   清除页面临时状态，清除所有客户端状态，将页面恢复到初始化状态
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/85087#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 31 May 2007 14:31:49 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/85087</link>
        <guid>http://lincoln.javaeye.com/blog/85087</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（5）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/85086" style="color:red;">http://lincoln.javaeye.com/blog/85086</a>&nbsp;
          发表时间: 2007年05月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          监听方法<br />Tapestry框架属于事件触发机制，监听方法是我们在客户端触发服务器端Tapestry框架的主要方式。监听方法有多种形式，当表单form被触发提交的时候，或者当一个链接被单击的时候，就会触发监听方法。监听方法存在于我们的页面类或组件类中，例如，DirctLink组件和Form组件都拥有一个监听参数，我们可以使用一个”listener”字符串将我们页面类中的监听方法绑定在组件的监听参数上。<br />监听方法其实就是一个普通的功用非抽象方法，它可能携带有参数，也可能返回某种类型的值。Tapestry框架并不强制要求监听方法必须携带参数或必须拥有返回值，一个普通的功用方法一旦与某个组件的监听参数通过“listener”字符串绑定，那么这个普通的功用方法就变成了监听方法。<br />1.	监听方法的返回类型<br />监听方法可以使void方法，也可以返回一个String字符串,或者可能是一个实现了IPage接口的页面对象实例。返回一个字符串或返回一个页面对象实例是为了激活并跳转页面以便响应请求。如果返回NULL，则跳转到当前页面。<br />Void：监听方法跳转到当前页面;<br />String: 监听方法根据返回的页面名称来激活页面并表现响应，返回NULL，则跳转到当前页面；<br />IPage：监听方法根据返回的页面类实例来激活页面对象。页面对象可以通过request cycle来获取，也可以通过页面属性注入页面实例来获得，返回NULL则跳转到当前页面；<br />ILink:监听方法根据返回的实现ILink接口的对象对客户端URL进行重定向。<br />2．监听方法的参数<br />   当使用DirectLink组件，ActionLink组件，Submit组件等带有parameters参数的组件时，你甚至可以定制该组件的监听方法参数。DirectLink组件的parameters参数所携带的值将会被编码到URL中，以便我们可以通过监听方法的参数来直接获取。<br />例如：我们通过以下方式调用DirectLink组件。<br /><pre name="code" class="java">&lt;component id=”byFruitLink” type=”DirectLink”>
	&lt;binding name=”listener” value=”listener:buyFruit”/>
	&lt;binding name=”parameters” value=”{fruit.id,fruit.name}”/>
&lt;/component></pre>那么我们可以通过以下两种方式获取DirectLink组件的Parameters参数所绑定的值。<br />以IRequestCycle 对象作为监听方法的参数，然后通过该对象的getListener Parameters()方法获取一个包含所有数据的数组，再从该数组中依次取出组件parameters参数绑定的值，例如：<br /><pre name="code" class="java">Public IPage buyFruit(IRequestCycle cycle){
	Object[] parameters = cycle.getListenerparameters();
	Integer id = (Integer) parameters[0];
	String name =(String)parameter[1];
}</pre>直接以监听方法的参数对应获取每一个DirectLink组件的parameters参数所携带的值。<br /><pre name="code" class="java">Public IPage buyFruit(Integer id,String name){
}</pre>第二种方式显得非常灵活。DirectLink组件的parameters参数所携带的值并不一定都是String类型，它可以是任意类型，但是监听方法参数的类型必须与parameters组件参数所携带的值得类型一一对应。
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/85086#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 31 May 2007 14:29:53 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/85086</link>
        <guid>http://lincoln.javaeye.com/blog/85086</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（4）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/85082" style="color:red;">http://lincoln.javaeye.com/blog/85082</a>&nbsp;
          发表时间: 2007年05月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          在Tapestry框架运行时，通过反射机制生成一个继承于我们页面类的子类，根据页面规范或组件规范中声明的属性，向这个反射生成的子类中添加标准的JavaBean属性，这就是属性指定的运作过程。在页面规范中声明的数据称为页面属性；在组件规范中声明的属性成为组件属性。其实，不论页面属性还是组件属性，他们都以完全一样的方式进行声明和使用。将Tapestry属性分为页面属性和组件属性，只不过为了方便称呼。<br />例如，在登录页面Home，为了拥有一个用户名和密码的输入值载体，我们在页面规范Home.page中声明两个页面属性。<br /><pre name="code" class="java">&lt;property name=”username”/>
&lt;property name=”password”/> </pre><br />为了在页面类Home.java中能够获取到这两个页面属性，我们需要定义两个对应于页面属性的JavaBean抽象getter方法。<br /><pre name="code" class="java">Public abstract string getUsername();//用户名
Public abstract string getPassword();//密码 </pre><br />在页面规范或组件规范中声明属性的时候，我们并不需要声明其类型。Tapestry框架根据我们在页面类或组件类中声明的JavaBean抽象的getter/setter方法类型来决定属性的类型。<br />从反射机制的角度，由于Tapestry框架运行时会生成一个集成于页面类的子类，根据Java的多态性，我们很容易理解，既然子类生成的是标准JavaBean属性，那么当我们在父类（页面类或组件类）中调用JavaBean属性的抽象getter方法，即可以获得子类JavaBean中装载的值。<br />同样，如果我们需要在页面类中设置属性值，那么可以在页面类中添加对应的JavaBean属性抽象的setter方法。如：<br /><pre name="code" class="java">Public abstract void setUsername(String username);//设置用户名
Public abstract voif setPassword(String password);//设置密码
 </pre><br />现在我们已经理解了页面属性（或组件属性）的概念，因此我们可以通过在页面类中添加JavaBean属性的方式来达到页面属性相同的效果。在Home.java中：<br /><pre name="code" class="java">Private string password;
Protected void initialize(){
	This.password = null;
}
Public String getPassword(){
	Return password;
}
Public void setPassword(String password){
	This.password = password;
}</pre>除了标准的JavaBean属性，我们需要注意的是protected void initialize()方法。所有的页面类都继承于org.apache.tapestry.html.BasePage类，而BasePage类继承于AbstractPage类，protected void initialize() 方法是org.apache.tapestry.AbstractPage类中的方法。<br />实际上，页面属性在运作的时候，Tapestry框架不光为其添加标准的JavaBean属性还在页面表现之前和表现之后，两次对页面属性进行初始化。而protected void initialized() 方法具有同样的初始化作用，在页面表现之前和表现之后，两次对页面类中的成员变量进行初始化。<br />需要注意的是，org.apache.tapestry.AbstractComponent 类中并没有protected void initialize() 方法，而所有的组件类都继承于AbstractComponent类。因此我们不能用这样方式对组件类中的成员变量进行初始化。<br />Tapestry框架对页面属性或组件属性的默认初始化值是Java各种类型对象的默认值，如int型为0，String型为NULL等。我们需要对页面属性(或组件属性)进行自定义的初始化事例如下：<br /><pre name="code" class="java">&lt;property name=”username” initial-value =”literal:我爱Tapestry”/></pre><br />如这里对页面属性username的声明所示，我们为该页面属性提供一个初始化值：字符串“我爱Tapestry”。它的作用等同于在页面类中对成员变量username进行以下声明。<br /><pre name="code" class="java">Private String username;
Protected void initialize(){
	This.username=” 我爱Tapestry”;
} 
Public String getUsername(){
	Return username;
}
Public void setUsername(String username){
	This.username=username;
}</pre>
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/85082#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 31 May 2007 14:16:43 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/85082</link>
        <guid>http://lincoln.javaeye.com/blog/85082</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（3）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/85075" style="color:red;">http://lincoln.javaeye.com/blog/85075</a>&nbsp;
          发表时间: 2007年05月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Tapestry属性生命周期<br />Tapestry属性可能拥有持久化状态，在各个请求周期之间保存用户指定的Tapestry属性。当应用程序的某一部分请求一个页面时候，将会获得一个页面实例和用户先前存入持久化页面属性中的值。如果这个页面中调用的组件包含有持久化的组件属性，那么请求也将获得持久化组件属性的值。<br /><br />页面实例不是session持久化。他们仅仅在第一次创建的时候存在于JavaVM的内存中。页面和组件不需要实现java.io.serializable接口，他们永远不会被序列化。而应用程序引擎对象总是不停地创建页面的新实例，并重新对页面赋予其先前被记录的状态。页面的状态也就是指页面所拥有的Tapestry属性(页面属性，以及页面调用组件的组件属性)的值。<br />在Tapestry中，属性的声明周期和重要。它拥有以下3种声明周期。<br />持久-----在各个请求周期之间，属性被记录并持久化。当属性被重载的时候，持久化页面属性被恢复。持久化Tapestry属性根据其声明的持久化策略已不同的方式被不同的用户共享。<br /><br />瞬时----在页面表现之前，属性被设定，并且在当前请求结束的时候，重置为默认值。<br />动态----属性的值根据页面的表现而改变，但是在当前请求结束的时候，被重置。<br />持久化Tapestry属性就好像用户名，又像在商业应用程序中所显示的产品名称；而瞬时持久Tapestry属性通常只被使用一次，比如错误信息；而动态Tapestry属性与表现进程联系紧密，比如以某种排序显示一组条款，就可能需要动态Tapestry属性来为每一条款在循环中赋值。<br /><br />1.持久化页面状态<br />Tapestry框架有责任在请求周期内跟踪页面状态的改变，并且在请求周期之间储存状态。实际上，这是应用程序的引擎对象的职责，通过页面记录器对象来完成。页面记录器是实现org.apache.tapestry.engine.IPageRecorder接口的对象，用于获取页面及其组件中属性的变化，并在请求周期内储存页面的状态。当一个页面的持久化状态被改变时，引擎对象通知页面记录器，并提供属性的名称以及新的值。这个“通知”作为一条信息被持久化地储存下来，当下一个请求周期到达的时候，页面记录器将根据这个信息将页面退回到先前的状态。<br />声明Tapestry属性持久化的工作由开发人员来完成，属性的值必须被序列化（非原始类型通过实现serializable接口来实现）。Tapestry属性的持久化有两种形式。<br /><br />Session持久化 当页面被第一次访问的时候，页面属性的值被储存到HttpSession对象中，当页面需要操作页面属性的时候，从HttpSession中获取页面属性的值，当session会话的生命周期结束，页面属性的值即被清空。声明方法：<br /><pre name="code" class="java">&lt;property name=”username” persist=”session”/></pre><br /><br />客户端持久化 当页面被第一次访问的时候，Tapestry框架自动向客户端浏览器中所偶表单添加hidden域以保存客户端持久化属性的值。并且页面上所有用于跳转的 URL都将会被添加客户端持久化属性进行操作的时候，Tapestry框架自动从客户端浏览器的hidden域中，或者从URL里包含的客户端持久化属性相关信息中获取到客户端持久化属性的值，并重新赋给对应的Tapestry属性。客户端持久化分为客户端应用程序持久化和客户端页面持久化。<br /><br />客户端应用程序持久化：Tapestry属性的客户端持久化范围为整个应用程序，使用方法是在页面规范或组件规范中声明&lt;property>元素的persist属性，且persist属性的值为”client:app”,例如：<br /><pre name="code" class="java">&lt;property name =”username” persist=”client:app”/></pre><br /><br />客户端页面持久化：Tapestry属性的持久化范围是属性所在页面，使用方法是在页面规范或组件规范中声明&lt;property>元素的persist属性，且persist属性的值为”client”或”client:page”例如：<br /><pre name="code" class="java">&lt;property name=”username” persist=”client:page”/>
&lt;property name=”username” persist=”client”/></pre><br /><br />2.瞬时页面状态<br />我们使用的大多数Tapestry属性都是瞬时的页面状态，瞬时的页面状态就如同我们在页面类中定义成员变量，同时创建该成员变量的标准JavaBean方法，并在initialize()方法中对其进行初始化。当页面表现的时候，瞬时的Tapestry属性被赋值，当页面表现结束时，瞬时的属性被还原为初始值。声明方法为：<br /><pre name="code" class="java">&lt;property name=”username”/></pre><br /><br />3.动态页面状态<br />页面属性在页面表现的时候动态地改变。最典型的例子就是Foreach组件，例如：<br /><pre name="code" class="java">&lt;property name=”fruit”/>
	&lt;component id=”fruitListLoop” type=”Foreach”>
	&lt;binding name=”source” value=”fruitList”/>
	&lt;binding name=”value” value=”fruit”/>
	&lt;/component></pre>在这里，组件ID为fruitListLoop的Foreach组件的value参数绑定了页面属性fruit，Foreach组件每遍历到其source参数所绑定fruitList集合中的每一个元素，就将这个元素赋予value参数绑定的页面属性fruit。于是该页面属性fruit的值在Foreach组件的整个循环遍历中，是不断被改变的。<br />当Foreach组件调用结束，整个页面表现完毕之后，value参数所绑定的fruit页面属性又会被还原为初始值。因此，动态页面状态实际上也是瞬时页面状态。
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/85075#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 31 May 2007 14:02:11 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/85075</link>
        <guid>http://lincoln.javaeye.com/blog/85075</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（2）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/85059" style="color:red;">http://lincoln.javaeye.com/blog/85059</a>&nbsp;
          发表时间: 2007年05月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <div class="cnt">
<p style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l1 level1 lfo2"><font size="2"><font face="Times New Roman"><span><span>3．<span style="FONT: 7pt Times New Roman">&nbsp;&nbsp;</span></span></span><span>Tapestry </span></font><span>的面向组件开发方式</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt 17.95pt; TEXT-INDENT: 21pt; mso-para-margin-left: 1.71gd; mso-char-indent-count: 2.0"><font size="2"><span><font face="Times New Roman">Tapestry</font></span><span>面向组件的开发方式是传统开发方式的变革。什么是面向组件呢？这就像搭积木一样。</span><span><font face="Times New Roman">Tapestry</font></span><span>将一个应用程序分成很多个页面，而一个页面又是由若干组件组成的，我们不需要去关心任何</span><span><font face="Times New Roman">Servlet</font></span><span>细节，也不需要关心如何生成</span><span><font face="Times New Roman">URL</font></span><span>和</span><span><font face="Times New Roman">URL</font></span><span>的结构，</span><span><font face="Times New Roman">Tapestry</font></span><span>会为我们自动生成合适的</span><span><font face="Times New Roman">URL</font></span><span>跳转叶面或者传递参数。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt 17.95pt; TEXT-INDENT: 21pt; mso-para-margin-left: 1.71gd; mso-char-indent-count: 2.0"><font size="2"><span>在</span><span><font face="Times New Roman">Tapestry</font></span><span>框架中，页面有</span><span><font face="Times New Roman">HTML</font></span><span>模板，页面规范和页面类组成，而组件由</span><span><font face="Times New Roman">HTML</font></span><span>模板，组件规范和组件类组成。叶面的</span><span><font face="Times New Roman">HTML</font></span><span>模板和组件的</span><span><font face="Times New Roman">HTML</font></span><span>模板都是在原始的</span><span><font face="Times New Roman">HTML</font></span><span>静态页面中嵌入少量的</span><span><font face="Times New Roman">jwcid</font></span><span>属性标签构成，页面类和组件类都是抽象或非抽象的</span><span><font face="Times New Roman">Java</font></span><span>类，并最终继承于</span><span><font face="Times New Roman">Tapestry</font></span><span>的</span><span><font face="Times New Roman">org..apache.tapestry.AbstractComponent</font></span><span>类。因此，页面对于</span><span><font face="Times New Roman">Tapestry</font></span><span>来说，是一种特殊的组件。那么一个使用</span><span><font face="Times New Roman">Tapestry</font></span><span>框架的</span><span><font face="Times New Roman">Web</font></span><span>应用程序，实际上就是一堆</span><span><font face="Times New Roman">Tapestry</font></span><span>组件的集合。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt 18pt"><font size="2"><span><font face="Times New Roman"><span></span><span></span></font></span><span>页面的所有业务逻辑都放在页面类中，页面与页面的关系是</span><span><font face="Times New Roman">Java</font></span><span>对象之间的关系。叶面类的逻辑不会影响到组件类的逻辑，页面与组件通过组件参数联系在一起，组件参数是页面向组件或者组件向页面传递数据的媒介。组件的所有逻辑都放在组件类中。因此，组件的逻辑实现完全脱离页面的束缚。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt 18pt"><font size="2"><span><font face="Times New Roman"><span>&nbsp;&nbsp;</span>Tapestry</font></span><span>组件是一个&ldquo;黑盒子&rdquo;，用于相应</span><span><font face="Times New Roman">HTTP</font></span><span>请求，并表现响应。在调用</span><span><font face="Times New Roman">Tapestry</font></span><span>组件的时候，我们根本就不需要去关心这个组件是如何实现的。我们面对的是组件的参数，只要向组件传递合适的数据，那么组件就会实现我们预期的功能。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt 18pt"><span><span></span><font face="Times New Roman"><font size="2"><span></span><span></span></font></font></span><font size="2"><span>页面类和组件类都是</span><span><font face="Times New Roman">Java</font></span><span>类，实现页面逻辑或者组件逻辑的过程不会影响到页面或组件的</span><span><font face="Times New Roman">HTML</font></span><span>模板。因此，</span><span><font face="Times New Roman">Java</font></span><span>程序员能够和美工人员实现完全独立的分工。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt 18pt"><font size="2"><span><span><font face="Times New Roman">&nbsp;&nbsp;</font></span></span><span>使用</span><span><font face="Times New Roman">Tapestry</font></span><span>框架来开发应用程序，就如同我们组装一台电脑。我们只是关心</span><span><font face="Times New Roman">CPU</font></span><span>的性能如何，内存多大。却不会关心</span><span><font face="Times New Roman">CPU</font></span><span>是怎样制造的，内存芯片之间如何通信等。如果我们嫌这台电脑运算内存不够，加根内存条就可以了，并不会影响</span><span><font face="Times New Roman">CPU</font></span><span>硬盘等已有硬件的工作。同样，从小型的应用程序到各种大型团队开发的几百个页面的大型应用程序，对于</span><span><font face="Times New Roman">Tapestry</font></span><span>来说，只不过是页面和组件使用多少的差别。使用</span><span><font face="Times New Roman">Tapestry</font></span><span>来开发各种大小应用程序，都能够保持一个协调一致的结构。这种结构一致的特性使得不同开发者在类似的问题上会找到类似的解决办法。我们创建的</span><span><font face="Times New Roman">Tapestry</font></span><span>组件能够跨项目使用，那么随着我们开发的应用程序的增多，积累的</span><span><font face="Times New Roman">Tapesty</font></span><span>组件就会越多，开发应用程序的效率就会越快。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt 18pt"><span><span></span><font face="Times New Roman"><font size="2"><span>&nbsp;&nbsp;</span>Tapestry</font></font></span><font size="2"><span>框架带来了真正的</span><span><font face="Times New Roman">Java Web </font></span><span>应用程序</span><span><font face="Times New Roman">OOP</font></span><span>编程。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l1 level1 lfo2"><font size="2"><span><span><font face="Times New Roman">4．<span style="FONT: 7pt Times New Roman">&nbsp;&nbsp;</span></font></span></span><span>面向元素</span><span><font face="Times New Roman">VS</font></span><span>面向组件&mdash;&mdash;思维方式的变革</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>在面向元素的开发方式中，我们的思维是线性的。我们无法将精力集中在应用程序的整体功能实现上。当一个超链接或者一个表单按钮没有达到我们预期目的的时候，我们首先考虑的不是应用程序业务逻辑是否正确实现，而是超链接或表单发送的</span><span><font face="Times New Roman">URL</font></span><span>的结构是否正确，并成功映射到指定的</span><span><font face="Times New Roman">Servlet</font></span><span>。特别当我们在页面中大量使用</span><span><font face="Times New Roman">JavaScript</font></span><span>脚本语言构造</span><span><font face="Times New Roman">URL</font></span><span>，或者修改页面中</span><span><font face="Times New Roman">form</font></span><span>表单的</span><span><font face="Times New Roman">action</font></span><span>属性的时候，分析和维护这样的页面是一场非常可怕的噩梦。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>在面向元素的应用程序中，我们关心</span><span><font face="Times New Roman">URL</font></span><span>传递参数所携带的数据，关心</span><span><font face="Times New Roman">Servlet</font></span><span>的任何细节，甚至在高并发状态下，我们还必须担心</span><span><font face="Times New Roman">Servlet</font></span><span>是否正确地接收</span><span><font face="Times New Roman">URL</font></span><span>中传递的数据。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>由于国内</span><span><font face="Times New Roman">Struts</font></span><span>框架使用相当广泛，而</span><span><font face="Times New Roman">JSP</font></span><span>又是我们学习</span><span><font face="Times New Roman">Web</font></span><span>应用程序开发的起步技术。因此，面向元素的开发方式是我们最习惯和最容易实现的开发方式。虽然面向元素开发方式实现起来最容易，但这并不意味着这种开发方式效率最高。由于我们不等不关心很多</span><span><font face="Times New Roman">Servlet</font></span><span>底层实现，于是我们不得不重复地编写这些底层代码，当一个页面中有多个</span><span><font face="Times New Roman">action</font></span><span>或者</span><span><font face="Times New Roman">Servlet</font></span><span>涉及高并发的时候，我们还得非常仔细地分析这些易让人混淆的底层代码。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>Tapestry</font></span><span>框架的面向组件思维颠覆了我们的习惯性思维。在面向组件的开发方式中我们不再关注</span><span><font face="Times New Roman">Servlet</font></span><span>底层实现，也不再过问</span><span><font face="Times New Roman">URL</font></span><span>的结构。我们通过创建页面来构成应用程序，通过在页面中调用组件来实现页面功能。我们不关心页面如何通过</span><span><font face="Times New Roman">URL</font></span><span>跳转到另外一个页面，也不关心</span><span><font face="Times New Roman">form</font></span><span>表单如何通过</span><span><font face="Times New Roman">URL</font></span><span>将数据包装在请求中提交到服务器端。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;</font></span></span><span>在面向组件的开发方式中，我们的思维是块状的，面向对象的思维方式。我们关心如何将用户在登录时输入的用户名和密码。我们关心在用户登录失败时向客户端返回什么样的错误提示信息，却不关心如何将这些错误信息打印到客户端浏览器。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;</font></span></span><span>面向组件的开发方式使我们将注意力集中在页面逻辑实现上，这样就提高了工作效率。对于那些重复而又容易发生错误的底层</span><span><font face="Times New Roman">Servlet</font></span><span>代码，</span><span><font face="Times New Roman">Tapestry</font></span><span>框架能够很好地进行处理，使我们得以解脱。</span></font></p>
</div>
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/85059#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 31 May 2007 13:45:56 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/85059</link>
        <guid>http://lincoln.javaeye.com/blog/85059</guid>
      </item>
      <item>
        <title>Tapestry学习笔记（1）</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/85057" style="color:red;">http://lincoln.javaeye.com/blog/85057</a>&nbsp;
          发表时间: 2007年05月31日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p style="MARGIN: 0cm 0cm 0pt 18pt; TEXT-INDENT: -18pt; mso-list: l0 level1 lfo1"><font size="2"><span><span><font face="Times New Roman">1．<span style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;</span></font></span></span><span>什么是面向组件开发方式</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt 18pt"><font size="2"><span><font face="Times New Roman">Tapestry</font></span><span>最大的特点是其颠覆性的开发方式。所谓颠覆，是指</span><span><font face="Times New Roman">Tapestry</font></span><span>的开发方式不光与传统的开发方式截然不同，而且还是在逐渐取代传统的开发方式。在</span><span><font face="Times New Roman">web</font></span><span>表现层框架中有两大开发方式，一种是传统的面向元素开发方式，这种开发方式也是最早出现的开发方式。绝大多数框架都是使用面向元素开发方式，比如</span><span><font face="Times New Roman"> Structs</font></span><span>，</span><span><font face="Times New Roman">WebWork</font></span><span>，</span><span><font face="Times New Roman">SpringMVC</font></span><span>等。在面向组件开发方式出现的原因正是为了弥补面向元素开发方式中种种不足之处。因此，我们可以认为面向组件的开发方式比面向元素的开发方式更加先进。目前，面向组件的</span><span><font face="Times New Roman">Web</font></span><span>表现层框架有三种：</span><span><font face="Times New Roman">Tapestry</font></span><span>，</span><span><font face="Times New Roman">JSF</font></span><span>，</span><span><font face="Times New Roman">Wicket</font></span><span>。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><font face="Times New Roman">2</font></span><span>．传统的面向元素开发方式</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>在</span><span><font face="Times New Roman">Java</font></span><span>语言中，我们都是使用</span><span><font face="Times New Roman">Java Servlet<span>&nbsp;&nbsp;</span>API</font></span><span>来创建</span><span><font face="Times New Roman">Web</font></span><span>应用程序。</span><span><font face="Times New Roman">Java Servlet API</font></span><span>是</span><span><font face="Times New Roman">SUN</font></span><span>公司制定的标准。</span><span><font face="Times New Roman">Servlet</font></span><span>是一个</span><span><font face="Times New Roman">Java</font></span><span>类，它的作用是接收客户端发送过来的请求，然后返回一个响应，最后通过客户端浏览器显示出一个</span><span><font face="Times New Roman">HTML</font></span><span>页面。</span><span><font face="Times New Roman">Java Servlet API</font></span><span>为</span><span><font face="Times New Roman">Servlet</font></span><span>定义了一些基础类，例如</span><span><font face="Times New Roman"> HttpServletRequest</font></span><span>，该对象装载了客户端发送过来的请求，并允许</span><span><font face="Times New Roman">Servlet</font></span><span>获取</span><span><font face="Times New Roman">URL</font></span><span>中携带的质询参数。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>Servlet</font></span><span>在</span><span><font face="Times New Roman">Servlet</font></span><span>容器中运行，</span><span><font face="Times New Roman">Servlet</font></span><span>容器是</span><span><font face="Times New Roman">HTTP</font></span><span>协议与</span><span><font face="Times New Roman">Java Servlet API </font></span><span>之间的桥梁。</span><span><font face="Times New Roman">Servlet</font></span><span>容器负责创建</span><span><font face="Times New Roman">Servlet</font></span><span>实例并对其进行初始化。很多软件都提供了</span><span><font face="Times New Roman">Servlet</font></span><span>容器，其中有些软件的全部功能只是为了提供</span><span><font face="Times New Roman">Servlet</font></span><span>容器，例如，</span><span><font face="Times New Roman">Apache Tomcat,Resin,Jetty</font></span><span>等，而在有些软件中，</span><span><font face="Times New Roman">Servlet</font></span><span>容器可能只是一个庞大应用程序服务器中的一部分，例如</span><span><font face="Times New Roman">,BEA WebLogic,IBM WebSphere </font></span><span>或开源的</span><span><font face="Times New Roman">JBoss</font></span><span>应用程序服务器。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><font face="Times New Roman"><span>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>Servlet</font></span><span>容器根据</span><span><font face="Times New Roman">URL</font></span><span>来选择合适的</span><span><font face="Times New Roman">Servlet</font></span><span>响应请求，一个简单的</span><span><font face="Times New Roman">Web</font></span><span>应用程序可能包含许多</span><span><font face="Times New Roman">Servlet</font></span><span>。那么在</span><span><font face="Times New Roman">Web</font></span><span>应用程序的部署描述文件（应用程序中的一个</span><span><font face="Times New Roman">XML</font></span><span>文件通常命名为</span><span><font face="Times New Roman">web.xml,</font></span><span>放置在应用程序中的</span><span><font face="Times New Roman">WEB/WEB-INF/</font></span><span>目录下）中，我们就必须对每一个</span><span><font face="Times New Roman">Servlet</font></span><span>的名称和</span><span><font face="Times New Roman">Java</font></span><span>类全路经进行配置，并且配置</span><span><font face="Times New Roman">URL</font></span><span>和</span><span><font face="Times New Roman">Servlet</font></span><span>的映射关系。例如：</span></font></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt"></span></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">description</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt; COLOR: black">This is the description of my J2EE component</span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">description</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt"></span></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">display-name</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt; COLOR: black">This is the display name of my J2EE component</span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">display-name</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt"></span></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet-name</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt; COLOR: black">MyServlet</span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet-name</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt"></span></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet-class</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt; COLOR: black">com.wanjin.MyServlet</span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet-class</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt"></span></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt"></span></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt"></span></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet-mapping</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt"></span></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet-name</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt; COLOR: black">MyServlet</span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet-name</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt"></span></p>
<p align="left" style="MARGIN: 0cm 0cm 0pt; TEXT-ALIGN: left; mso-layout-grid-align: none"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">url-pattern</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt; COLOR: black">/servlet/MyServlet</span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">url-pattern</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span><span style="FONT-SIZE: 10pt"></span></p>
<p style="MARGIN: 0cm 0cm 0pt"><span style="FONT-SIZE: 10pt; COLOR: black"><span>&nbsp;&nbsp;</span></span><span style="FONT-SIZE: 10pt; COLOR: teal">&lt;/</span><span style="FONT-SIZE: 10pt; COLOR: #3f7f7f">servlet-mapping</span><span style="FONT-SIZE: 10pt; COLOR: teal">&gt;</span></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span>那么当客户端请求</span><span><font face="Times New Roman">URL</font></span><span>中包含&ldquo;</span><span><font face="Times New Roman">/servlet/MyServlet</font></span><span>&rdquo;字符串的时候，</span><span><font face="Times New Roman">Servlet</font></span><span>容器将会查找名称为&ldquo;</span><span><font face="Times New Roman">myServlet</font></span><span>&rdquo;的</span><span><font face="Times New Roman">Servlet</font></span><span>，然后根据名称为&ldquo;</span><span><font face="Times New Roman">myServlet</font></span><span>&rdquo;的</span><span><font face="Times New Roman">Servlet</font></span><span>的</span><span><font face="Times New Roman">Java</font></span><span>类群路径&ldquo;</span><span><font face="Times New Roman">com.wanjin.MyServlet</font></span><span>&rdquo;创建</span><span><font face="Times New Roman">Servlet</font></span><span>实例，并对其进行初始化。</span><span><font face="Times New Roman">MyServlet</font></span><span>类在接受到客户端返回相应。相应的结果以一个</span><span><font face="Times New Roman">HTML</font></span><span>，页面的形式在客户端浏览器出现，该叶面向是勒我们希望客户端得到的应用程序业务信息。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>整个过程是很简单的。但是当我们面对一个庞大</span><span><font face="Times New Roman">Web</font></span><span>应用程序的时候，该用程序就可能包含大量的</span><span><font face="Times New Roman">Servlet</font></span><span>。这就意味着我们必须定义大量的</span><span><font face="Times New Roman">URL</font></span><span>与</span><span><font face="Times New Roman">Servlet</font></span><span>映射关系。那么，应用程序的</span><span><font face="Times New Roman">URL</font></span><span>结构将变得越来越复杂，也越来越容易发生混淆。于是，在应用程序开发阶段，我们不得不将绝大部分精力用于处理数量众多且易于混淆的</span><span><font face="Times New Roman">URL</font></span><span>。</span></font></p>
<p style="MARGIN: 0cm 0cm 0pt"><font size="2"><span><span><font face="Times New Roman">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</font></span></span><span>在传统的开放方式中，例如，</span><span><font face="Times New Roman">JavaServer Page (JSP)</font></span><span>，</span><span><font face="Times New Roman">SpringMVC</font></span><span>，</span><span><font face="Times New Roman">Struts,WebWork</font></span><span>中，我们必须关注叶面中每一个</span><span><font face="Times New Roman">&lt;a&gt;</font></span><span>元素的</span><span><font face="Times New Roman">href</font></span><span>属性，以及</span><span><font face="Times New Roman">form</font></span><span>元素的</span><span><font face="Times New Roman">action</font></span><span>属性，因为这些属性装载了向服务器发送请求的</span><span><font face="Times New Roman">URL.</font></span><span>大多数情况下，我们对这些</span><span><font face="Times New Roman">HTML</font></span><span>元素属性的关注甚至超过了我们对应用程序业务逻辑的关注。我们将这种开发方式称为面向元素（标签）开发方式。</span></font></p>
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/85057#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Thu, 31 May 2007 13:44:48 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/85057</link>
        <guid>http://lincoln.javaeye.com/blog/85057</guid>
      </item>
      <item>
        <title>封忆校园</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/84537" style="color:red;">http://lincoln.javaeye.com/blog/84537</a>&nbsp;
          发表时间: 2007年05月29日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          &nbsp;&nbsp;&nbsp; 终于，完成了论文，完成了答辩，完成了大学生活。没有了整日的书声墨香，没有了整日的欢声笑语，曾经的一切仿佛都在山水画中度过，轻轻描描，星星点点，俯笔青山绿水，扬笔青天浮云。静，静，静。怎一个静字了得，才下眉头却上心头。
<p>&nbsp;&nbsp;&nbsp; 曾忆起，书生稚气。曾忆起，鸿鹄之志。曾忆起，依依佳人。蓦然回首，都已化作青山中的翠柏。印证着留下的青春的足迹。这一刻，没有了风，翠柏不再随风轻摆，没有了雨，那油油的绿叶也只能留在最后的记忆。</p>
<p>&nbsp;&nbsp;&nbsp; 我寻着这曾经，走过的无数次的幽静小路，迎着丁香花陶醉的芳香，数着路旁站立的青松翠柳，依旧，依旧，依旧是我熟悉的......这里有才华横溢的天骄才子，这里有风华绝代温文尔雅的才女。没有车水马龙，没有城市的喧嚣，更没有人群的纷争这里是世外桃源。驻足在这里，没有一丝的清风，仿佛是蟾鸣枝头的仲夏。又是那么那么的幽静。听不见欢声笑语，触不到拂面清风，看不见碌碌行人。一切的一切，化成了宁静的碧水，悠然的映青山。</p>
<p>&nbsp;&nbsp;&nbsp; 我要离开了，熟悉的幽静小路，我要离开了，熟悉的青松翠柳，我要离开了，风华正茂的同窗校友。已不是惜日的康桥，而是今日的北国春城。没有轻轻的挥手，只有静静地闭上眼睛......仰首青天</p>
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/84537#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 29 May 2007 19:41:24 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/84537</link>
        <guid>http://lincoln.javaeye.com/blog/84537</guid>
      </item>
      <item>
        <title>持久曾设计与ORM笔记</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/33916" style="color:red;">http://lincoln.javaeye.com/blog/33916</a>&nbsp;
          发表时间: 2006年11月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: 18pt">持久曾设计与ORM</span><br />ORM概述<br />     ORM可以说是目前比较热点的话题，所谓ORM-Object/Releational Mapper(这里请注意与建模领域中的Object Role Modeling相区分。另外注意这里出现的术语时“O/R Mapper”，而非最常见的“O/R Mapping”。相对来讲，O/R Mapping描述的是一种设计思想或者实现机制，在这一点上，与我们上面所说的“O/R”的含义实际上一致。而“O/R Mapper”一般指 根据O/R原理设计的持久化框架，其中包含了除O/R本身之外的更多内容，如SQL自生成，事务管理，Cache管理等。)，从字面上来理解，即“对象-关系型数据映射组件”。<br />     首先来看O/R，这其中包含两个关键词：Object(对象)和Rational(关系型数据)。在目前的应用系统中，大多数情况下，我们都必须同时面对对象和关系型数据进行开发。<br />     在业务罗辑层和表示层，我们将系统中的各参与失蹄进行面向对象的封装，并籍此将现实世界中的逻辑进行高度抽象后，以计算机语言实现。而另一方面，在数据持久层，迫于目前数据库技术发展的现实，我们必须在现有的关系型数据库模型上进行持久化实现。<br />一个问题自然浮现，我们应该如何面向对象的设计与传统关系型数据进行整合？<br />之前讨论“解耦合”思想与DAO模式的时候，实际上我们已经面对这个问题。回忆一下之前的描述：<br />	DAO模式实际上是两个模式的组合，即Data Accessor模式和Active Domain Object模式，其中Data Accessor模式实现了数据访问和业务逻辑的分离，而Active Domain Object实现了业务数据的对象化封装。<br />在前面的例子中，Customer类，即所谓的Domian Class/Object，对系统中的“用户”对象进行了抽象封装。系统运行过程中，Customer对象实例作为”用户”的抽象代表，扮演着虚拟世界中的一个角色。<br />同时，在我们的系统中，还需要将Customer实例的信息，也就是所谓的”用户数据”进行持久化，保存到数据库以供日后之用。于是，在上面的就绝方案中，我们引入Data Accessor(数据访问类)，实现对象到关系型数据库之间的持久化。<br />上述设计中，Customer类的属性信息与数据库表字段之间的对应，也就是O/R关系得一个实例。<br />从这个简单实例中，我们可以看到，为了实现持久化支持，我们必须编写对应得Data Accessor以实现数据的增删改查(CRUD-Create Retrieve Update Delete)。繁琐的JDBC代码难以避免。<br />O/R Mapper的出现则解决了这一问题。<br /><br />持久层实现类型<br /><br />一般来讲，持久层实现有3种类型。<br /><br />混杂模式<br />	混杂模式是持久化功能的原始实现模式。即在业务类中混杂JDBC访问代码，从而提供所需的持久化功能。<br />	在前面“解耦合思想的自然演进”一节的第一个实例中，我们已经看到了这样的例子。<br />业务代码中混杂了基于JDBC编码的持久化实现。<br />	那么，这种方式的持久层实现，这里就称之为混杂模式，如图 <img src="http://hiphotos.baidu.com/wanjin/abpic/item/d804e3d3d47bf2dfa8ec9a52.jpg" />所示。<br /><br /><br />这种模式的优点在于开发的迅速便捷。对于原型系统或者小型应用而言显得别具意义。但我们也应该看到，基于这种模式开发的系统，其维护性和扩展性较差，对象属性，数据库结构的变动都将直接导致业务逻辑代码的修改。<br />实际上，在这种模式中，我们并不能清晰地分辨出所谓“持久层”的逻辑层次。<br /><br />基于Data Class 的持久层实现模式<br /> 	在这种模式中，数据类(Data Class) 作为业务类与持久层沟通的桥梁，起着承上起下的作用。<br />	之前所讨论的DAO模式案例，实际上就是这种模式的一个典型实现。Data Class实际上包含了DAO模式中的Domian Class/Object 和Data Accessor Class。<br />	Domian Class作为对现实世界的抽象，起着信息携带者的作用。而Data Accessor Class则通过JDBC代码将 Domian Class与数据库表相关联。<br /><br />这种模式如图1-10所示。<br /> <br /><img src="http://hiphotos.baidu.com/wanjin/abpic/item/b4290ad59f383ec450da4b50.jpg" /><br /><br />在这种模式中，我们实现了业务逻辑与底层数据结构之间的分离。Data Class作为一个相对独立里的逻辑层次，较为清晰的体现了所谓持久层的概念。底层关系数据的结构变化，可以较好地屏蔽在数据类这个层次，从而避免对上层的业务逻辑造成影响。<br /><br />实际上，目前正在运行的很多Java企业应用系统中，基于这种类型的设计占了很大比重。<br />	而这种模型的缺陷也显而易见，随着设计层次的增多，编码量的增加相当可观。相对第一种混杂模式而言，系统结构上得到较大提升的同时，项目设计和开发成本也相应增高。<br /><br />繁杂的JDBC代码总得有人去编写，单纯依靠自身力量进行设计和编码，这样的问题难以避免。<br />	所幸，我们不是孤身作战。Sun J2EE 规范制定小组以及开源群体中的众多力量已经深刻的体会到了这一点，于是我们拥有了Entity Bean，JDO，Hibernate，iBatis，Apache OJB<br />这些杰出的持久层框架。而基于这些成熟可靠的第三方框架，我们得以从设计与工作量之间的平衡难点中解脱。于是，有了下面的第三种解决方案。<br /><br />基于现有持久层框架的实现模式<br />	实际上，这种模式是第二种模式的延伸。Data Classes所包含的Data Accessor和Domain Class数量并没有减少。<br />只是，我们把其中最为繁琐的工作——基于JDBC的OR映射工作，交由第三方完成。<br />	Data Accessor中的繁琐编码工作因此得到了空前简化，而与此同时，伴随持久层框架而来的辅助工具也大大减轻了Domain Class的编码负担。<br />这种实现模式如图1-11所示。<br /><img src=" http://hiphotos.baidu.com/wanjin/abpic/item/8e46123be9e02aea14cecb50.jpg" />
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/33916#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sun, 12 Nov 2006 20:41:05 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/33916</link>
        <guid>http://lincoln.javaeye.com/blog/33916</guid>
      </item>
      <item>
        <title>DAO设计模式笔记</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/33727" style="color:red;">http://lincoln.javaeye.com/blog/33727</a>&nbsp;
          发表时间: 2006年11月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <span style="font-size: 24pt">DAO设计模式</span><br /><br />DAO(Data Access Object)模式实际上是两个模式的组合，即Data Accessor 模式和 Active Domain Object 模式，其中 Data Accessor 模式实现了数据访问和业务逻辑的分离，而Active Domain Object 模式，其中Data Accessor模式实现了数据访问和业务逻辑的分离，而Active Domain Object 模式实现了业务数据的对象化封装，一般我们将这两个模式组合使用，因此，考虑到这些因素，这里将其作为同一个主题加以讨论。如图展示了DAO模式的实现层次。<br /><br />DAO模式通过对业务层提供数据抽象层接口，实现了以下目标：<br /><br />1.	数据存储逻辑的分离<br />通过对数据访问逻辑进行抽象，为上层机构提供抽象化的数据访问接口。业务层无需关心具体的select,insert,update操作，这样，一方面避免了业务代码中混杂JDBC调用语句，使得业务落实实现更加清晰，另一方面，由于数据访问几口语数据访问实现分离，也使得开发人员的专业划分成为可能。某些精通数据库操作技术的开发人员可以根据接口提供数据库访问的最优化实现，而精通业务的开发人员则可以抛开数据曾德繁琐细节，专注于业务逻辑编码。<br /><br />2.	数据访问底层实现的分离<br />   DAO模式通过将数据访问计划分为抽象曾和实现曾，从而分离了数据使用和数据访问的地称实现细节。这意味着业务层与数据访问的底层细节无关，也就是说，我们可以在保持上层机构不变得情况下，通过切换底层实现来修改数据访问的具体机制，常见的一个例子就是，我们可以通过仅仅替换数据访问曾实现，将我们的系统部署在不同的数据库平台之上。<br /><br />3.	 资源管理和调度的分离<br />在数据库操作中，资源的管理和调度是一个非常值得关注的主题。大多数系统的性能瓶颈往往并非集中于业务逻辑处理本身。在系统涉及的各种资源调度过程中，往往存在着最大的性能黑洞，而数据库作为业务系统中最重要的系统资源，自然也成为关注的焦点。DAO模式将数据访问逻辑从业务逻辑中脱离开来，使得在数据访问层实现统一的资源调度成为可能，通过数据库连接池以及各种缓存机制(Statement Cache,Data Cache等，缓存的使用是高性能系统实现的一个关键所在)的配合使用，往往可以保持上层系统不变的情况下，大幅度提升系统性能。<br /><br />4．数据抽象<br />	在直接基于JDBC调用的代码中，程序员面对的数据往往是原始的RecordSet数据集，诚然这样的数据集可以提供足够的信息，但对于业务逻辑开发过程而言，如此琐碎和缺乏寓意的字段型数据实在令人厌倦。<br />   DAO 模式通过对底层数据的封装，为业务曾提供一个面向对象的接口，使得业务逻辑开发员可以面向业务中的实体进行编码。通过引入DAO模式，业务逻辑更加清晰，且富于形象性和描述性，这将为日后的维护带来极大的便利。试想，在业务曾通过Customer.getName方法获得客户姓名，相对于直接通过SQL语句访问数据库表并从ResultSet中获得某个字符型字段而言，哪种方式更加易于业务逻辑的形象化和简洁化？<br /><br />空洞地谈些理论固然没有什么价值，我们需要看到的是通过对应用设计模式之后，我们的代码到底有怎样的改观，进而才能对设计带来的优劣有所感悟。下面让我们来看看代码：<br /><br />	[code]Public BigDecimal calcAmount(String customerID,BigDecimal amount){
      		//根据客户ID获得客户记录
        Customer customer  = CustomerDAO.getCustomer(customerID);
  
           //根据客户登记获得打折规则
         Promotion promotion = PromotionDAO.getPromotion(customer.getLevel());

           //累积客户总消费额，并保存累计结果
         Customer.setSumAmount(customer.getSumAmount().add(amount));
         CustomerDAO.save(customer);

          //返回打折后金额
          Return  amount.multiply(promotion.getRatio());
}[/code]这样的代码相信已经足够明晰，即使对于缺乏数据库技术基础的读者也可以轻松阅读。<br /><br />从上面这段代码中，我们可以看到，通过DAO模式对各个数据库对象进行封装，我们对业务层屏蔽了数据库访问的底层实现，业务曾仅包含与本领域相关的逻辑对象和算法，这样对于业务逻辑开发人员(以及日后专注于业务逻辑的代码阅读者)而言，面对的是一个简洁明快的逻辑实现结构。业务层的开发和维护将变得更加简单。<br />DAO模式中，数据库访问层实现被隐藏到Data Accessor中，前面说过，DAO模式实际上是两个模式的组合，即Data Accessor 和 Domain Object模式。<br />何谓 Data Accessor?即将数据访问的实现机制加以封装，与数据的使用代码相分离，从外部来看，Data Accessor 提供了黑盒式的数据存取接口。<br /><br />Domain Object则提供了对所面向领域内对象的封装。<br />从某种意义上，我们可以这么理解：<br />[code]Data Accessor object (DAO) =Data +Accessor + domain object[/code]<br /><br />这个等式自左向右，形象地描述了设计分离的3个层次。<br />现在，对于上面的例子，来看看业务层后所隐藏的实现细节：<br />首先，我们这个计算打折后金额的业务过程中，涉及了两个业务对象，即客户对象Customer，和促销规则对象Promotion。自然，这两个对象也就成为了此业务领域(Business Domain)中的Domain Object，所谓Domain Object，简单来讲就是对领域内(Domain)涉及的各个数据对象，反映到代码，就是一个拥有相关属性的getter,setter方法的JavaClass(Java Bean)<br /><br />以Customer和CustomerDao为例，实现代码如下(Promotion 和 PromotionDAO的实现代码与此类似)：<br /><br /><br /><br /><br />DAO 模式的进一步改良<br /><br />上面的例子中我们通过DAO模式实现了业务路基与数据逻辑的分离。对于专项开发(为特定客户环境指定的特定业务系统)而言，这样的分离设计差不多已经可以实现开发过程中业务层面与数据层面的相对独立，并且在实现复杂性与结构清晰性上达到较好的平衡。<br /><br />然而，对于一个产品化的业务系统而言，目前的设计却仍稍显不足。相对专项原发的软件项目而言，软件产品往往需要在不同客户环境下及时部署。一个典型情况就是常见的论坛系统，一个商业论坛系统可能会部署在厂前上万个不同的客户环境中。诚然，由于java良好的跨平台支持，我们在操作系统之间大可轻易迁移，但在另外一个层面，数据库层，却仍然面临着平台迁移的窘境。客户可能已经购买了Oracle,SQLServer,Sybase 或者其他类型的 数据库。这就意味着我们的产品必须能部署在这些平台上，才能满足客户的需求。<br /><br />对于我们现有的设计而言，为了满足不同客户的需求，我们可以实现针对不同类型数据库的<br />Data Accessor,并根据客户实际部署环境，通过类文件的静态替换来实现。显然，这样的实现方式在面对大量客户和复杂的部署环境时，将大大增加部署和维护工作的难度和复杂性。回忆一下“开闭原则”(Open-Close Principle) –对扩展开放，对修改封闭。我们应该采取适当的设计，将此类因素带来的变动(类的静态替换)屏蔽在系统之外。<br /><br />为了实现跨数据库平台移植，或者展开来说，为了支持不同数据访问机制之间的可配置切换，我们需要在目前的DAO层引入Factory模式和Proxy模式。<br /><br />这里所谓的不同数据访问机制，包括了不同数据库本身的访问实现，同时也包括了对于同一数据库德不同访问机制的兼容。例如我们的系统部署在小客户环境中，可能采用了基于JDBC的实现，而在企业环境中部署时，可能采用CMP作为数据访问的底层实现，以获得服务器集群上的性能优势(CMP具体怎样还有待商榷，这里暂且将其作为一个理由)。<br /><br />Factory模式的引入<br /><br />由于需要针对不同的数据库访问机制分别提供各自版本的Data Accessor实现，自然我们会想通过 Java Interface 定义一个调用接口，然后对这个调用接口实现不同数据库的 Data Accessor。通过以接口作为调用界面和实现规范，我们就可以避免代码只能给对具体实现的依赖。<br />对于例子中的CustomerDAO而言，我们可以抽象出如下的接口：<br />[code]Public interface CustomerDAO{
	Public Customer getCustomer(String custID);
    Puboic void save (Customer customer);
}[/code]<br /><br />这里作为示例，提供了两个实现，一个基于MySql数据库，一个基于Oracle，对这里的简单示例而言，基于Oracle和MySql的实现并没有什么太大区别，只是为了说明系统设计的结构。<br /> <br />作为最常用的创建模式，Factory模式在这里起到来接接口和实现的桥梁作用。通过Factory模式，我们可以根据具体需要加载相应得实现，并将此实现作为所对应接口的一个实例提供给业务层使用：<br />[code]CustomerDAO custDAO =(CustomerDAO)DAOFactory.getDAO(CustomerDAO.class);
Customer customer = custDAO.getCustomer(customerID);[/code]<br /><br />通过上面的代码我们可以看到，通过接口我们将具体的DAO实现从代码中分离。<br />也就是说，业务层通过接口调用底层实现，具体的DAO实现类不会出现在我们的业务代码中。而具体实现类在配置文件中加以配置，之后DAOFactory.getDAO方法通过读取配置文件获得当前我们期望使用的视线类的类名，再通过Java Class动态加载机制加载后返回。<br /><br />从而我们的代码并不依赖于某个特定的实现类，只需要在部署的时候在配置文件中指定当前采用的实现类即可。<br />本例中，为了提高性能，避免每次调用都读取配置文件所引起的大量磁盘操作，采用了HashMap作为DAO缓存实现示例：<br />[code]package net.wanjin.lab.persistence.dao;

import java.util.HashMap;

public class DAOFactory {
	
	private static HashMap daoMap = null;
	
	/**
	 * Return a implemetation instance of the specified DAO Interface
	 * @return the DAO Implemmenation Class Instance
	 */
	public static Object getDAO(Class daoInterface){
		initial();
		Object dao = daoMap.get(daoInterface);
		if(null ==dao){
			throw new DAOException("No Implementation found of DAO interface =>"
					+daoInterface.getName());
		}
		return dao;
	}
	/**
	 * Initial the DAOFactory
	 * Load DAO Interface and Implementation In daoMap for later use
	 */
	public static synchronized void initial(){
		if(null==daoMap){
			daoMap =DAOConfig.load();//根据配置文件加载DAO实现配置
		}
	}

}[/code]<br /><br /><br /><br />[code]package net.wanjin.lab.persistence.dao;

import java.util.Enumeration;
import java.util.HashMap;
import java.util.Properties;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;

/**
 * DAOConfig 类实现了配置文件的读取功能，并根据配置文件中的内容加载制定的接口和实现类；
 * @author Administrator
 */

public class DAOConfig {
	
	private static Logger logger = LogManager.getLogger(DAOConfig.class);
	
	private static final String DAO_CONFIG_FILE="dao.xml";
	private static final String DAO_CONFIG_SECTION="DAO";
	
	/**
	 * Load the DAO Interface_Implementation into a HashMap
	 * @return
	 */
	
	public static synchronized HashMap load(){
		HashMap map = new HashMap();
		
		JFigLocator jfigLocator = new JFigLocator(DAO_CONFIG_FILE);
		JFigIF daoConfig = JFig.getInstance(jfigLocator);
		Properties prop = daoConfig.getSectionAsProperties(DAO_CONFIG_SECTION);
		
		Enumeration enumSection = prop.keys();
		while(enumSection.hasMoreElements()){
			String daoIface =(String)enumSection.nextElement();
			String daoImpl = prop.getProperty(daoIface);
			try{
				Class iface = ClassToolKit.loadClass(daoIface);
				Class impl = ClassToolKit.loadClass(daoImpl);
				//将接口作为HashMap索引，实现类作为值
				map.put(iface, impl);
			}catch(ClassNotFoundException e){
				logger.debug("No Class Found"+e);
			}
		}//while enumSection
		return map;
	}
}[/code[code]]//dao.xml 文件
&lt;?xml version="1.0" encoding="UTF-8"?>
&lt;configuration>
	&lt;section name="DAO">
		&lt;entry key="net.wanjin.lab.persistence.dao.iface.CustomerDAO"
		       value="net.wanjin.lab.persistence.dao.impl.CustomerDAOImp_Mysql"/>
		 
		 &lt;entry key="net.wanjin.lab.persistence.dao.iface.PromotionDAO"
		        value="net.wanjin.lab.persistence.dao.impl.PromotionDAOImp_Mysql"/>
	&lt;/section>
&lt;/configuration>[/code]<br />DAOConfig中使用了JFig读取XML配置文件(dao.xml),关于JFig的具体信息请参见http://jfig.sourceforge.net.<br /><br />[code]package net.wanjin.lab.persistence.dao;

public class ClassToolKit {
	
	public static Class loadClass(String className)
				throws ClassNotFoundException{
		
		Class cls = null;
		try{
			//首先尝试用当前ClassLoader加载
			cls = Thread.currentThread().getContextClassLoader().loadClass(className);
		}catch(Exception e){
			e.printStackTrace();
		}
		if(cls == null){
			//如果通过当前ClassLoader加载失败，使用系统ClassLoader加载
			cls = Class.forName(className);
		}
		return cls;
	}
}[/code]这样，通过接口与实现相分离，并结合DAOFactory动态加载实现类，我们实现了底层访问实现的参数化配置功能。从而为增强产品的部署能力提供了强有力的支持。<br /><br />经过Factory模式的改造，我们业务层代码也进行了相应得修改：<br /><br />[code]package net.wanjin.lab.persistence.dao;

import java.math.BigDecimal;

import net.wanjin.lab.persistence.domain.Customer;

public class Customers {
	
	public BigDecimal calcAmount(String customerID,BigDecimal amount){
		
		//根据客户ID获得客户记录
		CustomerDAO customerDAO = (CustomerDAO)DAOFactory.getDAO(CustomerDAO.class);
		Customer customer = customerDAO.getCustomer(customerID); 
		
		//根据客户等级获得打折比率
		PromotionDAO promoDAO = (PromotionDAO)DAOFactory.getDAO(PromotionDAO.class);
		Promotion promotion = promoDAO.getPromotion(customer.getLevel());
		
		//累计客户总消费，并更新数据库
		customer.setSumAmount(customer.getSumAmount().add(amount));
		customerDAO.save(customer);
		
		//返回打折后金额
		return amount.multiply(promotion.getRatio());
		
	}

}[/code]<br />似乎出现了一些Bad Smell,相对于改造前的calcAmount方法，这段代码里混杂了一些数据访问层的内容，如DAOFactory.getDAO方法的调用。虽然有众多的理由解释引入DAOFactory.getDAO所带来的好处，但事实是，无论有多好的理由新的设计必须避免影响业务逻辑代码的可读性。没有哪家公司能说服你在自己的房屋中增加一条穿堂而过的管道，而理由是为了实施更好的供暖设计，我们软件也一样。<br /><br /><br />Proxy  模式的引入<br /><br />为了保持业务代码的简洁，将Factory模式带来的Bad Smell排除在系统之外。我们引入了结构模式中的Proxy模式。<br /><br />Proxy模式的作用事通过提供一个中间层(Proxy),将上层调用接口与下层实现相衔接，其标准实现如下。<br /><br /><br /> <br /><br />[code]package net.wanjin.lab.persistence.dao;

import java.math.BigDecimal;

public class DecoupleByDesign {

	
	public BigDecimal calcAmount(String customerID,BigDecimal amount){
		
		//根据客户ID获得客户记录
		Customer customer = CustomerProxy.getCustomer(customerID);
		
		//根据客户等级获得打折比率
		Promotion promotion = PromotionProxy.getPromotion(customer.getLevel());
		
		//累计客户消费额，并更新数据库
		customer.setSumAmount(customer.getSumAmount().add(amount));
		CusromerProxy.save(customer);
		
		//返回打折后金额
		return amount.multiply(promotion.getRatio());
	}
}[/code]<br />Bad Smell消失了，业务层在次变得干净简洁。而CustomerProxy 和PromotionProxy做了些什么呢？其实很简单：<br />[code]package net.wanjin.lab.persistence.dao;

import java.math.BigDecimal;

import net.wanjin.lab.persistence.domain.Customer;

public class DecoupleByDesign {

	
	public BigDecimal calcAmount(String customerID,BigDecimal amount){
		
		//根据客户ID获得客户记录
		Customer customer = CustomerProxy.getCustomer(customerID);
		
		//根据客户等级获得打折比率
		Promotion promotion = PromotionProxy.getPromotion(customer.getLevel());
		
		//累计客户消费额，并更新数据库
		customer.setSumAmount(customer.getSumAmount().add(amount));
		CustomerProxy.save(customer);
		
		//返回打折后金额
		return amount.multiply(promotion.getRatio());
	}
}[/code]<br /><br /><br />[code]package net.wanjin.lab.persistence.dao;

public class PromotionProxy {
	/**
	 * Get Promotion Object by Promotion Level
	 * @param level
	 * @return
	 */
	
	public static Promotion getPromotion(int level){
		PromotionDAO promoDAO = (PromotionDAO)DAOFactory.getDAO(PromotionDAO.class);
		return promoDAO.getPromotion(level);
		
	}

}[/code]<br />至此，通过Factory和Proxy模式的应用，我们对原有的DAO模式进行了改造，在不影响业务曾清晰性的前提下，提供了底层实现的参数配置化实现。<br />最后，让我们通过下面这个Sequence Diagram再整体考察一下改造后的成果。
          <br/>
          <span style="color:red;">
            <a href="http://lincoln.javaeye.com/blog/33727#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Sat, 11 Nov 2006 18:48:52 +0800</pubDate>
        <link>http://lincoln.javaeye.com/blog/33727</link>
        <guid>http://lincoln.javaeye.com/blog/33727</guid>
      </item>
      <item>
        <title>持久层设计与资源管理模式笔记</title>
        <author>Lincoln</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://lincoln.javaeye.com">Lincoln</a>&nbsp;
          链接：<a href="http://lincoln.javaeye.com/blog/33724" style="color:red;">http://lincoln.javaeye.com/blog/33724</a>&nbsp;
          发表时间: 2006年11月11日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          持久层设计与资源管理模式<br />无论是怎样的应用系统，都无法脱离对资源的管理和使用。而对于持久层而言，资源的合理管理和调度则显得尤为重要。<br /><br />在大多数应用系统中，80%以上的应用逻辑并不需要特别复杂的数据库访问逻辑(可能只是几条简单的Select或作者Insert/Update语句)。对于这些占到多数的简单逻辑而言，如果SQL语句和数据库本身的设计不是太糟糕(合适的关联，字段索引以及数据库分区策略)，在特定的硬件环境下，我们认为数据库的性能基本稳定。<br />此时，导致持久层性能地下的罪魁祸首可能并不是数据库本身，而是在于失败的资源管理调度机制。<br /><br />Connection Pool<br />即使对于我们而言，通过JDBC获取数据库连接实在是件再简单不过的事情，但对于JDBC Driver来说，连接数据库却并非一件轻松差事。数据库连接不仅仅是在应用服务器与数据库服务器之间建立一个Socket Connection(对于Type4 的JDBC Driver而言)，连接建立之后，应用服务器和数据库服务器之间还需要交换若干次数据（验证用户密码，权限等），然后，数据库开始初始化连接会话句柄，记录联机日志，为此连接分配相应得处理进程和系统资源。<br /><br />系统如此忙碌，如果我们只是简单地仍过去两个SQL语句，然后就将此连接抛弃，实在可惜，而数据库连接池技术正是为了解决这个问题。<br />数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接，并对外暴露数据库连接获取或返回方法。如图<br /> <br />   数据库连接池基本原理示意<br />外部使用者可以通过getConnection方法获取连接，使用完毕后再通过releaseConnection方法将连接返回，注意此时连接并没有关闭，而是由连接池管理器回收，并为下一次使用做好准备。<br />数据连接池技术带来下面的优势：<br />1.	资源重用<br />由于数据库连接得以重用，避免了频繁创建，释放连接引起的大量性能开销。在减少系统消耗的基础上，另一方面也增进了系统运行环境的平稳性（减少内存碎片以及数据库临时进程/线程的数量）。<br />2.	更快的系统响应速度<br />数据库连接池仔初始化过程中，往往已经创建了若干数据库连接池置于池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言，直接利用现有可用连接，避免了数据库连接初始化和释放过程的时间开销，从而缩减了系统整体响应时间。<br />3.	新的资源分配手段<br />对于应用共享同一个数据库的系统而言，可在应用曾通过数据库连接池的配置，实现某一应用最大可用数据库连接数的限制，避免某一应用独占所有数据库资源。<br />4.	统一的连接管理，避免数据库连接泄露<br />在较为完备的数据库连接池实现中，可根据预先的连接占用超时设定，强制收回被占用连接。从而避免了常规数据库连接操作中可能出现的资源泄露。<br />一个最小化的数据库连接池实现：<br /><br /><pre name="code" class="java">package net.wanjin.lab.utils;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Vector;

public class ConnectionPool {
	
	private static Vector pool;
	private final int POOL_MAX_SIZE =20;
	/**
	 * 获取数据库连接
	 * 如果当前池中有可用连接，则将池中最后一个返回，如果没有，则新建一个返回
	 * @return
	 * @throws DBException
	 */
	public synchronized Connection getConnection() throws DBException{
		
		if(pool ==null){
			pool = new Vector();
		}
		Connection conn;
		if(pool.isEmpty()){
			conn = createConnection();
		}else{
			int last_idx = pool.size() -1;
			conn =(Connection)pool.get(last_idx);
			pool.remove(pool.get(last_idx));
			
		}
		return conn;
	}
	/**
	 * 将使用完毕的数据库连接放回备用池
	 * 
	 * 判断当前池中连接数是否已经超过阀值
	 * 如果超过，则关闭该连接
	 * 否则放回池中以备下次重用
	 * @param conn
	 */
	public synchronized void releaseConnection(Connection conn){
		
		if(pool.size()>POOL_MAX_SIZE){
			try{
				conn.close();
			}catch(SQLException e){
				e.printStackTrace();
			}
		}else{
			pool.add(conn);
		}
	}
	/**
	 * 读取数据库配置信息，并从数据库连接池中获得数据库连接
	 * @return
	 * @throws DBException
	 */
	private static Connection createConnection() throws DBException{
		Connection conn;
		try{
			Class.forName("oracle.jdbc.driver.OracleDriver");
			conn = DriverManager.getConnection("jdbc:oracle:thin:@1521:oracle","personal","personal");
			return conn;
		}catch(ClassNotFoundException e){
			throw new DBException("ClassNotFoundException when loading JDBC Driver");
		}catch(SQLException e){
			throw new DBexception("SQLException when loading JDBC Driver");
		}
	}

}</pre><br />完备的连接池实现固然错综复杂，但就其根本而言，还是源自同样的思想。<br /><br />先脱离连接池本身的具体实现，我们看看这段代码在实际应用中可能产生的问题，注意到，由于getConnection方法返回的是一个标准的JDBC Connection,程序员由于编成习惯，可能会习惯性的调用其close方法关闭连接。如此一来，连接无法得到重用，数据库连接池机制形同虚设。为了解决这个问题，比较好的途径有：<br />1.	Decorator 模式<br />2.	Dynamic Proxy 模式<br />下面我们就这两种模式进行一些探讨：<br />“Decorator模式的主要目的是利用一个对象，透明地为另一个对象添加新的功能”。<br />这句话是从GOF关于设计模式的经典著作《设计模式-可复用面向对象软件的基础》一书中关于Decorator模式的描述直译而来，可能比较难以理解。简单来讲，就是通过一个Decorator对原有对象进行封装，同时实现与原有对象相同的接口，从而得到一个基于原有对象的，对既有接口的增强性实现。<br /> <br /><br />对于前面所讨论的Connection释放的问题，理所当然，我们首先想到的是，如果能让JDBC Connection在执行close操作时自动返回到数据库连接池中，那么所有问题都将迎刃而解，但是，JDBC Connection自己显然无法根据实际情况判断何去何从。此时，引入Decorator模式来解决我们所面对的问题就非常合适。<br /><br />首先，我们引入一个ConnectionDecorator类：<br /><pre name="code" class="java">import java.sql.Connection;
import java.sql.SQLException;



public class ConnectionDecorator implements Connection {
	Connection dbconn;
	
	public ConnectionDecorator(Connection conn){
		this.dbconn = conn; //实际从数据库获得的Connection引用
	}
	
	/**
	 * 此方法将被子类覆盖，以实现数据库连接池的连接返回操作
	 * @see java.sql.Connection#close()
	 */
	public void close() throws SQLException{
		this.dbconn.close();
	}
	/**
	 * (non-Javadoc)
	 * @see java.sql.Connectionn#commit()
	 */
	public void commit() throws SQLException{
		this.dbconn.commit(); //调用实际连接的commit方法
	}
	//以下各个方法类似,均调用dbconn.xxx方法作为Connection接口中方法的简单实现.

}</pre><br />可以看到，ConnectionDecorator类实际上的对传入的数据库连接加上一个外壳，他实现了java.slq.Connection接口，不过本身并没有实现任何实际内容，只是简单的把方法的实现委托给运行期实际获得的Connection实例，，而从外部来看，ConnctionDecorator与普通Connection实例并没有什么区别，因为他们实现了同样的接口，对外提供了同样的功能调用。<br /><br />目标很清楚，通过这样的封装，我们的ConnectionDecorator对于外部的程序员而言，调用方法与普通的JDBC Connection完全相同，而在内部通过对ConnectionDecorator的修改，我们就可以透明地改变现有实现，为了增加新的特性：<br /><br /><pre name="code" class="java">package net.wanjin.lab.persistence.Decorator;

import java.sql.Connection;

impor