﻿<?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-study-随笔分类-J2EE介绍</title><link>http://www.blogjava.net/xixidabao/category/15382.html</link><description>GROW WITH JAVA</description><language>zh-cn</language><lastBuildDate>Sat, 21 Apr 2007 02:48:12 GMT</lastBuildDate><pubDate>Sat, 21 Apr 2007 02:48:12 GMT</pubDate><ttl>60</ttl><item><title>用 Spring 更好地处理 Struts 动作三种整合 Struts 应用程序与 Spring 的方式</title><link>http://www.blogjava.net/xixidabao/archive/2007/04/21/112404.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sat, 21 Apr 2007 02:43:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/04/21/112404.html</guid><description><![CDATA[<p><a name=N1008F><span class=atitle><font size=4>为什么 Spring 这么了不起？</font></span></a></p>
<p>Spring 的创立者 Rod Johnson 以一种批判的眼光看待 Java&#8482; 企业软件开发，并且提议很多企业难题都能够通过战略地使用 IOC 模式（也称作依赖注入）来解决。当 Rod 和一个具有奉献精神的开放源码开发者团队将这个理论应用于实践时，结果就产生了 Spring 框架。简言之，Spring 是一个轻型的容器，利用它可以使用一个外部 XML 配置文件方便地将对象连接在一起。每个对象都可以通过显示一个 JavaBean 属性收到一个到依赖对象的引用，留给您的简单任务就只是在一个 XML 配置文件中把它们连接好。</p>
<p>
<table cellSpacing=0 cellPadding=0 width="40%" align=right border=0>
    <tbody>
        <tr>
            <td width=10><img height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10></td>
            <td>
            <table cellSpacing=0 cellPadding=5 width="100%" border=1>
                <tbody>
                    <tr>
                        <td bgColor=#eeeeee><a name=N1009C><strong>IOC 和 Spring</strong></a><br>
                        <p>IOC 是一种使应用程序逻辑外在化的设计模式，所以它是被注入而不是被写入客户机代码中。将 IOC 与接口编程应用结合，就像 Spring 框架那样，产生了一种架构，这种架构能够减少客户机对特定实现逻辑的依赖。请参阅 <a href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#resources"><font color=#996699><u>参考资料</u></font></a> 了解更多关于 IOC 和 Spring 的信息。</p>
                        </td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
</p>
<p>依赖注入是一个强大的特性，但是 Spring 框架能够提供更多特性。Spring 支持可插拔的事务管理器，可以给您的事务处理提供更广泛的选择范围。它集成了领先的持久性框架，并且提供一个一致的异常层次结构。Spring 还提供了一种使用面向方面代码代替正常的面向对象代码的简单机制。</p>
<p>Spring AOP 允许您使用<em>拦截器</em> 在一个或多个执行点上拦截应用程序逻辑。加强应用程序在拦截器中的日志记录逻辑会产生一个更可读的、实用的代码基础，所以拦截器广泛用于日志记录。您很快就会看到，为了处理横切关注点，Spring AOP 发布了它自己的拦截器，您也可以编写您自己的拦截器。</p>
<p><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br></p>
<p><a name=N100B3><span class=atitle><font size=4>整合 Struts 和 Spring</font></span></a></p>
<p>与 Struts 相似，Spring 可以作为一个 MVC 实现。这两种框架都具有自己的优点和缺点，尽管大部分人同意 Struts 在 MVC 方面仍然是最好的。很多开发团队已经学会在时间紧迫的时候利用 Struts 作为构造高品质软件的基础。Struts 具有如此大的推动力，以至于开发团队宁愿整合 Spring 框架的特性，而不愿意转换成 Spring MVC。没必要进行转换对您来说是一个好消息。Spring 架构允许您将 Struts 作为 Web 框架连接到基于 Spring 的业务和持久层。最后的结果就是现在一切条件都具备了。</p>
<p>在接下来的小窍门中，您将会了解到三种将 Struts MVC 整合到 Spring 框架的方法。我将揭示每种方法的缺陷并且对比它们的优点。 一旦您了解到所有三种方法的作用，我将会向您展示一个令人兴奋的应用程序，这个程序使用的是这三种方法中我最喜欢的一种。</p>
<p><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br></p>
<p><a name=N100BF><span class=atitle><font size=4>三个小窍门</font></span></a></p>
<p>接下来的每种整合技术（或者窍门）都有自己的优点和特点。我偏爱其中的一种，但是我知道这三种都能够加深您对 Struts 和 Spring 的理解。在处理各种不同情况的时候，这将给您提供一个广阔的选择范围。方法如下：</p>
<ul>
    <li>使用 Spring 的 <code>ActionSupport</code> 类整合 Structs
    <li>使用 Spring 的 <code>DelegatingRequestProcessor</code> 覆盖 Struts 的 <code>RequestProcessor </code>
    <li>将 Struts <code>Action</code> 管理委托给 Spring 框架 </li>
</ul>
<p><a name=N100E4><span class=smalltitle><strong><font size=3>装载应用程序环境</font></strong></span></a></p>
<p>无论您使用哪种技术，都需要使用 Spring 的 <code>ContextLoaderPlugin</code> 为 Struts 的 <code>ActionServlet</code> 装载 Spring 应用程序环境。就像添加任何其他插件一样，简单地向您的 struts-config.xml 文件添加该插件，如下所示：</p>
<p>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">&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;
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>前面已经提到过，在 <a href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#download"><font color=#996699><u>下载</u></font></a> 部分，您能够找到这三个完全可使用的例子的完整源代码。每个例子都为一个书籍搜索应用程序提供一种不同的 Struts 和 Spring 的整合方法。您可以在这里看到例子的要点，但是您也可以下载应用程序以查看所有的细节。</p>
<p><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br></p>
<p><a name=N10101><span class=atitle><font size=4>窍门 1. 使用 Spring 的 ActionSupport</font></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>
<p><br><a name=N1011F><strong>清单 1. 使用 ActionSupport 整合 Struts</strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">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><strong>|(1)</strong></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><strong>|(2)</strong></span>
            BookService bookService =
            (BookService) ctx.getBean("bookService");   <span class=boldcode><strong>|(3)</strong></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");
            }
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<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>
<p><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br></p>
<p><a name=N10149><span class=atitle><font size=4>窍门 2. 覆盖 RequestProcessor</font></span></a></p>
<p>将 Spring 从 Struts 动作中分离是一个更巧妙的设计选择。分离的一种方法是使用 <code>org.springframework.web.struts.DelegatingRequestProcessor</code> 类来覆盖 Struts 的 <code>RequestProcessor</code> 处理程序，如清单 2 所示：</p>
<p><br><a name=N1015F><strong>清单 2. 通过 Spring 的 DelegatingRequestProcessor 进行整合</strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">&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><strong>|(1)</strong></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;
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>我利用了 <code>&lt;controller&gt;</code> 标记来用 <code>DelegatingRequestProcessor</code> 覆盖默认的 Struts <code>RequestProcessor</code>。下一步是在我的 Spring 配置文件中注册该动作，如清单 3 所示：</p>
<p><br><a name=N1017C><strong>清单 3. 在 Spring 配置文件中注册一个动作</strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">&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><strong>|(1)</strong></span>
            &lt;property name="bookService"&gt;
            &lt;ref bean="bookService"/&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            &lt;/beans&gt;
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>注意：在 (1) 处，我使用名称属性注册了一个 bean，以匹配 struts-config 动作映射名称。<code>SearchSubmit</code> 动作揭示了一个 JavaBean 属性，允许 Spring 在运行时填充属性，如清单 4 所示：</p>
<p><br><a name=N10191><strong>清单 4. 具有 JavaBean 属性的 Struts 动作</strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">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><strong>| (1)</strong></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><strong>|(2)</strong></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");
            }
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<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>
<p><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br></p>
<p><a name=N101B7><span class=atitle><font size=4>窍门 3. 将动作管理委托给 Spring</font></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>
<p><br><a name=N101D0><strong>清单 5. Spring 整合的委托方法</strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">&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><strong>|(1)</strong></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;
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<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>
<p><br><a name=N101F0><strong>清单 6. 在 Spring 环境中注册一个 Struts 动作</strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">&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;
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><font face="Lucida Console"><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></font></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><font face="Lucida Console"><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br></font>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><font face="Lucida Console"><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></font></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br></p>
<p><a name=N101F7><span class=atitle><font size=4>动作委托的优点</font></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>
<p><br>
<table cellSpacing=0 cellPadding=0 width="100%" border=0>
    <tbody>
        <tr>
            <td><img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"><br><img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0></td>
        </tr>
    </tbody>
</table>
<table class=no-print cellSpacing=0 cellPadding=0 align=right>
    <tbody>
        <tr align=right>
            <td><img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"><br>
            <table cellSpacing=0 cellPadding=0 border=0>
                <tbody>
                    <tr>
                        <td vAlign=center><img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0><br></td>
                        <td vAlign=top align=right><a class=fbox href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#main"><strong><font color=#996699><u>回页首</u></font></strong></a></td>
                    </tr>
                </tbody>
            </table>
            </td>
        </tr>
    </tbody>
</table>
<br><br></p>
<p><a name=N10210><span class=atitle><font size=4>拦截 Struts</font></span></a></p>
<p>前面提到过，通过将 Struts 动作委托给 Spring 框架而整合 Struts 和 Spring 的一个主要的优点是：您可以将 Spring 的 AOP 拦截器应用于您的 Struts 动作。通过将 Spring 拦截器应用于 Struts 动作，您可以用最小的代价处理横切关注点。</p>
<p>虽然 Spring 提供很多内置拦截器，但是我将向您展示如何创建自己的拦截器并把它应用于一个 Struts 动作。为了使用拦截器，您需要做三件事：</p>
<ol>
    <li>创建拦截器。
    <li>注册拦截器。
    <li>声明在何处拦截代码。 </li>
</ol>
<p>这看起来非常简单的几句话却非常强大。例如，在清单 7 中，我为 Struts 动作创建了一个日志记录拦截器。 这个拦截器在每个方法调用之前打印一句话：</p>
<p><br><a name=N10230><strong>清单 7. 一个简单的日志记录拦截器</strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">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!");
            }
            }
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>这个拦截器非常简单。<code>before()</code> 方法在拦截点中每个方法之前运行。在本例中，它打印出一句话，其实它可以做您想做的任何事。下一步就是在 Spring 配置文件中注册这个拦截器，如清单 8 所示：</p>
<p><br><a name=N10242><strong>清单 8. 在 Spring 配置文件中注册拦截器</strong></a><br>
<table cellSpacing=0 cellPadding=5 width="100%" bgColor=#eeeeee border=1>
    <tbody>
        <tr>
            <td>
            <pre><code class=section>
            <font face="Lucida Console">&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><strong>|(1)</strong></span>
            &lt;!-- AutoProxies --&gt;
            &lt;bean name="loggingAutoProxy"
            class="org.springframework.aop.framework.autoproxy.
            BeanNameAutoProxyCreator"&gt; <span class=boldcode><strong>|(2)</strong></span>
            &lt;property name="beanNames"&gt;
            &lt;value&gt;/searchSubmit&lt;/valuesgt; <span class=boldcode><strong>|(3)</strong></span>
            &lt;/property&gt;
            &lt;property name="interceptorNames"&gt;
            &lt;list&gt;
            &lt;value&gt;logger&lt;/value&gt; <span class=boldcode><strong>|(4)</strong></span>
            &lt;/list&gt;
            &lt;/property&gt;
            &lt;/bean&gt;
            &lt;/beans&gt;
            </font></code></pre>
            </td>
        </tr>
    </tbody>
</table>
<br></p>
<p>您可能已经注意到了，清单 8 扩展了 <a href="http://www-128.ibm.com/developerworks/cn/java/j-sr2.html#code6"><font color=#996699><u>清单 6</u></font></a> 中所示的应用程序以包含一个拦截器。具体细节如下：</p>
<ul>
    <li>在 (1) 处，我注册了这个拦截器。
    <li>在 (2) 处，我创建了一个 bean 名称自动代理，它描述如何应用拦截器。还有其他的方法定义拦截点，但是这种方法常见而简便。
    <li>在 (3) 处，我将 Struts 动作注册为将被拦截的 bean。如果您想要拦截其他的 Struts 动作，则只需要在 "beanNames" 下面创建附加的 <code>&lt;value&gt;</code> 标记。
    <li>在 (4) 处，当拦截发生时，我执行了在 (1) 处创建的拦截器 bean 的名称。这里列出的所有拦截器都应用于&#8220;beanNames&#8221;。 </li>
</ul>
<p>就是这样。就像这个例子所展示的，将您的 Struts 动作置于 Spring 框架的控制之下，为处理您的 Struts 应用程序提供了一系列全新的选择。在本例中，使用动作委托可以轻松地利用 Spring 拦截器提高 Struts 应用程序中的日志记录能力。</p>
<img src ="http://www.blogjava.net/xixidabao/aggbug/112404.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-04-21 10:43 <a href="http://www.blogjava.net/xixidabao/archive/2007/04/21/112404.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式之Observer </title><link>http://www.blogjava.net/xixidabao/archive/2007/04/18/111546.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Wed, 18 Apr 2007 03:14:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/04/18/111546.html</guid><description><![CDATA[<div class=postTitle><a class=postTitle2 id=viewpost1_TitleUrl href="http://www.blogjava.net/Sprite-bei/archive/2007/04/16/110976.html"><u><font color=#800080>设计模式之Observer</font></u></a> </div>
<p><span>Java深入到一定程度,就不可避免的碰到设计模式(design pattern)这一概念,了解设计模式,将使自己对java中的接口或抽象类应用有更深的理解.设计模式在java的中型系统中应用广泛,遵循一定的编程模式,才能使自己的代码便于理解,易于交流,Observer(观察者)模式是比较常用的一个模式,尤其在界面设计中应用广泛,而本站所关注的是Java在电子商务系统中应用,因此想从电子商务实例中分析Observer的应用.</span></p>
<p>虽然网上商店形式多样<span>,每个站点有自己的特色,但也有其一般的共性,单就"商品的变化,以便及时通知订户"这一点,是很多网上商店共有的模式,这一模式类似Observer patern.</span></p>
<p>具体的说<span>,如果网上商店中商品在名称 价格等方面有变化,如果系统能自动通知会员,将是网上商店区别传统商店的一大特色.这就需要在商品product中加入Observer这样角色,以便product细节发生变化时,Observer能自动观察到这种变化,并能进行及时的update或notify动作.</span></p>
<p><span></span></p>
<p><span>Java的API还为为我们提供现成的Observer接口Java.util.Observer.我们只要直接使用它就可以.</span></p>
<p>我们必须<span>extends Java.util.Observer才能真正使用它:<br>1.提供Add/Delete observer的方法;<br>2.提供通知(notisfy) 所有observer的方法;</span></p>
<table cellSpacing=3 cellPadding=0 width="80%" bgColor=#cccccc border=0>
    <tbody>
        <tr>
            <td>
            <p><span>import java.util.Observable;</span></p>
            <p><span>public class product extends Observable {</span></p>
            <p><span>&nbsp;private String name;</span></p>
            <p><span>&nbsp;private float price;<br>&nbsp;public product(){<br>&nbsp;}</span></p>
            <p><span>&nbsp;public String getName() {<br>&nbsp;&nbsp;return name;<br>&nbsp;}</span></p>
            <p><span>&nbsp;public void setName(String name) {<br>&nbsp;&nbsp;this.name = name;<br>&nbsp;&nbsp;//设置变化点 <br>&nbsp;&nbsp;setChanged();<br>&nbsp;&nbsp;notifyObservers(name);</span></p>
            <p><span>&nbsp;}</span></p>
            <p><span>&nbsp;public float getPrice() {<br>&nbsp;&nbsp;return price;<br>&nbsp;}</span></p>
            <p><span>&nbsp;public void setPrice(float price) {<br>&nbsp;&nbsp;this.price = price;<br>&nbsp;&nbsp;//设置变化点<br>&nbsp;&nbsp;setChanged();<br>&nbsp;&nbsp;notifyObservers(new Float(price));</span></p>
            <p><span>&nbsp;}</span></p>
            <p><span>&nbsp;//以下可以是数据库更新 插入命令.<br>&nbsp;public void saveToDb() {<br>&nbsp;&nbsp;//&nbsp; .....................</span></p>
            <p><span>&nbsp;}</span></p>
            <p><span>&nbsp;public static void main(String[] args) {<br>&nbsp;&nbsp;product p=new product();<br>&nbsp;&nbsp;p.addObserver(new NameObserver());<br>&nbsp;&nbsp;p.addObserver(new PriceObserver());<br>&nbsp;&nbsp;p.setPrice(100);<br>&nbsp; p.setName("name");<br>&nbsp;}<br>}</span></p>
            </td>
        </tr>
    </tbody>
</table>
<p><span><br>我们注意到,在product类中 的setXXX方法中,我们设置了 notify(通知)方法, 当调用setXXX,实际上就触发了notisfyObservers方法,这将通知相应观察者应该采取行动了.</span></p>
<p>下面看看这些观察者的代码<span>,他们究竟采取了什么行动:</span></p>
<table cellSpacing=3 cellPadding=0 width="99%" border=0>
    <tbody>
        <tr>
            <td>
            <p><span>//观察者NameObserver主要用来对产品名称(name)进行观察的<br>public class NameObserver implements Observer{</span></p>
            <p>　　<span>private String name=null;</span></p>
            <p>　　<span>public void update(Observable obj,Object arg){<br><br>　　　　if (arg <a href="http://www-900.ibm.com/developerWorks/java/l-leditor/index.shtml" target=_blank><u><font color=#0000ff>instanceof</font></u></a> String){</span></p>
            <p>　　　　<span> name=(String)arg;<br>　　　　 //产品名称改变值在name中<br>　　　　 System.out.println("NameObserver :name changet to "+name);</span></p>
            <p>　　　　<span>}</span></p>
            <p>　　<span>}</span></p>
            <p><span>}</span></p>
            <p><span>//观察者PriceObserver主要用来对产品价格(price)进行观察的<br>public class PriceObserver implements Observer{</span></p>
            <p>　　<span>private float price=0;</span></p>
            <p>　　<span>public void update(Observable obj,Object arg){<br><br>　　　　if (arg instanceof Float){</span></p>
            <p>　　　　<span> price=((Float)arg).floatValue();<br>　　<br>　　　　 System.out.println("PriceObserver :price changet to "+price);</span></p>
            <p>　　　　<span>}</span></p>
            <p>　　<span>}</span></p>
            <p><span>}<br><br></span></p>
            </td>
        </tr>
    </tbody>
</table>
输出结果如下：<br>PriceObserver :price changet to 100.0<br>NameObserver :name changet to name<br><br>转自：<a href="http://www.blogjava.net/Sprite-bei/archive/2007/04/16/110976.html">http://www.blogjava.net/Sprite-bei/archive/2007/04/16/110976.html</a>
<img src ="http://www.blogjava.net/xixidabao/aggbug/111546.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-04-18 11:14 <a href="http://www.blogjava.net/xixidabao/archive/2007/04/18/111546.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Struts,Spring,Hibernate优缺点 </title><link>http://www.blogjava.net/xixidabao/archive/2007/04/14/110605.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sat, 14 Apr 2007 02:38:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/04/14/110605.html</guid><description><![CDATA[1.struts<br><br>struts框架具有组件的模块化，灵活性和重用性的优点，同时简化了基于MVC的web应用程序的开发。<br><br>优点：<br>Struts跟Tomcat、Turbine等诸多Apache项目一样，是开源软件，这是它的一大优点。使开发者能更深入的了解其内部实现机制。<br>除此之外，Struts的优点主要集中体现在两个方面：Taglib和页面导航。Taglib是Struts的标记库，灵活动用，能大大提高开发效率。另外，就目前国内的JSP开发者而言，除了使用JSP自带的常用标记外，很少开发自己的标记，或许Struts是一个很好的起点。<br>关于页面导航，我认为那将是今后的一个发展方向，事实上，这样做，使系统的脉络更加清晰。通过一个配置文件，即可把握整个系统各部分之间的联系，这对于后期的维护有着莫大的好处。尤其是当另一批开发者接手这个项目时，这种优势体现得更加明显。<br><br><br>另外，struts是业界"标准"（很多成功案例），学习资源丰富，HTML标签非常优秀<br><br>缺点：<br>Taglib是Struts的一大优势，但对于初学者而言，却需要一个持续学习的过程，甚至还会打乱你网页编写的习惯，但是，当你习惯了它时，你会觉得它真的很棒。<br>Struts将MVC的Controller一分为三，在获得结构更加清晰的同时，也增加了系统的复杂度。<br>ActionForms使用不便、无法进行单元测试（StrutsTestCase只能用于集成）<br><br><br><strong>【IT168技术文档</strong>】 <br>&nbsp;&nbsp;&nbsp; Struts跟Tomcat、Turbine等诸多Apache项目一样，是开源软件，这是它的一大优点。使开发者能更深入的了解其内部实现机制。 Struts开放源码框架的创建是为了使开发者在构建基于Java Servlet和JavaServer Pages（JSP）技术的Web应用时更加容易。Struts框架为开放者提供了一个统一的标准框架，通过使用Struts作为基础，开发者能够更专注于应用程序的商业逻辑。Struts框架本身是使用Java Servlet和JavaServer Pages技术的一种Model-View-Controller（MVC）实现. <br>具体来讲,Struts的优点有:&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 1. 实现MVC模式，结构清晰,使开发者只关注业务逻辑的实现.&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 2. 有丰富的tag可以用 ,Struts的标记库(Taglib)，如能灵活动用，则能大大提高开发效率。另外，就目前国内的JSP开发者而言，除了使用JSP自带的常用标记外，很少开发自己的标记，或许Struts是一个很好的起点。&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 3. 页面导航.页面导航将是今后的一个发展方向，事实上，这样做，使系统的脉络更加清晰。通过一个配置文件，即可把握整个系统各部分之间的联系，这对于后期的维护有着莫大的好处。尤其是当另一批开发者接手这个项目时，这种优势体现得更加明显。&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 4. 提供Exception处理机制 .&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 5. 数据库链接池管理&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 6. 支持I18N&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 缺点:&nbsp;<br>&nbsp;&nbsp;&nbsp; 一、 转到展示层时，需要配置forward，每一次转到展示层，相信大多数都是直接转到jsp，而涉及到转向，需要配置forward，如果有十个展示层的jsp，需要配置十次struts，而且还不包括有时候目录、文件变更，需要重新修改forward，注意，每次修改配置之后，要求重新部署整个项目，而tomcate这样的服务器，还必须重新启动服务器，如果业务变更复杂频繁的系统，这样的操作简单不可想象。现在就是这样，几十上百个人同时在线使用我们的系统，大家可以想象一下，我的烦恼有多大。<br>&nbsp;<br>&nbsp;&nbsp;&nbsp; 二、 Struts 的Action必需是thread－safe方式，它仅仅允许一个实例去处理所有的请求。所以action用到的所有的资源都必需统一同步，这个就引起了线程安全的问题。&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 三、 测试不方便. Struts的每个Action都同Web层耦合在一起，这样它的测试依赖于Web容器，单元测试也很难实现。不过有一个Junit的扩展工具Struts TestCase可以实现它的单元测试。&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 四、 类型的转换. Struts的FormBean把所有的数据都作为String类型，它可以使用工具Commons-Beanutils进行类型转化。但它的转化都是在Class级别，而且转化的类型是不可配置的。类型转化时的错误信息返回给用户也是非常困难的。&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 五、 对Servlet的依赖性过强. Struts处理Action时必需要依赖ServletRequest 和ServletResponse，所有它摆脱不了Servlet容器。&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 六、 前端表达式语言方面.Struts集成了JSTL，所以它主要使用JSTL的表达式语言来获取数据。可是JSTL的表达式语言在Collection和索引属性方面处理显得很弱。&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 七、 对Action执行的控制困难. Struts创建一个Action，如果想控制它的执行顺序将会非常困难。甚至你要重新去写Servlet来实现你的这个功能需求。&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 八、 对Action 执行前和后的处理. Struts处理Action的时候是基于class的hierarchies，很难在action处理前和后进行操作。&nbsp;<br><br>&nbsp;&nbsp;&nbsp; 九、 对事件支持不够. 在struts中，实际是一个表单Form对应一个Action类(或DispatchAction)，换一句话说：在Struts中实际是一个表单只能对应一个事件，struts这种事件方式称为application event，application event和component event相比是一种粗粒度的事件。&nbsp;<br>&nbsp;&nbsp;&nbsp;&nbsp;<br>&nbsp;&nbsp;&nbsp; Struts重要的表单对象ActionForm是一种对象，它代表了一种应用，这个对象中至少包含几个字段，这些字段是Jsp页面表单中的input字段，因为一个表单对应一个事件，所以，当我们需要将事件粒度细化到表单中这些字段时，也就是说，一个字段对应一个事件时，单纯使用Struts就不太可能，当然通过结合JavaScript也是可以转弯实现的。<br><br><br><br><br>2．Hibernate<br>Hibernate是一个开放源代码的对象关系映射框架，它对JDBC进行了非常轻量级的对象封装，使得Java程序员可以随心所欲的使用对象编程思维来操纵数据库。<br>Hibernate可以应用在任何使用JDBC的场合，既可以在Java的客户端程序实用，也可以在Servlet/JSP的Web应用中使用，最具革命意义的是，Hibernate可以在应用EJB的J2EE架构中取代CMP，完成数据持久化的重任。<br>大多数开发机构经常采取创建各自独立的数据持久层。一旦底层的数据结构发生改变，那么修改应用的其余部分使之适应这种改变的代价将是十分巨大的。Hibernate适时的填补了这一空白，它为Java应用提供了一个易用的、高效率的对象关系映射框架。hibernate是个轻量级的持久性框架，功能却非常丰富。<br><br>优点：<br>a.??????? Hibernate 使用 Java 反射机制 而不是字节码增强程序来实现透明性。<br>b.??????? ?Hibernate 的性能非常好，因为它是个轻量级框架。 映射的灵活性很出色。<br>c.??????? 它支持各种关系数据库，从一对一到多对多的各种复杂关系。 <br><br><br>缺点：它限制您所使用的对象模型。(例如，一个持久性类不能映射到多个表)其独有的界面和可怜的市场份额也让人不安，尽管如此，Hibernate 还是以其强大的发展动力减轻了这些风险。其他的开源持久性框架也有一些，不过都没有 Hibernate 这样有市场冲击力。<br><br><span class=tpc_content>上面回贴情绪有点激动，希望谅解，我不是因为有人批评Hibernate而感到不快，而是因为帖子里面的观点实在让我觉得荒谬。不管觉得Hibernate好也吧，不好也吧，我唯一觉得遗憾的是，在中文论坛里面找不到一个对Hibernate的真正高水平的评价。在TSS上有一个关于Hibernate的hot thread，跟了几百贴，其中包括Hibernate作者Gavin和LiDO JDO的CTO，对于JDO和Hibernate有过一些激烈的争论，我曾经耐心的看了一遍，仍然没有发现针对Hibernate真正有力的攻击，那些所谓的攻击无非针对Hibernate没有一个GUI的配置工具，没有商业公司支持，没有标准化等等这些站不住脚的理由。<br><br>补充几点我的意见：<br><br>一、Hibernate是JDBC的轻量级的对象封装，它是一个独立的对象持久层框架，和App Server，和EJB没有什么必然的联系。Hibernate可以用在任何JDBC可以使用的场合，例如Java应用程序的数据库访问代码，DAO接口的实现类，甚至可以是BMP里面的访问数据库的代码。从这个意义上来说，Hibernate和EB不是一个范畴的东西，也不存在非此即彼的关系。<br><br>二、Hibernate是一个和JDBC密切关联的框架，所以Hibernate的兼容性和JDBC驱动，和数据库都有一定的关系，但是和使用它的Java程序，和App Server没有任何关系，也不存在兼容性问题。<br><br>三、Hibernate不能用来直接和Entity Bean做对比，只有放在整个J2EE项目的框架中才能比较。并且即使是放在软件整体框架中来看，Hibernate也是做为JDBC的替代者出现的，而不是Entity Bean的替代者出现的，让我再列一次我已经列n次的框架结构：<br><br>传统的架构：<br>1) Session Bean &lt;-&gt; Entity Bean &lt;-&gt; DB<br>为了解决性能障碍的替代架构：<br>2) Session Bean &lt;-&gt; DAO &lt;-&gt; JDBC &lt;-&gt; DB<br>使用Hibernate来提高上面架构的开发效率的架构：<br>3) Session Bean &lt;-&gt; DAO &lt;-&gt; Hibernate &lt;-&gt; DB <br><br>就上面3个架构来分析：<br>1、内存消耗：采用JDBC的架构2无疑是最省内存的，Hibernate的架构3次之，EB的架构1最差。<br><br>2、运行效率：如果JDBC的代码写的非常优化，那么JDBC架构运行效率最高，但是实际项目中，这一点几乎做不到，这需要程序员非常精通JDBC，运用Batch语句，调整PreapredStatement的Batch Size和Fetch Size等参数，以及在必要的情况下采用结果集cache等等。而一般情况下程序员是做不到这一点的。因此Hibernate架构表现出最快的运行效率。EB的架构效率会差的很远。<br><br>3、开发效率：在有JBuilder的支持下以及简单的项目，EB架构开发效率最高，JDBC次之，Hibernate最差。但是在大的项目，特别是持久层关系映射很复杂的情况下，Hibernate效率高的惊人，JDBC次之，而EB架构很可能会失败。<br><br>4、分布式，安全检查，集群，负载均衡的支持<br>由于有SB做为Facade，3个架构没有区别。<br><br>四、EB和Hibernate学习难度在哪里？<br><br>EB的难度在哪里？不在复杂的XML配置文件上，而在于EB运用稍微不慎，就有严重的性能障碍。所以难在你需要学习很多EJB设计模式来避开性能问题，需要学习App Server和EB的配置来优化EB的运行效率。做EB的开发工作，程序员的大部分精力都被放到了EB的性能问题上了，反而没有更多的精力关注本身就主要投入精力去考虑的对象持久层的设计上来。<br><br>Hibernate难在哪里？不在Hibernate本身的复杂，实际上Hibernate非常的简单，难在Hibernate太灵活了。<br><br>当你用EB来实现持久层的时候，你会发现EB实在是太笨拙了，笨拙到你根本没有什么可以选择的余地，所以你根本就不用花费精力去设计方案，去平衡方案的好坏，去费脑筋考虑选择哪个方案，因为只有唯一的方案摆在你面前，你只能这么做，没得选择。<br><br>Hibernate相反，它太灵活了，相同的问题，你至少可以设计出十几种方案来解决，所以特别的犯难，究竟用这个，还是用那个呢？这些方案之间到底有什么区别呢？他们的运行原理有什么不同？运行效率哪个比较好？光是主键生成，就有七八种方案供你选择，你为难不为难？集合属性可以用Set，可以用List，还可以用Bag，到底哪个效率高，你为难不为难？查询可以用iterator，可以用list，哪个好，有什么区别？你为难不为难？复合主键你可以直接在hbm里面配置，也可以自定义CustomerType，哪种比较好些？你为难不为难？对于一个表，你可以选择单一映射一个对象，也可以映射成父子对象，还可以映射成两个1:1的对象，在什么情况下用哪种方案比较好，你为难不为难？<br><br>这个列表可以一直开列下去，直到你不想再看下去为止。当你面前摆着无数的眼花缭乱的方案的时候，你会觉得幸福呢？还是悲哀呢？如果你是一个负责的程序员，那么你一定会仔细研究每种方案的区别，每种方案的效率，每种方案的适用场合，你会觉得你已经陷入进去拔不出来了。如果是用EB，你第一秒种就已经做出了决定，根本没得选择，比如说集合属性，你只能用Collection，如果是Hibernate，你会在Bag，List和Set之间来回犹豫不决，甚至搞不清楚的话，程序都没有办法写。<br><br><br></span><br><br><br><br>3． Spring<br>它是一个开源的项目，而且目前非常活跃；它基于IoC（Inversion of Control，反向控制）和AOP的构架多层j2ee系统的框架，但它不强迫你必须在每一层 中必须使用Spring，因为它模块化的很好，允许你根据自己的需要选择使用它的某一个模块；它实现了很优雅的MVC，对不同的数据访问技术提供了统一的 接口，采用IoC使得可以很容易的实现bean的装配，提供了简洁的AOP并据此实现Transcation Managment，等等<br>优点 <br>? ?a. Spring能有效地组织你的中间层对象，不管你是否选择使用了EJB。如果你仅仅使用了Struts或其他为J2EE的 API特制的framework，Spring致力于解决剩下的问题。 <br>? ?b. Spring能消除在许多工程中常见的对Singleton的过多使用。根据我的经验，这是一个很大的问题，它降低了系统的可测试性和面向对象的程度。 <br>? ?c. 通过一种在不同应用程序和项目间一致的方法来处理配置文件，Spring能消除各种各样自定义格式的属性文件的需要。曾经对某个类要寻找的是哪个魔法般的属性项或系统属性感到不解，为此不得不去读Javadoc甚至源编码？有了Spring，你仅仅需要看看类的JavaBean属性。Inversion of Control的使用（在下面讨论）帮助完成了这种简化。 <br>??d.? 通过把对接口编程而不是对类编程的代价几乎减少到没有，Spring能够促进养成好的编程习惯。 <br>??e. Spring被设计为让使用它创建的应用尽可能少的依赖于他的APIs。在Spring应用中的大多数业务对象没有依赖于Spring。 <br>??f. 使用Spring构建的应用程序易于单元测试。 <br>??g.? Spring能使EJB的使用成为一个实现选择,而不是应用架构的必然选择。你能选择用POJOs或local EJBs来实现业务接口，却不会影响调用代码。 <br>??h. Spring帮助你解决许多问题而无需使用EJB。Spring能提供一种EJB的替换物，它们适用于许多web应用。例如，Spring能使用AOP提供声明性事务管理而不通过EJB容器，如果你仅仅需要与单个数据库打交道，甚至不需要一个JTA实现。 <br>? i. ?Spring为数据存取提供了一个一致的框架,不论是使用的是JDBC还是O/R mapping产品（如Hibernate）。 <br>Spring确实使你能通过最简单可行的解决办法来解决你的问题。而这是有有很大价值的。<br><br>?缺点：使用人数不多、jsp中要写很多代码、控制器过于灵活，缺少一个公用控制器 
<img src ="http://www.blogjava.net/xixidabao/aggbug/110605.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-04-14 10:38 <a href="http://www.blogjava.net/xixidabao/archive/2007/04/14/110605.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>设计模式：用Java动态代理实现AOP </title><link>http://www.blogjava.net/xixidabao/archive/2007/04/08/109208.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sun, 08 Apr 2007 02:55:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/04/08/109208.html</guid><description><![CDATA[<table width="100%" align=center>
    <tbody>
        <tr>
            <td class=title align=middle>设计模式：用Java动态代理实现AOP </td>
        </tr>
        <tr>
            <td align=middle><a href="http://www.itisedu.com/"><u><font color=#800080>http://www.itisedu.com</font></u></a>&nbsp;&nbsp;&nbsp;2006-12-7 15:08:52&nbsp;&nbsp;&nbsp;<a href="http://www.itisedu.com/"><u><font color=#800080>中科永联</font></u></a> </td>
        </tr>
        <tr>
            <td align=left>[关键字]设计模式 Java 动态 代理 AOP </td>
        </tr>
        <tr>
            <td>目前整个开发社区对<a href="http://www.itisedu.com/phrase/200604231341385.html" target=_new><u><font color=#0000ff>AOP</font></u></a>(Aspect&nbsp;Oriented&nbsp;Programing)推崇备至，也涌现出大量支持AOP的优秀<a href="http://www.itisedu.com/phrase/200604241001145.html" target=_new><u><font color=#0000ff>Framework</font></u></a>,--Spring,&nbsp;<a href="http://www.itisedu.com/phrase/200605111438435.html" target=_new><u><font color=#0000ff>JAC</font></u></a>,&nbsp;<a href="http://www.itisedu.com/phrase/200605111343075.html" target=_new><u><font color=#0000ff>Jboss</font></u></a>&nbsp;AOP&nbsp;等等。AOP似乎一时之间成了潮流。Java初学者不禁要发出感慨，<a href="http://www.itisedu.com/phrase/200604240956125.html" target=_new><u><font color=#0000ff>OOP</font></u></a>还没有学通呢，又来AOP。本文不是要在理论上具体阐述何为AOP,&nbsp;为何要进行AOP&nbsp;.&nbsp;要详细了解学习AOP可以到它老家http://aosd.net去瞧瞧。这里只是意图通过一个简单的例子向初学者展示一下如何来进行AOP. <br><br>　　为了简单起见，例子没有没有使用任何第三方的AOP&nbsp;Framework,&nbsp;而是利用Java语言本身自带的动态代理功能来实现AOP.&nbsp; <br><br>　　让我们先回到AOP本身，AOP主要应用于日志记录，性能统计，安全控制,事务处理等方面。它的主要意图就要将日志记录，性能统计，安全控制等等代码从商业逻辑代码中清楚的划分出来，我们可以把这些行为一个一个单独看作系统所要解决的问题，就是所谓的面向问题的编程(不知将AOP译作面向问题的编程是否欠妥)。通过对这些行为的分离，我们希望可以将它们独立地配置到商业方法中，而要改变这些行为也不需要影响到商业方法代码。&nbsp; <br><br>　　假设系统由一系列的BusinessObject所完成业务逻辑功能，系统要求在每一次业务逻辑处理时要做日志记录。这里我们略去具体的业务逻辑代码。&nbsp; <br><br>public&nbsp;interface&nbsp;BusinessInterface&nbsp;{&nbsp; <br>　public&nbsp;void&nbsp;processBusiness();&nbsp; <br>}&nbsp; <br><br>public&nbsp;<a href="http://www.itisedu.com/phrase/200604231359565.html" target=_new><u><font color=#0000ff>class</font></u></a>&nbsp;BusinessObject&nbsp;implements&nbsp;BusinessInterface&nbsp;{&nbsp; <br>　private&nbsp;Logger&nbsp;logger&nbsp;=&nbsp;Logger.getLogger(this.getClass().getName());&nbsp; <br>　public&nbsp;void&nbsp;processBusiness(){&nbsp; <br>　　try&nbsp;{&nbsp; <br>　　　logger.info("start&nbsp;to&nbsp;processing...");&nbsp; <br>　　　//business&nbsp;logic&nbsp;here.&nbsp; <br>　　　System.out.println(&#8220;here&nbsp;is&nbsp;business&nbsp;logic&#8221;);&nbsp; <br>　　　logger.info("end&nbsp;processing...");&nbsp; <br>　　}&nbsp;catch&nbsp;(Exception&nbsp;e){&nbsp; <br>　　　logger.info("exception&nbsp;happends...");&nbsp; <br>　　　//exception&nbsp;handling&nbsp; <br>　　}&nbsp; <br>　}&nbsp; <br>}&nbsp; <br><br>　　这里处理商业逻辑的代码和日志记录代码混合在一起，这给日后的维护带来一定的困难，并且也会造成大量的代码重复。完全相同的log代码将出现在系统的每一个BusinessObject中。&nbsp; <br><br>按照AOP的思想，我们应该把日志记录代码分离出来。要将这些代码分离就涉及到一个问题，我们必须知道商业逻辑代码何时被调用，这样我们好插入日志记录代码。一般来说要截获一个方法，我们可以采用回调方法或者动态代理。动态代理一般要更加灵活一些，目前多数的AOP&nbsp;Framework也大都采用了动态代理来实现。这里我们也采用动态代理作为例子。&nbsp; <br><br>　　<a href="http://www.itisedu.com/phrase/200604181646475.html" target=_new><u><font color=#0000ff>JDK</font></u></a>1.2以后提供了动态代理的支持，<a href="http://www.itisedu.com/phrase/200604232224305.html" target=_new><u><font color=#0000ff>程序</font></u></a>员通过实现java.lang.reflect.InvocationHandler接口提供一个执行处理器，然后通过java.lang.reflect.Proxy得到一个代理<a href="http://www.itisedu.com/phrase/200603090845215.html" target=_new><u><font color=#0000ff>对象</font></u></a>，通过这个代理对象来执行商业方法,在商业方法被调用的同时，执行处理器会被自动调用。&nbsp; <br><br>　　有了JDK的这种支持，我们所要做的仅仅是提供一个日志处理器。&nbsp; <br><br>public&nbsp;class&nbsp;LogHandler&nbsp;implements&nbsp;InvocationHandler&nbsp;{&nbsp; <br><br>　private&nbsp;Logger&nbsp;logger&nbsp;＝&nbsp;Logger.getLogger(this.getClass().getName());&nbsp; <br>　　private&nbsp;<a href="http://www.itisedu.com/phrase/200604231338435.html" target=_new><u><font color=#0000ff>Object</font></u></a>&nbsp;delegate;&nbsp; <br>　　public&nbsp;LogHandler(Object&nbsp;delegate){&nbsp; <br>　　　this.delegate&nbsp;=&nbsp;delegate;&nbsp; <br>　　}&nbsp; <br><br>　public&nbsp;Object&nbsp;invoke(Object&nbsp;proxy,&nbsp;Method&nbsp;method,&nbsp;Object[]&nbsp;args)&nbsp;throws&nbsp;Throwable&nbsp;{&nbsp; <br>　　Object&nbsp;o&nbsp;=&nbsp;null;&nbsp; <br>　　try&nbsp;{&nbsp; <br>　　　logger.info("method&nbsp;stats..."&nbsp;＋&nbsp;method);&nbsp; <br>　　　o&nbsp;=&nbsp;method.invoke(delegate,args);&nbsp; <br>　　　logger.info("method&nbsp;ends..."&nbsp;+&nbsp;method);&nbsp; <br>　　}&nbsp;catch&nbsp;(Exception&nbsp;e){&nbsp; <br>　　　logger.info("Exception&nbsp;happends...");&nbsp; <br>　　　//excetpion&nbsp;handling.&nbsp; <br>　　}&nbsp; <br>　　return&nbsp;o;&nbsp; <br>　}&nbsp; <br>}&nbsp; <br><br>　　现在我们可以把BusinessObject里面的所有日志处理代码全部去掉了。&nbsp; <br><br>public&nbsp;class&nbsp;BusinessObject&nbsp;implements&nbsp;BusinessInterface&nbsp;{&nbsp; <br><br>　private&nbsp;Logger&nbsp;logger&nbsp;=&nbsp;Logger.getLogger(this.getClass().getName());&nbsp; <br>　public&nbsp;void&nbsp;processBusiness(){&nbsp; <br>　　//business&nbsp;processing&nbsp; <br>　　System.out.println(&#8220;here&nbsp;is&nbsp;business&nbsp;logic&#8221;);&nbsp; <br>　}&nbsp; <br>}&nbsp; <br><br>　　<a href="http://www.itisedu.com/phrase/200603082208195.html" target=_new><u><font color=#0000ff>客户端</font></u></a>调用商业方法的代码如下： <br><br>BusinessInterface&nbsp;businessImp&nbsp;=&nbsp;new&nbsp;BusinessObject();&nbsp; <br><br>InvocationHandler&nbsp;handler&nbsp;=&nbsp;new&nbsp;LogHandler(businessImp);&nbsp; <br><br>BusinessInterface&nbsp;proxy&nbsp;=&nbsp;(BusinessInterface)&nbsp;Proxy.newProxyInstance(&nbsp; <br>　businessImp.getClass().getClassLoader(),&nbsp; <br>　businessImp.getClass().getInterfaces(),&nbsp; <br>　handler);&nbsp; <br><br>proxy.processBusiness();&nbsp; <br><br>　　程序输出如下：&nbsp; <br><br>INFO:&nbsp;method&nbsp;stats...&nbsp; <br>here&nbsp;is&nbsp;business&nbsp;logic&nbsp; <br>INFO:&nbsp;method&nbsp;ends...&nbsp; <br><br>　　至此我们的第一次小尝试算是完成了。可以看到，采用AOP之后，日志记录和业务逻辑代码完全分开了，以后要改变日志记录的话只需要修改日志记录处理器就行了，而<a href="http://www.itisedu.com/phrase/200603091835065.html" target=_new><u><font color=#0000ff>业务对象</font></u></a>本身（BusinessObject）无需做任何修改。并且这个日志记录不会造成重复代码了，所有的商业处理对象都可以重用这个日志处理器。&nbsp; <br><br>　　当然在实际应用中，这个例子就显得太粗糙了。由于JDK的动态代理并没有直接支持一次注册多个InvocationHandler，那么我们对业务处理方法既要日志记录又要性能统计时，就需要自己做一些变通了。一般我们可以自己定义一个Handler接口，然后维护一个队列存放所有Handler,&nbsp;当InvocationHandler被触发的时候我们依次调用自己的Handler。所幸的是目前几乎所有的AOP&nbsp;Framework都对这方面提供了很好的支持.这里推荐大家使用Spring。 </td>
        </tr>
        <tr>
            <td align=right>来源：赛迪网论坛 </td>
        </tr>
        <tr>
            <td align=middle>中科永联高级技术培训中心本着技术交流、共享精神，部分信息资料为网上收集，如果本页内容侵犯了您的版权，请立刻通知我们，我们将在1个工作日内作出妥善处理，并向您致以诚挚的歉意。 </td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/xixidabao/aggbug/109208.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-04-08 10:55 <a href="http://www.blogjava.net/xixidabao/archive/2007/04/08/109208.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>模式与J2EE </title><link>http://www.blogjava.net/xixidabao/archive/2007/04/08/109205.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sun, 08 Apr 2007 02:42:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/04/08/109205.html</guid><description><![CDATA[<table width="100%" align=center>
    <tbody>
        <tr>
            <td class=title align=middle>模式与J2EE </td>
        </tr>
        <tr>
            <td align=middle><a href="http://www.itisedu.com/"><u><font color=#0000ff>http://www.itisedu.com</font></u></a>&nbsp;&nbsp;&nbsp;2006-5-12 15:56:28&nbsp;&nbsp;&nbsp;<a href="http://www.itisedu.com/"><u><font color=#0000ff>中科永联</font></u></a> </td>
        </tr>
        <tr>
            <td align=left>[关键字]模式 J2EE </td>
        </tr>
        <tr>
            <td>
            <p>
            <p><font face=Verdana></font></p>
            <font face=Verdana><br>信息工程是以当前数据系统为基础，在一个企业或企业的主要部门，关于建设<a href="http://www.itisedu.com/phrase/200603011147495.html" target=_new><u><font color=#0000ff>信息系统</font></u></a>的规 划、分析、设计和构成的一整套相互关联的正规化、自动化的技术应用。
            <p>&#160;</p>
            <p>--- J<a href="http://www.itisedu.com/phrase/200603091620485.html" target=_new><u><font color=#0000ff>AM</font></u></a><a href="http://www.itisedu.com/phrase/200604231411155.html" target=_new><u><font color=#0000ff>ES</font></u></a> Martin&nbsp; </p>
            <p>&nbsp;&nbsp;&nbsp; 正如上面信息工程的创始人James Martin为信息工程的概念所做定义<a href="http://www.itisedu.com/phrase/200603090857555.html" target=_new><u><font color=#0000ff>类</font></u></a>似，<a href="http://www.itisedu.com/phrase/200603061709535.html" target=_new><u><font color=#0000ff>模式</font></u></a>(<a href="http://www.itisedu.com/phrase/200604231320535.html" target=_new><u><font color=#0000ff>pattern</font></u></a>s)的创始人建筑师Chri<a href="http://www.itisedu.com/phrase/200604231411575.html" target=_new><u><font color=#0000ff>ST</font></u></a>opher Alexander在&lt;模式语言，1977、1979&gt;一书中对模式的概念进行了如下描述(附注：书名后面的年份代表在各个不同时期的作品，下面形式同上)：<br>每一个模式描述了一个在我们周围不断重复发生的问题，以及该问题的解决方案的核心。这样，你就能一次又一次的使用该解决方案而不必做重复劳动。每个模式是由三部分组成的一个规则，这个规则描述特定环境、问题和解决方案之间的关系。简单的说，没有一个模式是独立的实体，每个模式都存在着相互支持，但支持的程度不同：大的模式可以内嵌小的模式，同等层次的模式并列存在，而小的模式被嵌入到大的模式之中。 <br>--- Christopher Alexander&nbsp;&nbsp; <br>&nbsp;&nbsp;&nbsp; 模式的概念在<a href="http://www.itisedu.com/phrase/200604232134205.html" target=_new><u><font color=#0000ff>软件</font></u></a>行业被采用以后，得到的广泛的发展，现在已经存在许多种<a href="http://www.itisedu.com/phrase/200603051002565.html" target=_new><u><font color=#0000ff>类型</font></u></a>的模式应用，其中比较有名的箸作有：<a href="http://www.itisedu.com/phrase/200603061811045.html" target=_new><u><font color=#0000ff>GoF</font></u></a>(Erich <a href="http://www.itisedu.com/phrase/200604230903435.html" target=_new><u><font color=#0000ff>GA</font></u></a>mma、Richard Helm、Ralph Johnson和John Vliss<a href="http://www.itisedu.com/phrase/200604261459505.html" target=_new><u><font color=#0000ff>IDE</font></u></a>s四人，简称：Gang of Four[GoF])的&lt;<a href="http://www.itisedu.com/phrase/200603061631585.html" target=_new><u><font color=#0000ff>设计模式</font></u></a>，1995&gt;，Martin Fowler的&lt;分析模式，1997&gt;，Frank Buschmann等人的&lt;<a href="http://www.itisedu.com/phrase/200603122156385.html" target=_new><u><font color=#0000ff>体系结构</font></u></a>模式，1996、2000&gt;、Jim O.Coplien、Niel Harrison等人的&lt;编程模式，1995、1996、1998、1999&gt;和Deepak Alur等人的&lt;<a href="http://www.itisedu.com/phrase/200603091447335.html" target=_new><u><font color=#0000ff>J2EE</font></u></a>核心模式，2001&gt;等，其中最具影响的是GoF的&lt;设计模式&gt;一书，书中详细讨论了三种类型，共23种模式。好的设计源于工作中经验的积累，当设计使用标准的模板以模式的方式进行交流时，模式就成了交流和重用的强大机制，并且可以改善设计和开发软件的方式。模式可以帮助我们在一个特定的环境里整理并记录已知的可重现的问题及解决方案，并且通过模式来与他人交流这些知识，这些模式可以解决在不同环境中重复出现的问题。模式可以使设计重复使用，重复使用已知的解决方案可以缩短设计和开发应用的周期，有效的使用模式，可以使我们远离重复投资的怪圈。模式的关键在于简单性和可重现性。<br>&nbsp;&nbsp;&nbsp; 举一个模式应用的简单示例。例如，在你的便携式<a href="http://www.itisedu.com/phrase/200604231234155.html" target=_new><u><font color=#0000ff>电脑</font></u></a>上运行一个进程中的<a href="http://www.itisedu.com/phrase/200603090845215.html" target=_new><u><font color=#0000ff>对象</font></u></a>，并且这些对象需要和运行在另一进程中的别的对象通信，也许这一进程并不在你的便携式电脑上，而在别的地方。你又不想让系统中的对象担心如何找寻网上的其他对象或者执行<a href="http://www.itisedu.com/phrase/200604241405415.html" target=_new><u><font color=#0000ff>远程过程调用</font></u></a>。这时，可以使用代理(Proxy模式，详见GoF的&lt;设计模式&gt;一书)模式来解决这个问题，你能做的事就是为这个远程对象在你的本地过程中建立一个代理对象，该代理对象和远程对象具有相同的接口。你的本地对象利用通常处理过程中的<a href="http://www.itisedu.com/phrase/200603090938465.html" target=_new><u><font color=#0000ff>消息</font></u></a>发送来和代理交谈。这时代理对象负责把消息传送给实在对象，而不管实在对象位于何处。<br>&nbsp;&nbsp;&nbsp; 由于下面要讲的Java 2平台的企业版(J2EE)应用模式中很多用到了设计模式与<a href="http://www.itisedu.com/phrase/200603121222205.html" target=_new><u><font color=#0000ff>重构</font></u></a>(<a href="http://www.itisedu.com/phrase/200604232047545.html" target=_new><u><font color=#0000ff>Refactoring</font></u></a>)的概念，所以在此有必要再概要介绍一下重构的概念。重构已经被证明可以阻止软件的腐朽和衰败，关于重构方面的有名箸作当然首推是Martin Fowler所写的&lt;重构，1999&gt;一书了，书中详细介绍了重构的七大类型，共70余种具体的重构手法，同时也指出测试机制在重构中的重要性。书中Martin Fowler对重构的概念进行了详细说明：<br>&nbsp;&nbsp;&nbsp; 重构是对软件内部结构的一种调整，目地是在不改变[软件之可察行为]的前提下，提高其可理解性，降低其修改成本。重构是一种有纪律的、经过训练的、有条不紊的<a href="http://www.itisedu.com/phrase/200604232224305.html" target=_new><u><font color=#0000ff>程序</font></u></a>整理方法，可以将整理过程中不小心引入的错误的机率降到最低，本质上说，重构就是在代码写好之后改进它的设计。重构之前，首先检查自己是否有一套可靠的测试机制，这些测试必须有我检验能力。</p>
            <p><br>--- Martin Fowler&nbsp; </p>
            <p>&nbsp;&nbsp;&nbsp; 建立于Java编程语言和Java技术基础之上的J2EE平台是最适用于企业级分布式环境的应用结构，它被设计为面向多层体系的结构。J2EE包含下面关键技术：Java服务器页面(Java Service Page，JSP)、<a href="http://www.itisedu.com/phrase/200603091005185.html" target=_new><u><font color=#0000ff>Servlet</font></u></a>、<a href="http://www.itisedu.com/phrase/200603091138035.html" target=_new><u><font color=#0000ff>Enterprise JavaBean</font></u></a>s(<a href="http://www.itisedu.com/phrase/200604241156485.html" target=_new><u><font color=#0000ff>EJB</font></u></a>)<a href="http://www.itisedu.com/phrase/200603302222545.html" target=_new><u><font color=#0000ff>组件</font></u></a>、<a href="http://www.itisedu.com/phrase/200604261600355.html" target=_new><u><font color=#0000ff>Java消息服务</font></u></a>(<a href="http://www.itisedu.com/phrase/200604261607125.html" target=_new><u><font color=#0000ff>Java Message Service</font></u></a>，<a href="http://www.itisedu.com/phrase/200604261605045.html" target=_new><u><font color=#0000ff>JMS</font></u></a>)、<a href="http://www.itisedu.com/phrase/200604151904545.html" target=_new><u><font color=#0000ff>JDBC</font></u></a>和Java命名与目录接口(Java Naming and <a href="http://www.itisedu.com/phrase/200604231343545.html" target=_new><u><font color=#0000ff>DI</font></u></a>r<a href="http://www.itisedu.com/phrase/200604222051415.html" target=_new><u><font color=#0000ff>EC</font></u></a>tory Interface，JNDI)。由于J2EE平台是<a href="http://www.itisedu.com/phrase/200604161254415.html" target=_new><u><font color=#0000ff>分层</font></u></a>系统，所以我们将J2EE的层次模型化，这个模型使得我们将职责逻辑地分到不同的层中，共分了五个层次：客户层、表示层、业务层、集成层和资源层。因为客户层和资源层并不是J2EE平台直接关注的问题，所以后面介绍的15个J2EE应用模式全部属于上面五层中的中间三层，其中表示层模式包含与Servlet和JSP技术相关的模式、业务层模式包含与EJB技术有关的模式、集成层模式包含与JMS和J<a href="http://www.itisedu.com/phrase/200604231245475.html" target=_new><u><font color=#0000ff>DB</font></u></a>C有关的模式。具体模式可参看下面表格：</p>
            <p>表一：表示层模式
            <table id=AutoNumber1 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="89%" border=1>
                <tbody>
                    <tr>
                        <td align=middle width="50%"><strong>模式名</strong></td>
                        <td align=middle width="50%"><strong>简单描述</strong></td>
                    </tr>
                    <tr>
                        <td width="50%">截取过滤器(Intercepting Filter) </td>
                        <td width="50%">促进请求的预先处理和后处理</td>
                    </tr>
                    <tr>
                        <td width="50%">前端控制器(Front Controller) </td>
                        <td width="50%">提供请求处理的集中控制器</td>
                    </tr>
                    <tr>
                        <td width="50%"><a href="http://www.itisedu.com/phrase/200603141659315.html" target=_new><u><font color=#0000ff>视图</font></u></a>助手(View Helper) </td>
                        <td width="50%">把与表示层格式化无关的逻辑封装到助手组件</td>
                    </tr>
                    <tr>
                        <td width="50%">复合视图(Comp<a href="http://www.itisedu.com/phrase/200604232131175.html" target=_new><u><font color=#0000ff>OS</font></u></a>ite View) </td>
                        <td width="50%">从原子的子组件创建一个聚集视图</td>
                    </tr>
                    <tr>
                        <td width="50%">工作者服务(Service To Worker) </td>
                        <td width="50%">合并分发者组件、前端控制器和视图助手模式</td>
                    </tr>
                    <tr>
                        <td width="50%">分发者视图(Dispatcher View) </td>
                        <td width="50%">合并分发者组件、前端控制器和视图助手模式，把许多动作推迟到视图处理</td>
                    </tr>
                </tbody>
            </table>
            <br><br>表二：业务层模式<br>
            <table id=AutoNumber2 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="90%" border=1>
                <tbody>
                    <tr>
                        <td align=middle width="50%"><strong>模式名</strong></td>
                        <td align=middle width="50%"><strong>简单描述</strong></td>
                    </tr>
                    <tr>
                        <td width="50%">业务委托(Business Delegate) </td>
                        <td width="50%">把表示层和服务层分隔开，并且提供服务的外观和代理接口</td>
                    </tr>
                    <tr>
                        <td width="50%">值对象(Value <a href="http://www.itisedu.com/phrase/200604231338435.html" target=_new><u><font color=#0000ff>object</font></u></a>) </td>
                        <td width="50%">通过减少网络对话，以加速层之间的数据交换</td>
                    </tr>
                    <tr>
                        <td width="50%">会话外观(Session Facade) </td>
                        <td width="50%">隐藏<a href="http://www.itisedu.com/phrase/200603091835065.html" target=_new><u><font color=#0000ff>业务对象</font></u></a>复性，集中化<a href="http://www.itisedu.com/phrase/200603110944215.html" target=_new><u><font color=#0000ff>工作流</font></u></a>处理</td>
                    </tr>
                    <tr>
                        <td width="50%">复合实体(Composite Entity) </td>
                        <td width="50%">通过把参数相关的对象分组进单个实体bean，表示设计粗粒度实体bean的最好经验</td>
                    </tr>
                    <tr>
                        <td width="50%">值对象组装器(Value Object <a href="http://www.itisedu.com/phrase/200604232104015.html" target=_new><u><font color=#0000ff>AS</font></u></a>sembler) </td>
                        <td width="50%">把来自多个数据源的值对象组装成一个复合值对象</td>
                    </tr>
                    <tr>
                        <td width="50%">值列表处理器(Value List Handler) </td>
                        <td width="50%">管理查询执行、结果缓冲、以及结果处理</td>
                    </tr>
                    <tr>
                        <td width="50%">服务定位器(Service Locator) </td>
                        <td width="50%">封装业务服务查找和创建的复杂性，定位业务服务工厂</td>
                    </tr>
                </tbody>
            </table>
            <br><br>表三：集成层模式<br>
            <table id=AutoNumber3 style="BORDER-COLLAPSE: collapse" borderColor=#111111 cellSpacing=0 cellPadding=0 width="91%" border=1>
                <tbody>
                    <tr>
                        <td align=middle width="50%"><strong>模式名</strong></td>
                        <td align=middle width="50%"><strong>简单描述</strong></td>
                    </tr>
                    <tr>
                        <td width="50%"><a href="http://www.itisedu.com/phrase/200604241352355.html" target=_new><u><font color=#0000ff>数据访问对象</font></u></a>(Data Access Object) </td>
                        <td width="50%">抽象数据源，提供对数据的透明访问</td>
                    </tr>
                    <tr>
                        <td width="50%">服务激发器(Service Activator) </td>
                        <td width="50%">加速EJB组件的异步处理</td>
                    </tr>
                </tbody>
            </table>
            </p>
            <p>&nbsp;&nbsp;&nbsp; 由于J2EE模式众多，篇幅有限，这里只概要介绍其中的一种应用模式 - 集成层的数据访问对象(<a href="http://www.itisedu.com/phrase/200604162200335.html" target=_new><u><font color=#0000ff>DAO</font></u></a>)模式，有兴趣的读者可以参看下面参考文献中的资料。<br>数据访问对象模式</p>
            <p>&nbsp;数据访问对象模式</p>
            <p>1、问题<br>&nbsp;&nbsp;&nbsp; 根据数据源不同，数据访问也不同。根据存储的类型(关系<a href="http://www.itisedu.com/phrase/200602271218062.html" target=_new><u><font color=#0000ff>数据库</font></u></a>、<a href="http://www.itisedu.com/phrase/200603101726185.html" target=_new><u><font color=#0000ff>面向对象</font></u></a>数据库等)和供应商不同，持久性存储(比如数据库)的访问差别也很大。当业务组件(如会话bean)或表示组件(如助手组件)需要访问某数据源时，它们可以使用合适的<a href="http://www.itisedu.com/phrase/200604241228185.html" target=_new><u><font color=#0000ff>API</font></u></a>来获得连接性，以及操作该数据源。但是在这些组件中包含连接性和数据访问代码会引入这些组件及数据源实现之间的紧密耦合。组件中这类代码依赖性使应用程序从某种数据源迁移到其它种类的数据源将变得非常麻烦和困难，当数据源变化时，组件也需要改变，以便于能够处理新类型的数据源。</p>
            <p>2、解决方案<br>&nbsp;&nbsp;&nbsp; 使用数据访问对象(DAO)来抽象和封装所有对数据源的访问。DAO管理着与数据源的连接以便于检索和存储数据，DAO实现了用来操作数据源的访问机制。依赖于DAO的业务组件为其<a href="http://www.itisedu.com/phrase/200603082208195.html" target=_new><u><font color=#0000ff>客户端</font></u></a>使用DAO提供了更简单的接口，DAO完全向客户端隐藏了数据源实现细节。由于当低层数据源实现变化时，DAO向客户端提供的接口不会变化，所以该模式允许DAO调整到不同的存储模式，而不会影响其客户端或业务组件。重要的是，DAO充当组件和数据源之间的适配器。</p>
            <p>3、实现策略<br>&nbsp;&nbsp;&nbsp; 通过调整抽象工厂(Abstract Factory)模式和工厂方法(Factory Method，这二个创建型模式的实现详情可参看GoF的&lt;设计模式&gt;一书)模式，DAO模式可以达到很高的灵活度。 </p>
            <p>当低层存储不会随着实现变化而变化时，可以使用工厂方法模式来实现该策略，以产生应用程序需要的大量DAO，如下面<a href="http://www.itisedu.com/phrase/200603071659325.html" target=_new><u><font color=#0000ff>类图</font></u></a>1所示。 <br>当低层存储随着实现的变化而变化时，策略可以通过使用抽象工厂模式而实现。抽象工厂可以基于工厂方法实现而创建，并可使用工厂方法实现，该策略提供一个DAO的抽象工厂对象，其中该对象可以构造多种类型的具体的DAO工厂，每个工厂支持一种不同类型的持久性存储实现。一旦你获取某特定实现的具体DAO工厂，你可以使用它来生成该实现中所支持和实现的DAO，如下面类图2所示。 </p>
            <p><img src="http://www.itisedu.com/manage/Upload/image/2006512155455670.jpg" border=0><br>&nbsp;</p>
            <p>4、应用<br>&nbsp;&nbsp;&nbsp; 当数据访问代码被直接嵌入到有其他不相关职责的某类中时，就会使修改变的十分困难。这时可以采用分离数据访问代码的解决方案，将数据访问代码抽取到一个新类中，并且把该新类逻辑或者物理地移动到离数据源比较近的位置，这样可以增强模块性和可重用性，如下面图3所示。具体作法可以使用提炼类(Extract <a href="http://www.itisedu.com/phrase/200604231359565.html" target=_new><u><font color=#0000ff>Class</font></u></a>，一种重构手法，细节可参看Martin的&lt;重构&gt;一书)方法创建一个新类，并将原来类中把数据访问代码移动到这个新的数据访问对象(DAO)类，使用这个新的DAO对象从控制器类中访问数据。<br>&nbsp;&nbsp;&nbsp; 示例：持久性逻辑被嵌入到一个使用新DAO对象管理的持久性的某企业新DAO对象中，把持久性代码和该企业新DAO对象代码结合起来会创建脆弱的、紧密耦合的代码。当持久性代码是该企业新DAO对象的一部分时，对该持久性存储的任何改动都要求更改该新DAO对象的持久性代码。这种耦合对企业新DAO对象代码维护会带来负面的影响。下面图4为运用分离数据访问对象方法对其进行重构改进后的结果。</p>
            <p><img src="http://www.itisedu.com/manage/Upload/image/2006512155535250.jpg" border=0></p>
            <p>&nbsp;&nbsp;&nbsp; 在15个J2EE模式中，每个模式都作用于设计模式和构架模式之间的某些方面。每个模式不是孤立存在的，需要其它模式的支持才能更加体现其含义和用处，为了最大限度的用好模式，还需要充分理解模式之间的关系。 </p>
            <p>&nbsp;参考文献 </p>
            <p>系统分析员教程 --- 罗晓沛等箸 <br>设计模式：可复用面向对象软件的元素 --- 李英军等译 <br>重构-改善既有代码的设计 --- 侯捷等译 <br>J2EE核心模式 --- 牛志奇等译 <br><a href="http://www.itisedu.com/phrase/200602271429302.html" target=_new><u><font color=#0000ff>UML</font></u></a>精粹(第二版) --- 徐家福译</p>
            </font></td>
        </tr>
    </tbody>
</table>
<img src ="http://www.blogjava.net/xixidabao/aggbug/109205.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-04-08 10:42 <a href="http://www.blogjava.net/xixidabao/archive/2007/04/08/109205.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Hibernate二级缓存攻略</title><link>http://www.blogjava.net/xixidabao/archive/2007/04/08/109196.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sun, 08 Apr 2007 02:04:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2007/04/08/109196.html</guid><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
<div id=contitle>
<h1>Hibernate二级缓存攻略</h1>
</div>
<div id=conauthor><span>2006-10-27 15:59 </span><span>作者： AreYouOK </span><span>出处： JAVAEYE </span><span>责任编辑：<a title=向本编辑提问 href="http://comments.yesky.com/t/%B7%BD%D6%DB/6,324/2612396.shtml" target=_blank>方舟</a> </span></div>
<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 很多人对二级缓存都不太了解，或者是有错误的认识，我一直想写一篇文章介绍一下hibernate的二级缓存的，今天终于忍不住了。 <br><br>　　我的经验主要来自hibernate2.1版本，基本原理和3.0、3.1是一样的，请原谅我的顽固不化。 <br><br>　　hibernate的session提供了一级缓存，每个session，对同一个id进行两次load，不会发送两条sql给数据库，但是session关闭的时候，一级缓存就失效了。<br><br>　　二级缓存是SessionFactory级别的全局缓存，它底下可以使用不同的缓存类库，比如ehcache、oscache等，需要设置hibernate.cache.provider_class，我们这里用ehcache，在2.1中就是 hibernate.cache.provider_class=net.sf.hibernate.cache.EhCacheProvider如果使用查询缓存，加上hibernate.cache.use_query_cache=true<br><br>　　缓存可以简单的看成一个Map，通过key在缓存里面找value。<br><br>　　<strong>Class的缓存</strong> <br><br>　　对于一条记录，也就是一个PO来说，是根据ID来找的，缓存的key就是ID，value是POJO。无论list，load还是iterate，只要读出一个对象，都会填充缓存。但是list不会使用缓存，而iterate会先取数据库select id出来，然后一个id一个id的load，如果在缓存里面有，就从缓存取，没有的话就去数据库load。假设是读写缓存，需要设置： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache usage="read-write"/＞ </td>
        </tr>
    </tbody>
</table>
<br>　　如果你使用的二级缓存实现是ehcache的话，需要配置ehcache.xml <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache name="com.xxx.pojo.Foo" maxElementsInMemory="500" eternal="false" timeToLiveSeconds="7200" timeToIdleSeconds="3600" overflowToDisk="true" /＞ </td>
        </tr>
    </tbody>
</table>
<br>　　其中eternal表示缓存是不是永远不超时，timeToLiveSeconds是缓存中每个元素（这里也就是一个POJO）的超时时间，如果eternal="false"，超过指定的时间，这个元素就被移走了。timeToIdleSeconds是发呆时间，是可选的。当往缓存里面put的元素超过500个时，如果overflowToDisk="true"，就会把缓存中的部分数据保存在硬盘上的临时文件里面。 <br><br>　　每个需要缓存的class都要这样配置。如果你没有配置，hibernate会在启动的时候警告你，然后使用defaultCache的配置，这样多个class会共享一个配置。 <br><br>　　当某个ID通过hibernate修改时，hibernate会知道，于是移除缓存。 <br><br>　　这样大家可能会想，同样的查询条件，第一次先list，第二次再iterate，就可以使用到缓存了。实际上这是很难的，因为你无法判断什么时候是第一次，而且每次查询的条件通常是不一样的，假如数据库里面有100条记录，id从1到100，第一次list的时候出了前50个id，第二次iterate的时候却查询到30至70号id，那么30-50是从缓存里面取的，51到70是从数据库取的，共发送1+20条sql。所以我一直认为iterate没有什么用，总是会有1+N的问题。 <br><br>　　（题外话：有说法说大型查询用list会把整个结果集装入内存，很慢，而iterate只select id比较好，但是大型查询总是要分页查的，谁也不会真的把整个结果集装进来，假如一页20条的话，iterate共需要执行21条语句，list虽然选择若干字段，比iterate第一条select id语句慢一些，但只有一条语句，不装入整个结果集hibernate还会根据数据库方言做优化，比如使用mysql的limit，整体看来应该还是list快。） <br><br>　　如果想要对list或者iterate查询的结果缓存，就要用到查询缓存了<br><br>　　<strong>查询缓存 </strong><br><br>　　首先需要配置hibernate.cache.use_query_cache=true <br><br>　　如果用ehcache，配置ehcache.xml，注意hibernate3.0以后不是net.sf的包名了：<br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache name="net.sf.hibernate.cache.StandardQueryCache" <br>maxElementsInMemory="50" eternal="false" timeToIdleSeconds="3600" <br>timeToLiveSeconds="7200" overflowToDisk="true"/＞ <br>＜cache name="net.sf.hibernate.cache.UpdateTimestampsCache" <br>maxElementsInMemory="5000" eternal="true" overflowToDisk="true"/＞ </td>
        </tr>
    </tbody>
</table>
<br>　　然后 <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>query.setCacheable(true);//激活查询缓存 <br>query.setCacheRegion("myCacheRegion");//指定要使用的cacheRegion，可选 </td>
        </tr>
    </tbody>
</table>
<br>　　第二行指定要使用的cacheRegion是myCacheRegion，即你可以给每个查询缓存做一个单独的配置，使用setCacheRegion来做这个指定，需要在ehcache.xml里面配置它： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache name="myCacheRegion" maxElementsInMemory="10" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" overflowToDisk="true" /＞ </td>
        </tr>
    </tbody>
</table>
<br>　　如果省略第二行，不设置cacheRegion的话，那么会使用上面提到的标准查询缓存的配置，也就是：net.sf.hibernate.cache.StandardQueryCache<br><br>　　对于查询缓存来说，缓存的key是根据hql生成的sql，再加上参数，分页等信息（可以通过日志输出看到，不过它的输出不是很可读，最好改一下它的代码）。 <br><br>　　比如hql： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>from Cat c where c.name like ? </td>
        </tr>
    </tbody>
</table>
<br>　　生成大致如下的sql： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>select * from cat c where c.name like ? </td>
        </tr>
    </tbody>
</table>
<br>　　参数是"tiger%"，那么查询缓存的key*大约*是这样的字符串（我是凭记忆写的，并不精确，不过看了也该明白了）： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>select * from cat c where c.name like ? , parameter:tiger% </td>
        </tr>
    </tbody>
</table>
<br>　　这样，保证了同样的查询、同样的参数等条件下具有一样的key。 <br><br>　　现在说说缓存的value，如果是list方式的话，value在这里并不是整个结果集，而是查询出来的这一串ID。也就是说，不管是list方法还是iterate方法，第一次查询的时候，它们的查询方式很它们平时的方式是一样的，list执行一条sql，iterate执行1+N条，多出来的行为是它们填充了缓存。但是到同样条件第二次查询的时候，就都和iterate的行为一样了，根据缓存的key去缓存里面查到了value，value是一串id，然后在到class的缓存里面去一个一个的load出来。这样做是为了节约内存。 <br><br>　　可以看出来，查询缓存需要打开相关类的class缓存。list和iterate方法第一次执行的时候，都是既填充查询缓存又填充class缓存的。 <br>这里还有一个很容易被忽视的重要问题，即打开查询缓存以后，即使是list方法也可能遇到1+N的问题！相同条件第一次list的时候，因为查询缓存中找不到，不管class缓存是否存在数据，总是发送一条sql语句到数据库获取全部数据，然后填充查询缓存和class缓存。但是第二次执行的时候，问题就来了，如果你的class缓存的超时时间比较短，现在class缓存都超时了，但是查询缓存还在，那么list方法在获取id串以后，将会一个一个去数据库load！因此，class缓存的超时时间一定不能短于查询缓存设置的超时时间！如果还设置了发呆时间的话，保证class缓存的发呆时间也大于查询的缓存的生存时间。这里还有其他情况，比如class缓存被程序强制evict了，这种情况就请自己注意了。<br><br>　　另外，如果hql查询包含select字句，那么查询缓存里面的value就是整个结果集了。<br><br>　　当hibernate更新数据库的时候，它怎么知道更新哪些查询缓存呢？ <br><br>　　hibernate在一个地方维护每个表的最后更新时间，其实也就是放在上面net.sf.hibernate.cache.UpdateTimestampsCache所指定的缓存配置里面。 <br><br>　　当通过hibernate更新的时候，hibernate会知道这次更新影响了哪些表。然后它更新这些表的最后更新时间。每个缓存都有一个生成时间和这个缓存所查询的表，当hibernate查询一个缓存是否存在的时候，如果缓存存在，它还要取出缓存的生成时间和这个缓存所查询的表，然后去查找这些表的最后更新时间，如果有一个表在生成时间后更新过了，那么这个缓存是无效的。 <br><br>　　可以看出，只要更新过一个表，那么凡是涉及到这个表的查询缓存就失效了，因此查询缓存的命中率可能会比较低。<br><br><strong>Collection缓存 <br><br></strong>　　需要在hbm的collection里面设置：<br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache usage="read-write"/＞ </td>
        </tr>
    </tbody>
</table>
<br>　　假如class是Cat，collection叫children，那么ehcache里面配置 <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>＜cache name="com.xxx.pojo.Cat.children" <br>maxElementsInMemory="20" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="7200" <br>overflowToDisk="true" /＞ </td>
        </tr>
    </tbody>
</table>
<br>　　Collection的缓存和前面查询缓存的list一样，也是只保持一串id，但它不会因为这个表更新过就失效，一个collection缓存仅在这个collection里面的元素有增删时才失效。 <br><br>　　这样有一个问题，如果你的collection是根据某个字段排序的，当其中一个元素更新了该字段时，导致顺序改变时，collection缓存里面的顺序没有做更新。<br><br>　　<strong>缓存策略</strong> <br><br>　　只读缓存（read-only）：没有什么好说的 <br><br>　　读/写缓存（read-write）:程序可能要的更新数据 <br><br>　　不严格的读/写缓存（nonstrict-read-write）：需要更新数据，但是两个事务更新同一条记录的可能性很小，性能比读写缓存好 <br>事务缓存（transactional）：缓存支持事务，发生异常的时候，缓存也能够回滚，只支持jta环境，这个我没有怎么研究过<br><br>　　读写缓存和不严格读写缓存在实现上的区别在于，读写缓存更新缓存的时候会把缓存里面的数据换成一个锁，其他事务如果去取相应的缓存数据，发现被锁住了，然后就直接取数据库查询。 <br><br>　　在hibernate2.1的ehcache实现中，如果锁住部分缓存的事务发生了异常，那么缓存会一直被锁住，直到60秒后超时。 <br><br>　　不严格读写缓存不锁定缓存中的数据。 使用二级缓存的前置条件。<br><br>　　你的hibernate程序对数据库有独占的写访问权，其他的进程更新了数据库，hibernate是不可能知道的。你操作数据库必需直接通过hibernate，如果你调用存储过程，或者自己使用jdbc更新数据库，hibernate也是不知道的。hibernate3.0的大批量更新和删除是不更新二级缓存的，但是据说3.1已经解决了这个问题。 <br><br>　　这个限制相当的棘手，有时候hibernate做批量更新、删除很慢，但是你却不能自己写jdbc来优化，很郁闷吧。 <br><br>　　SessionFactory也提供了移除缓存的方法，你一定要自己写一些JDBC的话，可以调用这些方法移除缓存，这些方法是： <br><br>
<table borderColor=#cccccc width="90%" align=center bgColor=#e7e9e9 border=1>
    <tbody>
        <tr>
            <td>void evict(Class persistentClass) <br>Evict all entries from the second-level cache. <br>void evict(Class persistentClass, Serializable id) <br>Evict an entry from the second-level cache. <br>void evictCollection(String roleName) <br>Evict all entries from the second-level cache. <br>void evictCollection(String roleName, Serializable id) <br>Evict an entry from the second-level cache. <br>void evictQueries() <br>Evict any query result sets cached in the default query cache region. <br>void evictQueries(String cacheRegion) <br>Evict any query result sets cached in the named query cache region. </td>
        </tr>
    </tbody>
</table>
<br>　　不过我不建议这样做，因为这样很难维护。比如你现在用JDBC批量更新了某个表，有3个查询缓存会用到这个表，用evictQueries(String cacheRegion)移除了3个查询缓存，然后用evict(Class persistentClass)移除了class缓存，看上去好像完整了。不过哪天你添加了一个相关查询缓存，可能会忘记更新这里的移除代码。如果你的jdbc代码到处都是，在你添加一个查询缓存的时候，还知道其他什么地方也要做相应的改动吗？<br><br>　　<strong>总结</strong>： <br><br>　　不要想当然的以为缓存一定能提高性能，仅仅在你能够驾驭它并且条件合适的情况下才是这样的。hibernate的二级缓存限制还是比较多的，不方便用jdbc可能会大大的降低更新性能。在不了解原理的情况下乱用，可能会有1+N的问题。不当的使用还可能导致读出脏数据。 <br>如果受不了hibernate的诸多限制，那么还是自己在应用程序的层面上做缓存吧。 <br><br>　　在越高的层面上做缓存，效果就会越好。就好像尽管磁盘有缓存，数据库还是要实现自己的缓存，尽管数据库有缓存，咱们的应用程序还是要做缓存。因为底层的缓存它并不知道高层要用这些数据干什么，只能做的比较通用，而高层可以有针对性的实现缓存，所以在更高的级别上做缓存，效果也要好些吧。<br>
<img src ="http://www.blogjava.net/xixidabao/aggbug/109196.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2007-04-08 10:04 <a href="http://www.blogjava.net/xixidabao/archive/2007/04/08/109196.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE 全面简介</title><link>http://www.blogjava.net/xixidabao/archive/2006/09/11/69038.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Mon, 11 Sep 2006 13:22:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/09/11/69038.html</guid><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="760" align="center" border="0">
				<tbody>
						<tr>
								<td class="title" valign="center" align="middle" height="56">
										<b>
												<font color="#ff0000" size="3">J2EE 全面简介<br /><!-- #EndEditable --></font>
										</b>
								</td>
						</tr>
						<tr>
								<td class="formtitle" align="middle" height="40">
										<!-- #BeginEditable "2" -->作者：刘湛 (jeru@163.net) 武汉大学信息与计算科学系学士 选自：Matrix-与Java共舞<!-- #EndEditable --></td>
						</tr>
				</tbody>
		</table>
		<table height="65" cellspacing="0" cellpadding="0" width="760" align="center" border="0">
				<tbody>
						<tr>
								<td class="content" height="65">
										<!-- #BeginEditable "3" -->
										<table class="content" width="77%" align="center" border="0">
												<tbody>
														<tr>
																<td class="content" valign="top">
																		<span class="myp11">
																				<font id="zoom">
																				</font>
																		</span>
																		<span class="p11b">
																		</span>
																		<p>
																				<strong>J2EE 全面简介</strong>
																				<br />本文从五个方面对J2EE进行了比较全面的介绍。从J2EE的概念说起，到它的优势，到J2EE典型的四层模型，和它的框架结构，最后是J2EE十三种核心技术的一个简介。本文分门别类的对J2EE中的服务，组件，层次，容器，API都做了比较详细的介绍，相信看完此文，读者会对J2EE有一个更清晰的认识。 </p>
																		<p>
																				<strong>一. J2EE的概念</strong>
																				<br />目前，Java 2平台有3个版本，它们是适用于小型设备和智能卡的Java 2平台Micro版（Java 2 Platform Micro Edition，J2ME）、适用于桌面系统的Java 2平台标准版（Java 2 Platform Standard Edition，J2SE）、适用于创建服务器应用程序和服务的Java2平台企业版（Java 2 Platform Enterprise Edition J2EE）。<br />J2EE是一种利用Java 2平台来简化企业解决方案的开发、部署和管理相关的复杂问题的体系结构。J2EE技术的基础就是核心Java平台或Java 2平台的标准版，J2EE不仅巩固了标准版中的许多优点，例如"编写一次、随处运行"的特性、方便存取数据库的JDBC API、CORBA技术以及能够在Internet应用中保护数据的安全模式等等，同时还提供了对 EJB（Enterprise JavaBeans）、Java Servlets API、JSP（Java Server Pages）以及XML技术的全面支持。其最终目的就是成为一个能够使企业开发者大幅缩短投放市场时间的<br />体系结构。<br />J2EE体系结构提供中间层集成框架用来满足无需太多费用而又需要高可用性、高可靠性以及可扩展性的应用的需求。通过提供统一的开发平台，J2EE降低了开发多层应用的费用和复杂性，同时提供对现有应用程序集成强有力支持，完全支持Enterprise JavaBeans，有良好的向导支持打包和部署应用，添加目录支持，增强了安全机制，提高了性能。</p>
																		<p>
																				<strong>二. J2EE的优势</strong>
																				<br />J2EE为搭建具有可伸缩性、灵活性、易维护性的商务系统提供了良好的机制:<br />保留现存的IT资产: 由于企业必须适应新的商业需求，利用已有的企业信息系统方面的投资，而不是重新制定全盘方案就变得很重要。这样，一个以渐进的（而不是激进的，全盘否定的）方式建立在已有系统之上的服务器端平台机制是公司所需求的。J2EE架构可以充分利用用户原有的投资，如一些公司使用的BEA Tuxedo、IBM CICS, IBM Encina,、InpriseVisiBroker及Netscape Application Server。这之所以成为可能是因为J2EE拥有广泛的业界支持和一些重要的'企业计算'领域供应商的参与。每一个供应商都对现有的客户提供了不用废弃已有投资，进入可移植的J2EE领域的升级途径。由于基于J2EE平台的产品几乎能够在任何操作系统和硬件配置上运行，现有的操作系统和硬件也<br />能被保留使用。<br />高效的开发: J2EE允许公司把一些通用的、很繁琐的服务端任务交给中间件供应商去完成。这样开发人员可以集中精力在如何创建商业逻辑上，相应地缩短了开发时间。高级中间件供应商提供以下这些复杂的中间件服务:<br />状态管理服务 -- 让开发人员写更少的代码，不用关心如何管理状态，这样能够更快地完成程序开发。<br />持续性服务 -- 让开发人员不用对数据访问逻辑进行编码就能编写应用程序，能生成更轻巧，与数据库无关的应用程序，这种应用程序更易于开发与维护。<br />分布式共享数据对象CACHE服务 -- 让开发人员编制高性能的系统，极大提高整体部署的伸缩性。<br />支持异构环境: J2EE能够开发部署在异构环境中的可移植程序。基于J2EE的应用程序不依赖任何特定操作系统、中间件、硬件。因此设计合理的基于J2EE的程序只需开发一次就可部署到各种平台。这在典型的异构企业计算环境中是十分关键的。J2EE标准也允许客户订购与J2EE兼容的第三方的现成的组件，把他们部署到异构环境中，节省了由自己制订整个方案所需的费用。<br />可伸缩性: 企业必须要选择一种服务器端平台，这种平台应能提供极佳的可伸缩性去满足那些在他们系统上进行商业运作的大批新客户。基于J2EE平台的应用程序可被部署到各种操作系统上。例如可被部署到高端UNIX与大型机系统，这种系统单机可支持64至256个处理器。（这是NT服务器所望尘莫及的）J2EE领域的供应商提供了更为广泛的负载平衡策略。能消除系统中的瓶颈，允许多台服务器集成部署。这种部署可达数千个处理器，实现可高度伸缩的系统，满足未来商业应用的需要。<br />稳定的可用性: 一个服务器端平台必须能全天候运转以满足公司客户、合作伙伴的需要。因为INTERNET是全球化的、无处不在的，即使在夜间按计划停机也可能造成严重损失。若是意外停机，那会有灾难性后果。J2EE部署到可靠的操作环境中，他们支持长期的可用性。一些J2EE部署在WINDOWS环境中，客户也可选择健壮性能更好的操作系统如Sun Solaris、IBM OS/390。最健壮的操作系统可达到99.999%的可用性或每年只需5分钟停机时间。这是实时性很强商业系统理想的选择。</p>
																		<p>
																				<br />
																				<strong>三. J2EE 的四层模型</strong>
																				<br />J2EE使用多层的分布式应用模型，应用逻辑按功能划分为组件，各个应用组件根据他们所在的层分布在不同的机器上。事实上，sun设计J2EE的初衷正是为了解决两层模式(client/server)的弊端，在传统模式中，客户端担当了过多的角色而显得臃肿，在这种模式中，第一次部署的时候比较容易，但难于升级或改进，可伸展性也不理想，而且经常基于某种专有的协议――通常是某种数据库协议。它使得重用业务逻辑和界面逻辑非常困难。现在J2EE 的多层企业级应用模型将两层化模型中的不同层面切分成许多层。一个多层化应用能够为不同的每种服务提供一个独立的层，以下是 J2EE 典型的四层结构:<br />运行在客户端机器上的客户层组件<br />运行在J2EE服务器上的Web层组件<br />运行在J2EE服务器上的业务逻辑层组件<br />运行在EIS服务器上的企业信息系统(Enterprise information system)层软件<br />J2EE应用程序组件<br />J2EE应用程序是由组件构成的.J2EE组件是具有独立功能的软件单元，它们通过相关的类和文件组装成J2EE应用程序，并与其他组件交互。J2EE说明书中定义了以下的J2EE组件:<br />应用客户端程序和applets是客户层组件.<br />Java Servlet和JavaServer Pages(JSP)是web层组件.<br />Enterprise JavaBeans(EJB)是业务层组件.<br />客户层组件<br />J2EE应用程序可以是基于web方式的,也可以是基于传统方式的.<br />web 层组件J2EE web层组件可以是JSP 页面或Servlets.按照J2EE规范，静态的HTML页面和Applets不算是web层组件。web层可能包含某些 JavaBean 对象来处理用户输入，并把输入发送给运行在业务层上的enterprise bean 来进行处理。</p>
																		<p>业务层组件<br />业务层代码的逻辑用来满足银行，零售，金融等特殊商务领域的需要,由运行在业务层上的enterprise bean 进行处理. 下图表明了一个enterprise bean 是如何从客户端程序接收数据，进行处理(如果必要的话), 并发送到EIS 层储存的，这个过程也可以逆向进行。<br />有三种企业级的bean: 会话(session) beans, 实体(entity) beans, 和 消息驱动(message-driven) beans. 会话bean 表示与客户端程序的临时交互. 当客户端程序执行完后, 会话bean 和相关数据就会消失. 相反, 实体bean 表示数据库的表中一行永久的记录. 当客户端程序中止或服务器关闭时, 就会有潜在的服务保证实体bean 的数据得以保存.消息驱动 bean 结合了会话bean 和 JMS的消息监听器的特性, 允许一个业务层组件异步接收JMS 消息.</p>
																		<p>
																				<br />企业信息系统层<br />企业信息系统层处理企业信息系统软件包括企业基础建设系统例如企业资源计划 (ERP), 大型机事务处理, 数据库系统,和其它的遗留信息系统. 例如，J2EE 应用组件可能为了数据库连接需要访问企业信息系统</p>
																		<p>
																				<br />
																				<strong>四. J2EE 的结构</strong>
																				<br />这种基于组件，具有平台无关性的J2EE 结构使得J2EE 程序的编写十分简单，因为业务逻辑被封装成可复用的组件，并且J2EE 服务器以容器的形式为所有的组件类型提供后台服务. 因为你不用自己开发这种服务, 所以你可以集中精力解决手头的业务问题.</p>
																		<p>
																				<br />容器和服务<br />容器设置定制了J2EE服务器所提供得内在支持，包括安全，事务管理，JNDI(Java Naming and Directory Interface)寻址,远程连接等服务，以下列出最重要的几种服务：<br />J2EE安全(Security)模型可以让你配置 web 组件或enterprise bean ,这样只有被授权的用户才能访问系统资源. 每一客户属于一个特别的角色，而每个角色只允许激活特定的方法。你应在enterprise bean的布置描述中声明角色和可被激活的方法。由于这种声明性的方法，你不必编写加强安全性的规则。<br />J2EE 事务管理（Transaction Management）模型让你指定组成一个事务中所有方法间的关系，这样一个事务中的所有方法被当成一个单一的单元. 当客户端激活一个enterprise bean中的方法，容器介入一管理事务。因有容器管理事务，在enterprise bean中不必对事务的边界进行编码。要求控制分布式事务的代码会非常复杂。你只需在布置描述文件中声明enterprise bean的事务属性，而不用编写并调试复杂的代码。容器将读此文件并为你处理此enterprise bean的事务。<br />JNDI 寻址(JNDI Lookup)服务向企业内的多重名字和目录服务提供了一个统一的接口,这样应用程序组件可以访问名字和目录服务.<br />J2EE远程连接（Remote Client Connectivity）模型管理客户端和enterprise bean间的低层交互. 当一个enterprise bean创建后, 一个客户端可以调用它的方法就象它和客户端位于同一虚拟机上一样.<br />生存周期管理（Life Cycle Management）模型管理enterprise bean的创建和移除,一个enterprise bean在其生存周期中将会历经几种状态。容器创建enterprise bean，并在可用实例池与活动状态中移动他，而最终将其从容器中移除。即使可以调用enterprise bean的create及remove方法，容器也将会在后台执行这些任务。</p>
																		<p>
																				<br />数据库连接池（Database Connection Pooling）模型是一个有价值的资源。获取数据库连接是一项耗时的工作，而且连接数非常有限。容器通过管理连接池来缓和这些问题。<br />enterprise bean可从池中迅速获取连接。在bean释放连接之可为其他bean使用。<br />容器类型<br />J2EE应用组件可以安装部署到以下几种容器中去:<br />EJB 容器管理所有J2EE 应用程序中企业级bean 的执行. enterprise bean 和它们的容器运行在J2EE 服务器上.<br />Web 容器管理所有J2EE 应用程序中JSP页面和Servlet组件的执行. Web 组件和它们的容器运行在J2EE 服务器上.<br />应用程序客户端容器管理所有J2EE应用程序中应用程序客户端组件的执行. 应用程序客户端和它们的容器运行在J2EE 服务器上.<br />Applet 容器是运行在客户端机器上的web浏览器和 Java 插件的结合.<br /><br /><strong>五. J2EE的核心API与组件</strong><br />J2EE平台由一整套服务（Services）、应用程序接口（APIs）和协议构成，它对开发基于Web的多层应用提供了功能支持，下面对J2EE中的13种技术规范进行简单的描述(限于篇幅，这里只能进行简单的描述):<br />JDBC(Java Database Connectivity):<br />JDBC API为访问不同的数据库提供了一种统一的途径，象ODBC一样，JDBC对开发者屏蔽了一些细节问题，另外，JDCB对数据库的访问也具有平台无关性。<br />JNDI(Java Name and Directory Interface):<br />JNDI API被用于执行名字和目录服务。它提供了一致的模型来存取和操作企业级的资源如DNS和LDAP，本地文件系统，或应用服务器中的对象。<br /><br />EJB(Enterprise JavaBean):<br />J2EE技术之所以赢得某体广泛重视的原因之一就是EJB。它们提供了一个框架来开发和实施分布式商务逻辑，由此很显著地简化了具有可伸缩性和高度复杂的企业级应用的开发。EJB规范定义了EJB组件在何时如何与它们的容器进行交互作用。容器负责提供公用的服务，例如目录服务、事务管理、安全性、资源缓冲池以及容错性。但这里值得注意的是，EJB并不是实现J2EE的唯一途径。正是由于J2EE的开放性，使得有的厂商能够以一种和EJB平行的方式来达到同样的目的。<br />RMI(Remote Method Invoke):<br />正如其名字所表示的那样，RMI协议调用远程对象上方法。它使用了序列化方式在客户端和服务器端传递数据。RMI是一种被EJB使用的更底层的协议。<br />Java IDL/CORBA:<br />在Java IDL的支持下，开发人员可以将Java和CORBA集成在一起。 他们可以创建Java对象并使之可在CORBA ORB中展开, 或者他们还可以创建Java类并作为和其它ORB一起展开的CORBA对象的客户。后一种方法提供了另外一种途径，通过它Java可以被用于将你的新的应用和旧的系统相集成。<br />JSP(Java Server Pages):<br />JSP页面由HTML代码和嵌入其中的Java代码所组成。服务器在页面被客户端所请求以后对这些Java代码进行处理，然后将生成的HTML页面返回给客户端的浏览器。<br />Java Servlet:<br />Servlet是一种小型的Java程序，它扩展了Web服务器的功能。作为一种服务器端的应用，当被请求时开始执行，这和CGI Perl脚本很相似。Servlet提供的功能大多与JSP类似，不过实现的方式不同。JSP通常是大多数HTML代码中嵌入少量的Java代码，而servlets全部由Java写成并且生成HTML。<br />XML(Extensible Markup Language):<br />XML是一种可以用来定义其它标记语言的语言。它被用来在不同的商务过程中共享数据。<br />XML的发展和Java是相互独立的，但是，它和Java具有的相同目标正是平台独立性。通过将Java和XML的组合，您可以得到一个完美的具有平台独立性的解决方案。<br />JMS(Java Message Service):<br />MS是用于和面向消息的中间件相互通信的应用程序接口(API)。它既支持点对点的域，有支持发布/订阅(publish/subscribe)类型的域，并且提供对下列类型的支持：经认可的消息传递,事务型消息的传递，一致性消息和具有持久性的订阅者支持。JMS还提供了另一种方式来对您的应用与旧的后台系统相集成。<br />JTA(Java Transaction Architecture):<br />JTA定义了一种标准的API，应用系统由此可以访问各种事务监控。<br />JTS(Java Transaction Service):<br />JTS是CORBA OTS事务监控的基本的实现。JTS规定了事务管理器的实现方式。该事务管理器是在高层支持Java Transaction API (JTA)规范，并且在较底层实现OMG OTS specification的Java映像。JTS事务管理器为应用服务器、资源管理器、独立的应用以及通信资源管理器提供了事务服务。<br />JavaMail:<br />JavaMail是用于存取邮件服务器的API，它提供了一套邮件服务器的抽象类。不仅支持SMTP服务器，也支持IMAP服务器。<br />JTA(JavaBeans Activation Framework):<br />JavaMail利用JAF来处理MIME编码的邮件附件。MIME的字节流可以被转换成Java对象</p>
																		<p>
																				<br />参考资料：<br />《Develop n-tier application using J2EE》- Steven Gould<br />《The Business Benefits of EJB and J2EE Technologies over COM+ and Windows D<br />NA》<br />《The J2EE Tutorial》chapter overview - Monica Pawlan</p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/xixidabao/aggbug/69038.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-09-11 21:22 <a href="http://www.blogjava.net/xixidabao/archive/2006/09/11/69038.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE宠物商店</title><link>http://www.blogjava.net/xixidabao/archive/2006/09/11/69037.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Mon, 11 Sep 2006 13:17:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/09/11/69037.html</guid><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="760" align="center" border="0">
				<tbody>
						<tr>
								<td class="title" valign="center" align="middle" height="56">
										<b>J2EE宠物商店</b>
								</td>
						</tr>
						<tr>
								<td class="formtitle" align="middle" height="40">
										<font size="2">
												<!-- #BeginEditable "2" -->
												<font color="#000080">作者：◇ 谷和启</font>
												<span class="myp11">
														<font id="zoom" color="#000080">  来源：CSDN</font>
												</span>
												<!-- #EndEditable -->
										</font>
								</td>
						</tr>
				</tbody>
		</table>
		<table height="65" cellspacing="0" cellpadding="0" width="760" align="center" border="0">
				<tbody>
						<tr>
								<td class="content" height="65">
										<!-- #BeginEditable "3" -->
										<table width="85%" align="center" border="0">
												<tbody>
														<tr>
																<td class="content">
																		<span class="myp11">
																				<font id="zoom">　</font>
																		</span>
																		<p>　<font color="#000000">J2EE 是Java技术在企业运算上的应用，它包含多种运算标准，如EJB组件架构、JDBC数据库运算、JMS信息传递、Java Servlets/JSP等Web组件程序等，功能强大且内容博大精深。为了让使用者可以有一个设计J2EE架构应用程序的模板，Sun做出一套系统名为“Java宠物商店（Pet Store）”。这个网上宠物店的范例是针对已经熟悉Java程序设计和J2EE概念人员的。这个系统是使用正规方式设计出来的，使得使用J2EE架构的人员有了一个参考的依据。这个范例一出来，很快就被用来当成比较各家J2EE兼容产品的依据。Oracle的J2EE Application Server（Oracle 9iAS）、BEA WebLogic Server、<a href="http://www-900.ibm.com/cn/shop/online.html"><font color="#002c99">IBM</font></a> WebSphere都有相应的产品。Java标准的精神原本就是各家厂商合作制定技术标准，然后再根据这个标准来评选出最优产品。<br /><br />　　Java Pet Store使用的架构设计就是所谓的MVC 设计模式。MVC是Model-View-Controller的简写，是设计模式的一种，源自Smalltalk。MVC模式则是将对象分成三类，Model对象专门用来包装应用程序的状态，View用来负责屏幕上的展现，Controller则负责定义应用程序的各种动作和反应。Java宠物店系统利用MVC模式设计整个系统架构，将各层的对象清楚地分开。Java宠物店的目的是展示一个具有扩充性的企业运算架构，就是采用三层（3-Tiers）式设计：资料展现层在最外面，中间是执行企业运算逻辑的中间层组件，后端就是单纯存放资料的关系型数据库。Java宠物店当初的用意也只是当成一个J2EE架构设计的模板，并没有特别针对加速执行效能作额外的设计。<br /><br /><b>系统构架与宠物商店简介</b><br /><br />　　系统构架<br /><br />　　Pet Store网站系统采用松耦合的设计架构，可以和多个数据源、EIS进行交互。这个例子共分成4个部分：<br /><br />　　1.Web购物站点；<br /><br />　　2.管理部分,包括销售统计、手工接受/拒绝订单；<br /><br />　　3.订单处理，包括以下4部分内容：<br /><br />　　 ◆ 通过JMS接受/处理订单消息；<br /><br />　　 ◆ 用Java Mail来通知客户；<br /><br />　　 ◆ 通过JMS发订单给供应商；<br /><br />　　◆ 维护订单数据库。<br /><br />　　4.供应商，此部分包括以下内容：<br /><br />　　 ◆ 通过JMS接受订单；<br /><br />　　◆ 派送货物给用户；<br /><br />　　◆ 提供一个基于Web的库存管理；<br /><br />　　◆ 维护库存数据库。<br /><br />　　宠物店网上商店功能<br /><br />　　通过浏览器可以访问此商店。客户通过浏览，可以把货物放入购物车，创建账户/登录账户，创建订单，然后通过信用卡支付。<br /><br />　　宠物商店结构<br /><br />　　宠物店的网站服务是从上而下的。最上层是WAF(Web Application Framework)来控制应用的屏幕跳转，产生视图，然后调用商业组件来完成商业处理（如图1所示）。<br /><br /><img src="http://tech.ccidnet.com/pub/attachment/2003/7/240773.gif" /><br /><br />　　图1 Java Pet Store 结构<br /><br />　　WAF提供了许多Web应用所需的服务，包括请求的过滤和分发、模板视图的产生、一系列可重用的Taglib，以及屏幕流程控制等。应用组件封装了处理的逻辑，它们代表了商业的数据，并且操作这些商业数据，实体EJB代表了商业实体，如客户、地址、账目。会话EJB提供了一些方法，如登陆一个用户、输出一个用户、管理购物车等。其它会话EJB提供了一些共同的方法，如产生唯一标示符。传统的JavaBean组件变成了值对象，用来在EJB组件和应用间传递数据。XML文档类则用来处理订单信息。<br /><br />　　这个宠物店例子的WAF是对J2EE蓝图Web层规范的实现。一个Web层处理一般可以划分成四步（如图2所示）:<br /><br />　　◆ 解释一个请求；<br /><br />　　◆ 执行一个商业逻辑处理；<br /><br />　　◆ 选择下一个视图；<br /><br />　　◆ 产生这个视图。<br /><br /><img src="http://tech.ccidnet.com/pub/attachment/2003/7/240774.gif" /><br /><br />　　图2 WAF的Web层处理<br /><br />　　宠物店模块设计<br /><br />　　宠物店由一些独立模块组成：<br /><br />　　◆ 控制模块 它来分发请求到各个业务处理逻辑、控制屏幕跳转、处理对应的组件及用户；<br /><br />　　◆ 登录和注册 控制模块由WAF实现和扩展；<br /><br />　　◆ 购物车模块 购物车跟踪用户购物过程；<br /><br />　　◆ 登录模块 需要用户登录在某些页面登录；<br /><br />　　◆ 消息模块 从宠物店到等单中心用来异步传输订单；<br /><br />　　◆ 类别模块 根据用户查询需求提供一个类别视图；<br /><br />　　◆ 客户模块 表示客户信息，如地址、信用卡、联系方式等（如图3所示）。<br /><br /><img src="http://tech.ccidnet.com/pub/attachment/2003/7/240775.gif" /><br /><br />　　图3 Java pet Store 模块设计<br /><br />　　在图3中，控制模块控制所有的交互和执行,每个用户会话都有一个购物车对应。<br /><br />　　宠物店的组件<br /><br />　　1. EJB，代表了商业数据和执行商业逻辑处理；<br /><br />　　2. JSP页面， 定义了整个应用的视图框架模板(template.jsp)和模板组成的各个JSP文件，以及各种被引用的图形文件；<br /><br />　　3. XML文件，用来定义屏幕、屏幕跳转控制、绑定URL到某个HTML Action、定制signOn以及J2EE部署的部署XML文件；<br /><br />　　4. Servlet过虑器, 用来校验用户安全的登陆和输出的编码；<br /><br />　　5. 异步信息发送组件,传输使用XML封装的订单到订单处理中心；<br /><br />　　6. 一个安装程序,用来产生例子数据库。<br /><br /><b>分析宠物商店的应用</b><br /><br />下面就按照MVC架构和层次化应用模型来分析这个应用。<br /><br />模型—视图—控制架构<br /><br />1．应用模型划分方法<br /><br />分析一个实际应用可以有三种划分的方法：第一种划分方法为模型—视图—控制（MVC）架构。这种方法把应用分解成数据、显示和控制三个部分。第二种划分方法把应用按照不同的角色划分成不同的层次，分离客户端、Web层、EJBs层和底端的数据层或遗留系统层，即J2EE应用的层次划分方法。第三种划分是传统的功能模块划分。<br /><br />划分的目的是使复杂的问题清晰化、条理化。每一种划分虽然增加了额外的复杂性，但也有它的好处。MVC架构为应用组件提供一个灵活的、可重用的、易测试的、可扩展的和清晰的设计角色。多层设计使实现技术的选择灵活，同时具有可升级和可扩展性。模块化的设计把系统分解成小的直接模块，可以进行单独分析、测试和理解。<br /><br />现在企业级应用与以前相比，要更多地支持使用不同类型接口的多类型用户，例如在线商店需要为Web顾客提供HMTL主页、为<a href="http://www-900.ibm.com/common/missing.html"><font color="#002c99">无线</font></a>顾客提供XML主页、为系统管理员提供JFC/Swing接口、为供应商提供基于XML的Web服务等（见图4）。<br /><br /><img src="http://tech.ccidnet.com/pub/attachment/2003/7/240776.gif" /><br /><br />图4 Java Pet Store 支持的各种类型用户关系图<br /><br />当开发一个支持单一类型客户端的应用时，可以把数据访问逻辑、显示控制逻辑和接口逻辑交织在一起。但对于支持多类型客户端的企业系统来说，这是很麻烦的。因此：<br /><br />◆ 对于每种类型的客户端接口，需开发不同的应用；<br /><br />◆ 每个应用的非界面代码是重复的，在实现、测试和维护方面需重复工作；<br /><br />◆ 复制工作本身是昂贵的，因为界面与非界面代码交织；<br /><br />◆ 重复工作不可避免，而且是有缺陷而缓慢的。<br /><br />2.使用模型—视图—控制架构<br /><br />通过在J2EE应用中使用模型—视图—控制架构，把核心数据和数据访问功能与使用这些功能的显示控制逻辑分开，如图5所示。这种分离允许多视图共享同样的企业数据模型。<br /><br /><img src="http://tech.ccidnet.com/pub/attachment/2003/7/240777.gif" /><br /><br />图5 MVC架构<br /><br />MVC架构起源于Smalltalk，最初用来在传统的图形用户界面模型中映射输入、处理和输出任务。然而，它可以直接用来映射多层企业应用中的相关概念，具体概念介绍如下：<br /><br />模型（Model）代表企业数据和业务规则，用来控制访问和数据更新。模型是接近现实世界的服务软件，因此现实世界的建模技术可以应用模型。<br /><br />视图（View）代表模型的内容。它通过模型访问企业数据并指定这些数据的显示。视图负责模型状态改变后呈现给用户的数据也相应改变。可以通过推（Push）模型实现，即视图在模型中注册获取更新指令，或者拉（Pull）模型，即由视图负责在需要获取最新数据的时候调用模型。<br /><br />控制（Controller）把与视图交互转化成模型执行的动作。在独立运行的GUI客户端，用户交互可能是按钮或菜单，而在Web应用中是GET和POST HTTP请求。模型执行的动作包括激活业务处理进程或改变模型状态。以用户交互和模型动作结果为基础，控制通过选择合适的视图完成相应功能。<br /><br />MVC架构有如下优点：<br /><br />◆ 多视图使用同一模型。模型与视图分离允许多视图使用同一企业模型。因此，企业级应用模型组件容易实现、测试和维护。<br /><br />◆ 容易支持新类型的客户端。支持一个新类型的客户端，只需写一个视图和控制，然后把它连到现存的企业模型中。<br /><br /><b>分析Java Pet Store应用</b><br /><br />视图<br /><br />视图是用户界面和应用程序的接口。在Java Pet Store中，视图在Web层实现。共有三种组件实现视图：JSP页面、JSP自定义标记和JavaBean。视图部分涉及到三方面内容：<br /><br />1.屏幕<br /><br />屏幕是一个页面显示的所有内容。根据需要，在ScreenDefinitions.jsp中定义如下屏幕：<br /><br /><ccid_nobr></ccid_nobr></font></p>
																		<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
																				<tbody>
																						<tr>
																								<td class="code" bgcolor="#e6e6e6">
																										<pre>
																												<font color="#000000">
																														<ccid_code>
Name：MAIN_SCREEN，DEFAULT_SCREEN
    Name：CATEGORY_SCREEN
    Name：SEARCH_SCREEN
    Name：PRODUCT_SCREEN
    Name：PRODUCT_DETAILS_SCREEN
    Name：CART_SCREEN
    Name：CHECKOUT_SCREEN
    Name：PLACEORDER_SCREEN
    Name：COMMIT_ORDER_SCREEN
    Name：SIGNIN_SCREEN
    Name：SIGNUP_SCREEN</ccid_code>
																												</font>
																										</pre>
																								</td>
																						</tr>
																				</tbody>
																		</table>
																		<font color="#000000">
																				<br />
																				<br />2．模板<br /><br />因为要使整个网站的页面具有相同的特征，如每个页面都要有Logo、Banner等相同的元素，所以采用模板定义页面的不同组成部分。本示例中定义的模板元素有footer.jsp、banner.jsp和index.jsp等。ScreenDefinitions.jsp定义好的屏幕包括这些模板元素，通过include指令包含到页面中</font>
																		<p>
																				<span class="myp11">
																						<font id="zoom">
																								<font color="#000000">3．视图选择<br /><br />视图的选择是通过控制来完成的。控制根据实际情况分析用户显示视图的ID，运行模板把整个视图显示出来，如图6所示。<br /><br /><img src="http://tech.ccidnet.com/pub/attachment/2003/7/240778.gif" /><br /><br />图6 视图选择<br /><br />模型<br /><br />模型的状态是视图显示的数据来源，也是控制的具体对象。在J2EE中，描述模型的状态采用三种EJB：无状态会话EJB、有状态会话EJB和实体EJB。<br /><br />1．辅助对象<br /><br />有两种主要的辅助对象：数据库访问对象和值对象。对于实体EJB来说，数据库访问对象封装了数据库访问的方法，如AccountDAO等。对于所有EJB的属性值，则都由一个值对象来封装，如ShoppingCartModel、AccountModel等。<br /><br />2．EJBs<br /><br />本示例用到的EJBs如图7所示。<br /><br /><img src="http://tech.ccidnet.com/pub/attachment/2003/7/240779.gif" /><br /><br />图7 Java Pet Store使用到的EJBs<br /><br />3．模型状态到视图的绑定<br /><br />一个模型对应多个视图，实现视图的绑定方法是：ModelUpdateListener和ModelUpdateNotifier实现了一种注册-监听模式，通过调用监听器的performUpdate方法来使视图得到更新。例如：<br /><br /><ccid_nobr></ccid_nobr></font>
																								<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
																										<tbody>
																												<tr>
																														<td class="code" bgcolor="#e6e6e6">
																																<pre>
																																		<font color="#000000">
																																				<ccid_code>
public class AccountWebImpl extends AccountModel
implements ModelUpdateListener{
 private ModelManager mm；
 private Account acctEjb；
 public AccountWebImpl(ModelManager mm){
  super(null，null，null}；
  this.mm=mm；
  mm.addListener(JNDINames.ACCOUNT_EJBHOME，this)；
 }
 public void performUpdate(){
  if(acctEjb==null)  {
   acctEjb=mm.getAccountEJB()；
  }
  try{
   if(acctEjb !=null)copy(acctEjb.getDetails())；
  }catch(RemoteException re){
   throw new GeneralFailureException(re)；
  }
 }
}</ccid_code>
																																		</font>
																																</pre>
																														</td>
																												</tr>
																										</tbody>
																								</table>
																								<font color="#000000">
																										<br />
																										<br />控制<br /><br />控制负责处理用户请求、调用相应的模型、更新模型的状态、刷新视图以及返回用户合理的页面。示例的所有控制对象如图8所示。<br /><br /><img src="http://tech.ccidnet.com/pub/attachment/2003/7/240780.gif" /><br /><br />图8 控制对象图<br /><br />1．RequestProcessor<br /><br />RequestProcessor接收并处理用户的所有请求，调用RequestToEventTranslator对象把请求转换成预定义的事件，在事件处理完成后，进行视图更新。 代码如下所示：<br /><br /><ccid_nobr></ccid_nobr></font>
																								<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
																										<tbody>
																												<tr>
																														<td class="code" bgcolor="#e6e6e6">
																																<pre>
																																		<font color="#000000">
																																				<ccid_code>
public class RequestProcessor{
     private ShoppingClientControllerWebImpl scc；
     private ModelManager mm；
     private ModelUpdateNotifier mun；
     private RequestToEventTranslator eventTranslator；
     private SecurityAdapter securityAdapter；
     public void init(...)  {
      mm = (ModelManager)session.getAttribute("modelManager")；
      mun = mm；
      SCC = new ShoppingClientControllerWebImpl(session)；
      eventTranslator= new RequestToEventTranslator(this，mm)；
     }
     public void processRequest(HttpServletRequest req){
      checkForWebServerLogin(req)；
      EStoreEvent event = eventTranslator.processRequest(req)；
      if (event != null){
       Collection updatedModelList = scc.handleEvent(event)；
       mun.notifyListeners(updatedModelList)；
      }     
     }
    }</ccid_code>
																																		</font>
																																</pre>
																														</td>
																												</tr>
																										</tbody>
																								</table>
																								<font color="#000000">
																										<br />
																										<br />2．ShoppingClientControllerWebImpl<br /><br />ShoppingClientContronerWebImpl是调用EJB层的ShoppingClientController代理对象，代码如下所示：<br /><br /><ccid_nobr></ccid_nobr></font>
																								<table cellspacing="0" bordercolordark="#ffffff" cellpadding="2" width="550" align="center" bordercolorlight="black" border="1">
																										<tbody>
																												<tr>
																														<td class="code" bgcolor="#e6e6e6">
																																<pre>
																																		<font color="#000000">
																																				<ccid_code>
public class ShoppingClientControllerWebImpl{
     private com....ejb.ShoppingClientController sccEjb；
     private HttpSession session；
     public ShoppingClientControllerWebImpl(HttpSession session){
      this.session = session；   
      ModelManager mm= (ModelManager)session.getAttribute("modelManager")；
      sccEjb = mm.getSCCEJB()；    
     }
     public synchronized AccountModel getAccount(){
      return sccEjb.getAccount().getDetails()；
     }    
     ......   
     public synchronized Collection handleEvent(EStoreEvent ese){
      return sccEjb.handleEvent(ese)；
     }
     public synchronized void remove()  {
      sccEjb.remove()；
     }
    }</ccid_code>
																																		</font>
																																</pre>
																														</td>
																												</tr>
																										</tbody>
																								</table>
																								<font color="#000000">
																										<br />
																										<br />3．ShoppingClientController<br /><br />ShoppingClientController是有状态的会话EJB，它为每个用户建立一个单独的实例，负责购物车和账号的生命周期，并负责处理事件。同时它也控制状态机StateMachine的生命周期。代码参见赛迪网http://linux.ccidnet.com的期刊浏览2003年第6期。<br /><br />4．StateMachine<br /><br />StateMachine实现核心的业务逻辑，它负责改变模型的状态，包括处理每个业务事件的方法。代码参见赛迪网http://linux.ccidnet.com的期刊浏览2003年第6期。<br /><br />　　<b>小结</b><br /><br />　　J2EE网站的开发方法，即是Internet的开发方法。Internet开发的发展可以划分为三个阶段：第一阶段是将业务逻辑和表现逻辑完全集成在一起，采用HTML、JSP和Servlets技术开发；第二阶段是将业务逻辑和表现逻辑分开，采用HTML、JSP、Servlets、JavaBeans Compoments和Custom Tags技术开发；第三个阶段是MVC设计模式（J2EE的开发方法）。今天，MVC设计模式已成为Internet开发发展的主流。无论是通过第一阶段开发的应用，还是通过第二阶段开发的应用，都会面临着开发人员的分工、应用的可维护性和可扩展性及可测量性的问题。为此，我们在设计阶段关心的重点是系统结构的复杂程度、代码之间的耦合度、代码的易维护性、应用框架的可重用性、EJB组件的可重用性和易测试性，以及不同技能开发人员的分工等。用纵观全局的眼光来看，在Internet系统开始设计的时候，就要考虑开发、运行、维护阶段的问题</font>
																						</font>
																				</span>
																		</p>
																		<p>　 </p>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/xixidabao/aggbug/69037.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-09-11 21:17 <a href="http://www.blogjava.net/xixidabao/archive/2006/09/11/69037.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>利用J2EE模式构建网站</title><link>http://www.blogjava.net/xixidabao/archive/2006/06/18/53638.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Sun, 18 Jun 2006 13:26:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/06/18/53638.html</guid><description><![CDATA[
		<table cellspacing="0" cellpadding="0" width="760" align="center" border="0">
				<tbody>
						<tr>
								<td class="title" valign="center" align="middle" height="56">
										<b>
												<font color="#ff0000" size="3">利用J2EE模式构建网站<!-- #EndEditable --></font>
										</b>
								</td>
						</tr>
						<tr>
								<td class="formtitle" align="middle" height="40">
										<!-- #BeginEditable "2" -->作者：李志<!-- #EndEditable --></td>
						</tr>
				</tbody>
		</table>
		<table height="65" cellspacing="0" cellpadding="0" width="760" align="center" border="0">
				<tbody>
						<tr>
								<td class="content" height="65">
										<!-- #BeginEditable "3" -->
										<table width="760" align="center">
												<tbody>
														<tr>
																<td class="content" width="768">
																		<table class="p11" cellspacing="0" cellpadding="0" width="100%" align="center" border="0">
																				<tbody>
																						<tr>
																								<td style="WORD-WRAP: break-word">  
<p class="content"><a name="1">一、前言</a></p><p class="content">    本文以一个教学网站的建设思路为例，探讨利用J2EE技术和WebSphere产品构建网站的模式和方法。</p><p class="content"><a name="2">二、设计网站系统</a></p><p class="content">    我们的样例是一个教学网站系统，它的软件包括WebSphere Application Server应用服务器软件V4.0、WSAD开发工具和DB2数据库（非商业用途），硬件为IBM xSeries服务器。在本文中，主要探讨MVC的开发模型和常用的J2EE模式，关于网站建设的其他细节就略过不提了。</p><p class="content">（一）系统用例图</p><p class="content">    分析网站的系统目标后，我们首先具体化系统功能，形成一张用例图，定义一系列的可重构组件，以指导随后的开发工作。</p><p></p><center><span class="content"><img height="353" alt=" 图1 Use Case picture" src="http://www.uml.org.cn/j2ee/images/002.jpg" width="577" border="0" /><br />图1 Use Case picture </span></center><p class="content">　</p><p class="content">（二）组件化设计</p><p class="content">    在构造网站系统时，我们把每层的系统想象成拥有多个"槽"的装置，开发人员可以向槽中插入组件以扩大其能力，也可以通过继承或其他机制具体化组件系统。这些组件可以是开发人员为该应用系统开发的，也可以是以前开发好的复用组件。在这种分层体系结构中，每个应用系统都表示为一个单独的系统。每个系统都采用组件构造。每个组件系统又可以通过其他下层组件系统构造。重构人员采用一组与特定应用系统领域和业务有关的组件或顶层中的组件系统来构造每个应用系统。</p><p class="content">    结合J2EE，让我们首先了解J2EE体系中的组件构成情况，如图2所示。</p><p></p><center><span class="content"><img height="279" alt="图2  J2EE组件打包策略" src="http://www.uml.org.cn/j2ee/images/004.jpg" width="436" border="0" /><br />图2 J2EE组件打包策略 </span></center><p class="content">　</p><p class="content">    在上图中可以看出，不同的组件归档到不同的文件包中，这样就保证了一个组件的"插拔"不会影响到其它的组件。根据应用系统组件的功能，我们可以把它们分为动态组件和静态组件。静态的组件包含网页文件，主要用于放置教学资料和参考文章。动态组件则包括各种功能模块，如论坛系统、模拟测验系统等。</p><p class="content">    应用系统组件之下是于特定业务有关的组件。在这里，我们可以添加非Java编写的一些程序，用于处理特定内容下的操作，比如模拟测验系统中的出题模块。当然，要考虑到上层组件调用的正面接口问题。对于这层组件，我们能够随时替换，只要其提供的数据符合上级正面的要求。</p><p class="content">    以上两级组件之下是J2EE应用服务器和操作系统，整体如图3所示。</p><p></p><center><span class="content"><img height="270" alt="图3 计算机组成原理网站系统组件架构图" src="http://www.uml.org.cn/j2ee/images/006.jpg" width="315" border="0" /><br />图3 计算机组成原理网站系统组件架构图 </span></center><p class="content">　</p><p class="content">（三）利用J2EE模式开发组件系统</p><p class="content">    下面着重介绍开发过程中使用的J2EE模式，这些模式都是通用类型的。</p><p class="content">    本系统采用MVC开发模型，即Model-View-Controller。Model是指应用程序的数据，以及对这些数据的操作；View是指用户界面；Controller负责用户界面和程序数据之间的同步。这种模型的好处在于分离不同功能的代码，便于以后的维护，还有利于在项目小组内按照小组成员各自的擅长进行分工，有利于三个部分并行开发、加快项目进度。</p><p class="content">    为了使各开发人员协调一致，为其他组件提供一致和标准的正面，增强系统的可维护性和可复用性，我们广泛采用了SUN公司提出的基于MVC的设计模式。</p><p class="content">    图4是用户注册模块的UML图，我们将结合这个模块具体阐述各模式的特点和在本系统中的实际应用。</p><p></p><center><span class="content"><img height="575" alt="图4 表示层模式" src="http://www.uml.org.cn/j2ee/images/007.jpg" width="700" border="0" /><br />图4 表示层模式 </span></center><p class="content">　</p><p class="content">    明确了所采用的体系和模式，下面具体设计类的属性和方法，通过设计完善的接口和继承、重载等方法进行重构。模块的UML的类图表示如下：</p><p></p><center><span class="content"><img height="980" alt="图5 模块的UML的类图" src="http://www.uml.org.cn/j2ee/images/009.jpg" width="760" border="0" /><br />图5 模块的UML的类图 </span></center><p class="content">　</p><p class="content">结合上图，让我们看看这个模块中都运用了哪些模式。</p><p class="content"><b>1. 表示层模式</b></p><p class="content">    系统的表示层集中了MVC模式中的View与Controller。该系统用JSP代表View，用Servlet代表Controller。在Controller这一模块中，又采用了视图助手、分发者与值对象模式，以增强系统的模块化，提高维护性。</p><p class="content">（1）前端控制器</p><p class="content">控制器通常表现为Servlet形式，其UML表示如下：</p><p></p><center><span class="content"><img height="174" alt="图6 前端控制器" src="http://www.uml.org.cn/j2ee/images/012.jpg" width="146" border="0" /><br />图6 前端控制器 </span></center><p class="content">　</p><p class="content">    根据Model-View-Controller的开发思想，使用控制器作为处理请求的最初联系点。该控制器管理着请求的处理，包括调用安全服务，比如验证和授权、委托业务处理、管理合适的视图选择、处理错误，以及管理内容创建逻辑的选择。也可以把前端控制器看成一个触发器，由它来启动流程。</p><p class="content">    下面是功能代码的样本。其中出现的RegisterHelper、Command等类，接下来会有详细介绍。</p><pre class="content">   public void performTask(javax.servlet.http.HttpServletRequest request,
		javax.servlet.http.HttpServletResponse response)
		throws javax.servlet.ServletException, java.io.IOException 
   {
   	   RegisterHelper rh=new RegisterHelper(request,response);//启动注册视图助手
   	   Command command=rh.getCommand();//由视图助手中获得并初始化Command
   	   CustomerBean cb=rh.getCustomerBean();//由视图助手中获得并初始化值对象
   	   request.setAttribute("customerbean",cb);
   	   String dispatcher=rh.getDispatcher();//由视图助手中获得并初始化分发者
	   request.setAttribute("type",rh.getType());//设置上下文属性
   	   try {
   	    command.execute((Helper)rh);//执行业务代码
   	   } catch(javax.ejb.DuplicateKeyException de) {
		request.setAttribute("errorbean",new ErrorBean("对不起，已经有人注册了该用户名!"));//注册重名处理
		dispatch(request,response,dispatcher);//分发并移交控制权
		return;
   	   } catch(Exception e) {
   		request.setAttribute("errorbean",new ErrorBean("对不起，数据库出错!"));//出错处理
		dispatch(request,response,dispatcher);
		return;
   	}
   	   dispatch(request,response,dispatcher);
    
   }
   </pre><p class="content">    优点：通过集中化决策点和控制，控制器有助于减少嵌入在JSP中Java代码（Scriptlet）的数量，保持View功能的纯洁性。它的位置如图5中Controller所示。</p><p class="content">（2）视图助手</p><p class="content">    表示层更改经常发生，而且当业务数据访问逻辑和表示格式化逻辑被混杂时，表示层更改很难开发和维护。这使系统灵活性更差，更缺乏可用性，而且对变化的适应性更弱。</p><p></p><center><span class="content"><img height="172" alt="图7 视图助手" src="http://www.uml.org.cn/j2ee/images/013.jpg" width="348" border="0" /><br />图7 视图助手 </span></center><p class="content">　</p><p class="content">    视图包含格式化代码，把其处理责任委托给其助手类。助手也存储该视图的中间数据，如表单、URL参数等，并且充当业务数据适配器。</p><p class="content">下面是功能代码的样本。</p><pre class="content">public class RegisterHelper implements Helper {

	static String dispatcher = "RegisterDispatcher";
	private CustomerBean customer = null;
	private String type = null;
	
	public RegisterHelper(
		javax.servlet.http.HttpServletRequest request,
		javax.servlet.http.HttpServletResponse response) {
		setType(request);
		setCustomerBean(request);		
	}

	/**
	 * 定义页面类型:HTML or XML
	 */
	public void setType(javax.servlet.http.HttpServletRequest request) {
		type = request.getParameter("type");
	}

	/**
	 * 获取Command
	 */
	public Command getCommand() {
		RegisterCommand rc = new RegisterCommand();
		return rc;
	}

	/**
	 * 向值对象中填充数据
	 */
	public void setCustomerBean(javax.servlet.http.HttpServletRequest request) {
		customer = new CustomerBean();
		customer.setUsername(request.getParameter("username"));
		customer.setPassword(request.getParameter("password"));
		customer.setEmail(request.getParameter("email"));
		customer.setTruename(request.getParameter("truename"));
		customer.setId(request.getParameter("id"));
		customer.setService(this.setService(request));
	}
	/**
	 * 获取值对象
	 */

	public CustomerBean getCustomerBean() {
		return this.customer;
	}

	/**
	 * 获取分发者
	 */
	public String getDispatcher() {
		return this.dispatcher;
	}

	/**
	 * 获取类型
	 */
	public String getType() {
		return type;
	}
}
</pre><p class="content">    优点：在助手中而不是在视图中封装业务逻辑会增强应用程序的模块化，并且更有利于组件重用。助手有大量的责任，包括收集视图和控制需要的数据，以及存储中间模型。它的位置如图5中Helper所示。</p><p class="content">（3）Command模式</p><p class="content">    Command中包含纯业务代码，如注册、登陆、检验等。在样例模块中，它的职责是将注册信息传递给Entity Bean。</p><p></p><center><span class="content"><img height="85" alt="图8 Command" src="http://www.uml.org.cn/j2ee/images/015.jpg" width="154" border="0" /><br />图8 Command </span></center><p class="content">　</p><p class="content">功能代码如下所示：</p><pre class="content">      public void execute(Helper helper) throws Exception 
   {
      RegisterHelper rh = (RegisterHelper) helper;//获取视图助手
		CustomerBean cb = rh.getCustomerBean();//从视图助手中获取值对象
		ServiceLocator sl = ServiceLocator.getInstance();//初始化服务定位器
		CustomersHome ch = (CustomersHome) sl.getHome(ServiceLocator.Services.CUSTOMERS);//从服务定位器中获取Entity Bean本地接口
		try {	
			Customers customers = ch.create(cb);//将注册信息导入数据库
		} catch(javax.ejb.DuplicateKeyException e) {
			throw new javax.ejb.DuplicateKeyException();
		} catch(Exception e) {
			throw e;
		}
	
   }
   </pre><p class="content">（4）分发者模式</p><p class="content">    如果将表示化逻辑和业务逻辑混合在视图中，会使系统可重用性和灵活性变差，而且一半还会使更改操作难以实施。分发者负责视图管理和导航，选择下一个视图，并且提供分发资源控制的机制。分发者可以提供静态的分发，也能提供更高级的动态分发机制。</p><p class="content">    在我们的项目中，由于涉及到PC用户和移动手机用户的访问，我们需要针对不同的用户返回不同的结果页面，因此分发者的存在就非常有必要。分发者表现为Servlet形式，它承接Controller的处理结果，并判断用户的类型，把正确的视图返回给用户。它的位置如图5中Dispatcher所示。</p><p></p><center><span class="content"><img height="214" alt="图9 分发者" src="http://www.uml.org.cn/j2ee/images/017.jpg" width="169" border="0" /><br />图9 分发者 </span></center><p class="content">　</p><p class="content">功能代码如下所示：</p><pre class="content">		public void performTask(
		javax.servlet.http.HttpServletRequest request,
		javax.servlet.http.HttpServletResponse response)
		throws javax.servlet.ServletException, java.io.IOException {
		String type=(String)request.getAttribute("type");//获取页面类型
		isError=(request.getAttribute("errorbean")!=null)?true:false;//判断是否出错
		String file=selectType(type,isError,response);//根据页面类型和是否出错确定显示页面
		getServletConfig().getServletContext().getRequestDispatcher(file).forward(request,response);//重定向到显示页面
	}
	
	public String selectType(String str,boolean isError,javax.servlet.http.HttpServletResponse response) {
		if (str.equals("html")) {//HTML类型的页面
			if (isError) {//成功
				System.out.println("Some error happens!");
				return "register_error.jsp";
			} else {//出错
				return "register_ok.jsp";
			}
		} else {//WML手机页面
			if (isError) {//成功
				System.out.println("Some error happens!");
				return "wml/register_error.jsp";
			} else {//出错				
				response.setContentType("text/vnd.wap.wml;charSet=gb2312");
				return "wml/register_ok.jsp";
			}
		}
	}
	</pre><p class="content">（5）复合视图</p><p class="content">    复杂的Web页面可以展示来自多个数据源的内容，使用多个包含单显示页面的子视图。同时，具有不同技能的多个开发人员可以参与这些Web页面的开发和维护。</p><p class="content">    因此，我们采用有多个原子视图组成的复合视图。模版中每个组件是动态结合在一起的，并且页面的布局是独立于内容进行管理的。在我们的项目中，采用了&lt;jsp:include page="***" flush="true"&gt;嵌入页面，使导航栏和标示独立于各个页面，使用户能够及时地看到任何变动，并使系统更改的代价降低到最小。</p><p class="content"><b>2. 业务层模式</b></p><p class="content">（1）值对象</p><p class="content">    J2EE应用程序把服务器端业务组件实现为Session Bean和Entity Bean。业务组件的一些方法可以向客户端返回数据。通常，客户端需要多次调用业务对象的get/set方法直到获得所有的属性值。由于EJB的调用采用RMI-IIOP方式通过网络进行，这样做大大延缓了业务层的处理速度，降低了效率。</p><p class="content">    为了解决这一问题，我们使用值对象封装业务数据。相应的方法调用是设置和检索值对象。当客户端向企业bean请求业务数据时，该企业bean可以构造值对象，用属性值来填充，并且按照值把它传递给客户端，这也符合EJB端粗粒度调用的需要。</p><p class="content">    值对象是可串行化的JavaBean对象。值对象类也可以提供接收所有必须的属性以创建该值对象的构造器。通常，值对象中的成员被定义为私有的，而其Get/Set方法则是公有的。</p><p></p><center><span class="content"><img height="333" alt="图10值对象" src="http://www.uml.org.cn/j2ee/images/019.jpg" width="156" border="0" /><br />图10值对象 </span></center><p class="content">　</p><p class="content">如图10所示，作为示例的CutomerBean中包含了对应Entity Bean所需的属性及其访问方法。</p><p class="content">（2）服务定位器</p><p class="content">    J2EE客户端与EJB组件进行交互，这些组件提供业务服务和持久性能力。为了与它们交互，客户端必须定位（或称为查找）该服务组件，或创建一个新的组件。比如，EJB客户端必须定位EJB的本地对象，然后客户端使用该本地对象来查找某对象，或者创建或删除一个或多个EJB。</p><p class="content">    这样，对于所有需要访问JNDI管理的服务对象的客户端而言，都需要进行定位工作。在需要查找服务的客户端中，会导致不必要的代码重复现象。同时，创建最初JNDI环境和在EJB本地对象上执行查找都会占用大量的资源。如果多个客户端反复地请求相同的bean本地对象，这种重复现象会严重影响应用程序性能。</p><p class="content">    我们使用服务定位器对象来抽取所有的JNDI应用，并且隐蔽最初环境创建、EJB本地对象查找和EJB对象重创建的复杂性。多个客户端可以重新使用服务定位器对象来降低代码的复杂性，提供单控制点，并且通过提供缓冲机制来提高性能。</p><p class="content">    该模式降低了来自于客户端依赖性的客户端复杂性，并且需要执行查找和创建过程，这些都是非常消耗资源的。为了消除这些问题，该模式提供了把所有依赖性和网络细节抽取到服务定位器的一种机制。它的位置如图11中ServiceLocator所示。</p><p></p><center><span class="content"><img height="229" alt="图11 服务定位器" src="http://www.uml.org.cn/j2ee/images/020.jpg" width="439" border="0" /><br />图11 服务定位器 </span></center><p class="content">　</p><p class="content">功能代码如下所示：</p><pre class="content">public class ServiceLocator 
{
   	private static ServiceLocator me;
	InitialContext context = null;
   /**
    * 初始化上下文
    */
   public ServiceLocator() 
   {
		try {
			context = new InitialContext();
		} catch (NamingException e) {
			e.printStackTrace();
		}    
   }
	public class Services {
		//为EJB设定请求序号
		final public static int CUSTOMERS=0;
		final public static int PARTNERS=1;
		final public static int ADMINISTRATORS=2;
		final public static int PERMITS=3;
		final public static int PAPERBROKER=4;
		final public static int CHECK=5;
	}
	final static Class CUSTOMERS_CLASS=CustomersHome.class;
	final static String CUSTOMERS_NAME="CustomersHome";
	final static Class PARTNERS_CLASS=PartnersHome.class;
	final static String PARTNERS_NAME="PartnersHome";
	final static Class ADMINISTRATORS_CLASS=AdministratorsHome.class;
	final static String ADMINISTRATORS_NAME="AdministratorsHome";
	final static Class PERMITS_CLASS=PermitsHome.class;
	final static String PERMITS_NAME="PermitsHome";
	final static Class PAPERBROKER_CLASS=PaperBrokerHome.class;
	final static String PAPERBROKER_NAME="PaperBrokerHome";
	final static Class CHECK_CLASS=CheckHome.class;
	final static String CHECK_NAME="CheckHome";
	public static ServiceLocator getInstance() {//单线程处理以节省资源
		if (me == null)
			me = new ServiceLocator();
		return me;
	}   
	static private Class getServiceClass(int service) {
		switch(service) {
			case Services.CUSTOMERS:
				return CUSTOMERS_CLASS;
			case Services.PARTNERS:
				return PARTNERS_CLASS;
			case Services.ADMINISTRATORS:
				return ADMINISTRATORS_CLASS;
			case Services.PERMITS:
				return PERMITS_CLASS;
			case Services.PAPERBROKER:
				return PAPERBROKER_CLASS;
			case Services.CHECK:
				return CHECK_CLASS;
		}
		return null;
	}
	static private String getServiceName(int service) {
		switch(service) {
			case Services.CUSTOMERS:
				return CUSTOMERS_NAME;
			case Services.PARTNERS:
				return PARTNERS_NAME;
			case Services.ADMINISTRATORS:
				return ADMINISTRATORS_NAME;
			case Services.PERMITS:
				return PERMITS_NAME;
			case Services.PAPERBROKER:
				return PAPERBROKER_NAME;
			case Services.CHECK:
				return CHECK_NAME;
		}
		return null;
	}
   /**
    * 返回EJB本地接口
    */
   public EJBHome getHome(int s) 
   {
    	EJBHome home = null;
		try {
			Object objref = context.lookup(getServiceName(s));
			home = (EJBHome) PortableRemoteObject.narrow(objref, getServiceClass(s));

		} catch (NamingException e) {
			e.printStackTrace();
		}
		return home;
   }
}
</pre><p class="content">缺点：如果增加新的EJB，需要修改服务定位器的代码。</p><p class="content">    以上这些模式都是可以通用的，在实际应用中运用这些模式，不仅加快了开发进度，而且开发人员各司其职，避免了代码的混乱，取得了比较好的效果。</p><p class="content"><a name="3">三、总结</a></p><p class="content">    本文记述了根据J2EE模式和MVC开发模型，利用IBM公司的WebSphere应用服务器来组织建设网站的心得和体会。大家的项目类型也许会有不同，但开发的模式总会有相通之处。善于根据实际情况选择开发模式，可以提高开发效率和代码质量，但也不要一味死抱着模式不放，量体裁衣才能游刃有余。</p></td>
																						</tr>
																				</tbody>
																		</table>
																</td>
														</tr>
												</tbody>
										</table>
								</td>
						</tr>
				</tbody>
		</table>
<img src ="http://www.blogjava.net/xixidabao/aggbug/53638.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-06-18 21:26 <a href="http://www.blogjava.net/xixidabao/archive/2006/06/18/53638.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>基于J2EE的电子商务网站实例解析</title><link>http://www.blogjava.net/xixidabao/archive/2006/05/26/48358.html</link><dc:creator>JAVA之路</dc:creator><author>JAVA之路</author><pubDate>Fri, 26 May 2006 08:21:00 GMT</pubDate><guid>http://www.blogjava.net/xixidabao/archive/2006/05/26/48358.html</guid><description><![CDATA[　<b>摘要：</b>本文以实例来详细分析运用J2EE技术架构来搭建企业级电子商务网站的全过程，并对关键部件的实现以及相关技术进行具体剖析，同时结合笔者经验，提出很多有价值的思路和方法。<br /><br />　　<b>一． 前言：</b><br /><br />　　近年来，随着互联网业务的迅猛发展，企业间、企业与消费者间实现电子商务已经成为可能，建立企业级的电子商务平台不仅可以拓宽企业的营销渠道，而且对提升企业品牌形象等方面有重要的战略意义。<br /><br />　　本文将以目前国内最大服务交易平台如易网（http://www.routease.com/）为例，来深入剖析一个电子商务交易平台搭建的全过程。<br /><br />　　《准备篇》<br /><br />　　一个项目的实施首先确定项目目标、项目需求与开发环境（为方便理解，将如易网作为项目来描述）。本篇主要讨论这三个方面： <br /><br />　　一.实施目标<br /><br />　　如易网的创办目标：建设为国内最大的服务类交易平台和在线工作平台。由于本篇以技术讲解为主，如要了解更多的背景资料，可以访问：http://www.routease.com/AboutUs.htm 。<br /><br />　　二.实施需求<br /><br />　　目标决定需求，定制清晰明确的项目需求是整个项目成败的关键。可以使用Rose工具来建立项目对象实体图，这里就不再赘述了，以下就几个重要对象做一些描述：（可以对照http://www.routease.com/来浏览下面内容）：<br /><br />　　TotradeEntity:交易实体对象。该对象为核心对象，标识交易的服务对象。比如翻译服务，开锁服务等。 <br /><br />　　ServiceRequirement：服务需求对象。该对象标识用户需求。比如需要电脑维修的信息等。<br /><br />　　SHOP:店铺对象。该对象为中小企业或者个人开的网店，一个店铺对应多个交易实体。<br /><br />　　USER:用户对象。该对象标识从事网站的合法注册用户，它保留用户信息。<br /><br />　　Account:帐户对象。该对象标识用户的帐户信息。<br /><br />　　Message:消息对象。该对象标识用户之间交流的信息。<br /><br />　　Credit：信誉对象。该对象标识用户交易的信誉等级信息，为交易提供有力参考。<br /><br />　　三.开发/运行环境<br /><br />　　基于以上需求分析，本站采用J2EE/Structs应用架构，服务器主机采用WIN2003 SERVER+APACHE2.0.54+TOMCAT 5.5.4的系统环境，开发环境：Eclipse+JDK1.5，数据库DAO采用的著名的ORM工具TopLink9.0.4.5。以下对相应开发技术及其工具做一个简要介绍：<br /><br />　　1． Structs技术<br /><br />　　Web应用的开发经历了一个由P2P（Page to Page）到MVC(model view controller)的发展过程。早期的Web应用对用户请求的处理和响应均是在页面上完成的，如图1-1所示，即所的JSP1.0。这样的Web架构最大的好处就是开发效率较高，然后近几年随着互联网的迅猛发展，网站功能日益增强，而这种P2P的网站架构（因为其业务规则代码与页面代码混为一团，不利于维护）已经不再适应大规模应用的发展要求，取而代之的是基于MVC的Web架构。MVC的核心思想是将应用分为模型、视图和控制器三部分。模型是指应用程序的数据，以及对这些数据的操作；视图是指用户界面；controller负责用户界面和程序数据之间的同步，也就是完成两个方向的动作：a.在根据用户界面(view)的操作完成对程序数据(model)的更新，b.将程序数据（model）的改变及时反应到用户界面（view）上。通过MVC的Web架构,可以弱化各个部分的耦合关系，并将业务逻辑处理与页面以及数据分离开来，这样当其中一个模块的代码发生改变时，并不影响其他模块的正常运行，所以基于MVC的Web架构更适应于大规模软件应用开发的潮流。 <br /><br /><table width="90%" align="center" border="0"><tbody><tr><td><div align="center"><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" src="/imagelist/06/21/62bdhtjmdg27.gif" border="1" /><br />图1<br /><img style="BORDER-LEFT-COLOR: #000000; BORDER-BOTTOM-COLOR: #000000; BORDER-TOP-COLOR: #000000; BORDER-RIGHT-COLOR: #000000" src="/imagelist/06/21/ji519258055o.gif" border="1" /><br />图2</div></td></tr></tbody></table><br />　　目前基于MVC的开发框架主要有Structs、Spring等。本站选用其中的Structs作为开发框架，采用Structs应用框架开发应用程序，将开发人员从繁琐的代码编制中解放出来，取而代之的是配置一些含有对应关系的XML文件，这样当应用环境发生变化时，不需重新编译程序即可运行，并且使得应用更加灵活、高效,而且重用度高。<br /><br />　　从开发角度，Struts主要有如下的功能：<br /><br />　　·包含一个controller servlet，能将用户的请求发送到相应的Action对象。通过Web.xml文件来配置其相关参数。<br /><br />　　·tag库，并且在controller servlet中提供关联支持，帮助开发人员创建交互式表单应用。<br /><br />　　·通过配置Structs-config.xml文件，将Action对象与用户请求以及请求结果页面关联起来。<br /><br />　　如需更多了解Structs的相关信息，请其官方网站：http://jakarta.apache.org/struts　<br /><br />　　2．TopLink技术<br /><br />　　过去，对模型数据的存取访问往往是直接是应用通过ODBC这样的数据库接口访问数据库。但是这样处理并不符合OOP的精神，而且应用开发人员必须熟悉后台数据库的模型构造，这就加大开发的难度。为此，ORM（Object Relational Mapping）技术应运而生.ORM技术实际是一个对象持久化的框架，其核心思想是建立了Java对象与后台数据库之间的映射关系。这样对这些Java对象的访问实际就是对后台数据库的访问，从而屏蔽了数据库访问的细节，开发人员甚至可以在不了解后台数据库的情况下进行开发工作。此外，Toplink在数据缓存优化上也有很好的表现。本项目采用著名的ORM工具Toplink进行开发。<br /><br /><br /><strong>《实施篇》</strong><br /><br />　　本篇主要介绍该平台的具体实现过程。根据软件工程的相关理论，结合笔者多年的开发经验，网站开发一般尊循以下六步骤：<br /><br />　　1． 收集、整理网站需求。<br /><br />　　2． 根据网站需求，构想网页的交互情景（即USE CASE）,并设计出网站的原形（Prototype）。<br /><br />　　3． 设计出实例化对象以及后台数据库结构。<br /><br />　　4． 采用ORM工具，建立实例化对象与后台数据库之间的映射关系。<br /><br />　　5． 根据网站交互需求，定制后台Action，以处理用户动作。<br /><br />　　6． 修改网站原形（Prototype）为动态页面（JSP文件）,将Action处理结果嵌入到动态页面中返回给客户端。<br /><br />　　在这六个步骤中，第一步实际已经在《准备篇》里已经给出了，下面重点讲解后面几个步骤。<br /><br />　　1． 网站原形（Prototype）<br /><br />　　网站原形是对一个网站功能的页面级描述，即看到网站原形就好比看到一个真实的网站一样，只是网站原形并没有嵌入动态代码，而且页面之间也缺乏关联而已。<br /><br />　　网站原形的开发为纯静态页面的开发，制作网站原形的关键在于将网站功能需求转化为人机界面。<br /><br />　　如易网的网站原形制作下载地址：<a href="http://www.routease.com/download/ruyinew924.rar" target="_blank">http://www.routease.com/download/ruyinew924.rar</a><br /><br />　　2． OOP设计与后台数据库设计<br /><br />　　借助强大的ORM开发工具，可以将OOP与数据库的设计同时进行（即可以同时实施上面步骤的3，4步），这也是ORM工具最大特点。本项目采用Oracle公司的Toplink作为ORM开发工具。以下简要介绍Toplink开发过程。<br /><br />　　1） 打开Toplink的Mapping Workbench组件，然后新建一个Mapping 工程。<br /><br />　　2） 配置工程的属性，即在"选项"面板上设置工程路径以及Java对象源代码的路径。<br /><br />　　3） 配置数据库登陆参数，包括应用访问数据库的URL、用户名、密码等。<br /><br />　　完成以上三步，就可以根据应用的需求来开发Java类。在Mapping Workbench里新建一个描述符（实际就是有一个Java类）,根据需求来添加属性，并自动生成Set/Get方法。一旦完成Java类的开发后，选择"自动映射到数据库"的选项，即可实现数据库表的自动创建。（Toplink的最大优势就是在定制好Java类之后可以自动生成数据库的表结构）。<br /><br />　　鉴于国内Toplink方面的资料较少，这里介绍一下Toplink生成的工程文件RouteaseMappingProject,该工程文件在web服务器启动的时候装载，可以理解为客户程序对数据库访问的接口程序，他有三类方法：<br /><br />　　·构造函数<br /><br />　　主要是调用oracle.toplink.sessions.Project的addDescriptor方法，其作用是将数据库和Java对象之间的映射关系加入到Project 中。代码示范如下：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public RouteaseMappingProject() {<br />addDescriptor(buildAccountDescriptor());<br />addDescriptor(buildPhoneDescriptor());<br />…….<br />}</td></tr></tbody></table><br />　　·applyLogin方法<br /><br />　　它处理客户程序登陆数据，并配置一些存取数据库的参数，比如缓冲池等。代码示范为：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public void applyLogin() {<br />　//配置数据库访问参数<br />　DatabaseLogin login = new DatabaseLogin();<br />　login.usePlatform(new oracle.toplink.oraclespecific.Oracle9Platform());<br />　login.setDriverClassName("oracle.jdbc.driver.OracleDriver"); 　login.setConnectionString(ApplicationConfiguration.get(ConfigurationConstants.DB_CON_STR)); 　login.setUserName(ApplicationConfiguration.get(ConfigurationConstants.DB_USER)); 　　login.setPassword(ApplicationConfiguration.get(ConfigurationConstants.DB_ENCRYPTED_PASSWORD));<br />　// 设置数据库参数<br />　login.setUsesNativeSequencing(true);<br />　login.setSequencePreallocationSize(1);<br />　login.setShouldBindAllParameters(false);<br />　login.setShouldCacheAllStatements(false);<br />　login.setUsesByteArrayBinding(true);<br />　login.setUsesStringBinding(false);<br />　if (login.shouldUseByteArrayBinding()) { // Can only be used with binding.<br />　　login.setUsesStreamsForBinding(false);<br />　}<br />　login.setShouldForceFieldNamesToUpperCase(false);<br />　login.setShouldOptimizeDataConversion(true);<br />　login.setShouldTrimStrings(true);<br />　login.setUsesBatchWriting(false);<br />　if (login.shouldUseBatchWriting()) { // Can only be used with batch writing.<br />　　login.setUsesJDBCBatchWriting(true);<br />　}<br />　login.setUsesExternalConnectionPooling(false);<br />　login.setUsesExternalTransactionController(false);<br />　setLogin(login);<br />}</td></tr></tbody></table><br />　　·建立映射关系<br /><br />　　Toplink通过类似于builXXXDescriptor方法来建立Java对象与数据库表字段之间的对应关系，示范代码如下：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>public Descriptor buildAccountDescriptor() {<br />　Descriptor descriptor = new Descriptor();<br />　descriptor.descriptorIsAggregate();<br />　descriptor.setJavaClass(com.routease.db.vo.user.Account.class);<br />　descriptor.setAlias("Account");<br />　// Mappings.<br />　//建立Account 对象的deposit属性与数据库表的DEPOSIT字段的对应关系<br />　DirectToFieldMapping depositMapping = new DirectToFieldMapping();<br />　depositMapping.setAttributeName("deposit");<br />　depositMapping.setFieldName("DEPOSIT");<br />　descriptor.addMapping(depositMapping);<br />　…<br />　return descriptor;<br />}</td></tr></tbody></table><br /><br /><br />　3． 定制后台Action<br /><br />　　根据MVC的精神，View和Model设计好之后应该是将开发重点转移到控制器的开发上。控制器是根据用户行为进行响应的处理模块，比如用户通过首页的搜索条对服务信息进行检索，这时，web服务中的SearchToTradeEntityAction（对应SearchToTradeEntityAction.java文件）会对用户这一动作进行处理。以下对这一Action进行详细分析：<br /><br /><table bordercolor="#cccccc" width="90%" align="center" bgcolor="#e3e3e3" border="1"><tbody><tr><td>package com.routease.action.totradeentity;<br />import java.util.Collection;<br />import javax.servlet.http.HttpServletRequest;<br />import javax.servlet.http.HttpServletResponse;<br />import org.apache.commons.beanutils.PropertyUtils;<br />import org.apache.commons.lang.StringUtils;<br />import org.apache.struts.action.ActionForm;<br />import org.apache.struts.action.ActionForward;<br />import org.apache.struts.action.ActionMapping;<br />import com.routease.action.PagingAction;<br />import com.routease.action.helper.UserHelper;<br />import com.routease.db.dao.DataSource;<br />import com.routease.db.dao.totradeentity.SearchingCriteria;<br />import com.routease.db.dao.totradeentity.ToTradeEntityDAO;<br />import com.routease.db.util.Constants;<br />import com.routease.db.util.Page;<br />public class SearchToTradeEntityAction extends PagingAction {<br />　public SearchToTradeEntityAction() <br />　{<br />　　super();<br />　}<br />　// executeWithDataSource方法为该Action默认执行的方法<br />　public ActionForward executeWithDataSource(ActionMapping actionMapping, ActionForm actionForm, HttpServletRequest request, HttpServletResponse response, DataSource ds) throws Exception {<br />　　//首先接受用户提交的表单数据<br />　　String objective = (String) PropertyUtils.getSimpleProperty(actionForm, "objective");<br />　　String keyWords = (String) PropertyUtils.getSimpleProperty(actionForm, "keyWords");<br />　　String keyWordsRange = (String) PropertyUtils.getSimpleProperty(actionForm, "keyWordsRange");<br />　　if (StringUtils.isEmpty(keyWordsRange)) {<br />　　　keyWordsRange = SearchingCriteria.KEY_WORDS_RANGE_NAME;<br />　　}<br />　　String industryLevel1 = (String) PropertyUtils.getSimpleProperty(actionForm, "industryLevel1");<br />　　String industryLevel2 = (String) PropertyUtils.getSimpleProperty(actionForm, "industryLevel2");<br />　　String startingPrice = (String) PropertyUtils.getSimpleProperty(actionForm, "startingPrice");<br />　　String endingPrice = (String) PropertyUtils.getSimpleProperty(actionForm, "endingPrice");<br />　　String city = (String) PropertyUtils.getSimpleProperty(actionForm, "city");<br />　　String province = (String) PropertyUtils.getSimpleProperty(actionForm, "province");<br />　　String startNoStr = (String) PropertyUtils.getSimpleProperty(actionForm, "startNumber");<br />　　String lengthStr = (String) PropertyUtils.getSimpleProperty(actionForm, "length");<br />　　if (StringUtils.isEmpty(startNoStr)) {<br />　　　startNoStr = "1";<br />　　}<br />　　//根据用户提交的数据，创建查询表达式对象SC<br />　　int startNumber = Integer.parseInt(startNoStr);<br />　　int length = UserHelper.getPagingLength(ds, request);<br />　　ToTradeEntityDAO serviceDAO = new ToTradeEntityDAO(ds);<br />　　SearchingCriteria sc = new SearchingCriteria();<br />　　sc.setCity(city);<br />　　sc.setProvince(province);<br />　　sc.setEndingPrice(endingPrice);<br />　　sc.setIndustryLevel1(industryLevel1);<br />　　sc.setIndustryLevel2(industryLevel2);<br />　　sc.setKeyWords(keyWords);<br />　　sc.setKeyWordsRange(keyWordsRange);<br />　　sc.setObjective(objective);<br />　　sc.setStartingPrice(startingPrice);<br />　　if (Constants.IS_TEST) {<br />　　　System.out.println("start of page:" + startNumber);<br />　　}<br />　　//提交查询对象SC,并获得查询结果集，将其结果集放入Request对象中，便于返回<br />　　Page result = serviceDAO.searchToTradeEntities(sc, startNumber, length);<br />　　Collection industries = serviceDAO.findIndustryDistribution(sc);<br />　　result.setSizePerPage(length);<br />　　request.setAttribute(Constants.TO_TRADE_ENTITY, result);<br />　　request.setAttribute("MY_INDUSTRIES",industries);<br />　　request.setAttribute("MY_PAGE", result);<br />　　//业务逻辑处理完毕之后，返回成功页面<br />　　return actionMapping.findForward("SUCCESS_PAGE");<br />　}<br />}</td></tr></tbody></table><br />　　SearchToTradeEntityAction是一个典型的Action，由前面注解不难看出，一般Action分为三部分：<br /><br />　　a. 接受用户表单数据<br /><br />　　b. 处理用户表单数据<br /><br />　　c. 返回处理结果及页面<br /><br />　　4． 修改页面为JSP文件<br /><br />　　凡是涉及到与用户状态相关的页面均应改造为动态页面（JSP文件），改造是在前面静态文件的基础上进行的，用服务器端返回的数据（存放在Request对象里）替换静态文本，由于这部分相对技术性不强，所以不再详细赘述了。<br /><br />　　通过前面四部分的介绍，基本概述了如易网技术实施的主要过程，在下面的一章里介绍网站技术中的几个重要技巧。<br /><br /><br /><b>《完结篇》</b><br /><br />　　本篇主要介绍网站实施过程中的几个重要技巧和思路，最后还将介绍网站维护方面的内容。<br /><br />　　一. 加快网站速度<br /><br />　　尽量以静态html文件为主，由于静态文件不需要WEB服务器解析而直接返回给客户端，所以速度更快。<br /><br />　　对网站实时性不强的动态文件可以采用后台定期刷新的机制来转化为静态文件或者js文件，如易网首页中的"推荐服务"栏目实际就是采用这种机制,但是对实时性要求较强的交易环节是不适合用这种方式的。<br /><br />　　另外一种加快网站速度的方法就是将频繁访问数据库的信息放在内存中，在web服务器启动的时候加载进来，这种以为空间换时间的思路也值得借鉴。<br /><br />　　二． 服务器监测管理流程<br /><br />　　一般企业级服务器都是采用独立服务器，需要专人维护，但是这样成本较高，有必要开发一套后台监控程序来对系统资源，数据/程序备份做监测，用技术手段来降低成本。<br /><br />　　如易网后台监控程序实现思路是：分两个线程，一个进程监测服务器的内存、磁盘资源以及数据库、Web服务等相关应用的状态，一旦发现有异常，将以Email或短信的形式通知系统管理员；另外一个进程主要对数据进行周期性的备份，并将备份通过ftp上传至指定备用服务器。<br /><br />　　本系统管理程序在网站运行期间起到重要的安全保障作用，而且也基本不需要人工干预，减少了人力成本，值得中小企业借鉴。<br /><br />　　三． 自助营销平台<br /><br />　　对企业电子商务平台，营销尤为重要。通过不同路径收集营销数据库，并定期给用户发送企业产品信息，这一切过程采用程序的方式实现，方便，省事。<br /><br />　　最后，简要介绍一下网站维护的事宜。网站一旦运营起来，必须保证其7*24小时的全天候正常运行。所以，网站后期的维护极为重要。根据笔者经验，主要需要做好以下几个方面：<br /><br />　　1． 定期做好数据备份和程序备份。<br /><br />　　2． 做好网站安全防护工作，对重要文件和目录设置访问权限，架设防火墙，关闭不用的端口。定期更改服务器的密码，防止黑客入侵。 <br /><br />　　3． 任何程序级的修改必须经过测试环境的验证之后才能发布到生产环境，要有套严格的发布流程。<br /><br />　　做好以上三个方面，网站的正常运行基本可以保证。<br /><br />　　<b>结束语：</b><br /><br />　　本文重点介绍了网站实施过程中的技术框架和实现方法，并结合实例分析了其中运用到的相关技术。实践证明，这套思路建立起来的网站架构稳定高效，具有很高的应用价值。<br /><br /><img src ="http://www.blogjava.net/xixidabao/aggbug/48358.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/xixidabao/" target="_blank">JAVA之路</a> 2006-05-26 16:21 <a href="http://www.blogjava.net/xixidabao/archive/2006/05/26/48358.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>