﻿<?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-难能之理宜停，难处之人宜厚，难处之事宜缓，难成之功宜智。-随笔分类-JEE 综合</title><link>http://www.blogjava.net/Weibo-Forum/category/8719.html</link><description>“道”就是指要自己才能走遍天下，“德”指两个人要直心相对.</description><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 06:37:04 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 06:37:04 GMT</pubDate><ttl>60</ttl><item><title>Java Annotation入门</title><link>http://www.blogjava.net/Weibo-Forum/archive/2006/04/13/40833.html</link><dc:creator>微薄社区</dc:creator><author>微薄社区</author><pubDate>Thu, 13 Apr 2006 03:16:00 GMT</pubDate><guid>http://www.blogjava.net/Weibo-Forum/archive/2006/04/13/40833.html</guid><wfw:comment>http://www.blogjava.net/Weibo-Forum/comments/40833.html</wfw:comment><comments>http://www.blogjava.net/Weibo-Forum/archive/2006/04/13/40833.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Weibo-Forum/comments/commentRss/40833.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Weibo-Forum/services/trackbacks/40833.html</trackback:ping><description><![CDATA[
		<center>
				<span style="FONT-SIZE: 20px">
						<b>Java Annotation入门</b>
				</span>
		</center>
		<br />
		<center>作者：<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new">cleverpig</a></center>
		<br />
		<br />
		<br />
		<br />
		<br />
		<span style="COLOR: red">版权声明：本文可以自由转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明</span>
		<br />作者:cleverpig(作者的Blog:<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new">http://blog.matrix.org.cn/page/cleverpig</a>)<br />原文:[http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html]http://www.matrix.org.cn/resource/article/44/44048_Java+Annotation.html[/url]<br />关键字:Java,annotation,标注<br /><br /><br /><span style="COLOR: blue">摘要：</span><br />本文针对java初学者或者annotation初次使用者全面地说明了annotation的使用方法、定义方式、分类。初学者可以通过以上的说明制作简单的annotation程序，但是对于一些高级的annotation应用（例如使用自定义annotation生成javabean映射xml文件）还需要进一步的研究和探讨。涉及到深入annotation的内容，作者将在后文《Java Annotation高级应用》中谈到。<br /><br />同时，annotation运行存在两种方式：运行时、编译时。上文中讨论的都是在运行时的annotation应用，但在编译时的annotation应用还没有涉及，<br /><br /><span style="COLOR: blue">一、为什么使用Annotation：</span><br /><br />在JAVA应用中，我们常遇到一些需要使用模版代码。例如，为了编写一个JAX-RPC web service，我们必须提供一对接口和实现作为模版代码。如果使用annotation对远程访问的方法代码进行修饰的话，这个模版就能够使用工具自动生成。<br />另外，一些API需要使用与程序代码同时维护的附属文件。例如，JavaBeans需要一个BeanInfo Class与一个Bean同时使用/维护，而EJB则同样需要一个部署描述符。此时在程序中使用annotation来维护这些附属文件的信息将十分便利而且减少了错误。<br /><br /><span style="COLOR: blue">二、Annotation工作方式：</span><br /><br />在5.0版之前的Java平台已经具有了一些ad hoc annotation机制。比如，使用transient修饰符来标识一个成员变量在序列化子系统中应被忽略。而@deprecated这个javadoc tag也是一个ad hoc annotation用来说明一个方法已过时。从Java5.0版发布以来，5.0平台提供了一个正式的annotation功能：允许开发者定义、使用自己的annoatation类型。此功能由一个定义annotation类型的语法和一个描述annotation声明的语法，读取annotaion的API，一个使用annotation修饰的class文件，一个annotation处理工具（apt）组成。<br />annotation并不直接影响代码语义，但是它能够工作的方式被看作类似程序的工具或者类库，它会反过来对正在运行的程序语义有所影响。annotation可以从源文件、class文件或者以在运行时反射的多种方式被读取。<br />当然annotation在某种程度上使javadoc tag更加完整。一般情况下，如果这个标记对java文档产生影响或者用于生成java文档的话，它应该作为一个javadoc tag；否则将作为一个annotation。<br /><br /><span style="COLOR: blue">三、Annotation使用方法：</span><br /><br /><b>1。类型声明方式：</b><br />通常，应用程序并不是必须定义annotation类型，但是定义annotation类型并非难事。Annotation类型声明于一般的接口声明极为类似，区别只在于它在interface关键字前面使用“@”符号。<br />annotation类型的每个方法声明定义了一个annotation类型成员，但方法声明不必有参数或者异常声明；方法返回值的类型被限制在以下的范围：primitives、String、Class、enums、annotation和前面类型的数组；方法可以有默认值。<br /><br />下面是一个简单的annotation类型声明：<br /><b>清单1:</b><br /><pre class="overflow"><br />    /**<br />     * Describes the Request-For-Enhancement(RFE) that led<br />     * to the presence of the annotated API element.<br />     */<br />    public @interface RequestForEnhancement {<br />        int    id();<br />        String synopsis();<br />        String engineer() default "[unassigned]"; <br />        String date();    default "[unimplemented]"; <br />    }<br /></pre><br />代码中只定义了一个annotation类型RequestForEnhancement。<br /><br /><b>2。修饰方法的annotation声明方式：</b><br />annotation是一种修饰符，能够如其它修饰符（如public、static、final）一般使用。习惯用法是annotaions用在其它的修饰符前面。annotations由“@+annotation类型+带有括号的成员-值列表”组成。这些成员的值必须是编译时常量（即在运行时不变）。<br /><br />A：下面是一个使用了RequestForEnhancement annotation的方法声明：<br /><b>清单2:</b><br /><pre class="overflow"><br />    @RequestForEnhancement(<br />        id       = 2868724,<br />        synopsis = "Enable time-travel",<br />        engineer = "Mr. Peabody",<br />        date     = "4/1/3007"<br />    )<br />    public static void travelThroughTime(Date destination) { ... }<br /></pre><br /><br />B：当声明一个没有成员的annotation类型声明时，可使用以下方式：<br /><b>清单3:</b><br /><pre class="overflow"><br />    /**<br />     * Indicates that the specification of the annotated API element<br />     * is preliminary and subject to change.<br />     */<br />    public @interface Preliminary { }<br /></pre><br /><br />作为上面没有成员的annotation类型声明的简写方式：<br /><b>清单4:</b><br /><pre class="overflow"><br />    @Preliminary public class TimeTravel { ... }<br /></pre><br /><br />C：如果在annotations中只有唯一一个成员，则该成员应命名为value：<br /><b>清单5:</b><br /><pre class="overflow"><br />    /**<br />     * Associates a copyright notice with the annotated API element.<br />     */<br />    public @interface Copyright {<br />        String value();<br />    }<br /></pre><br /><br />更为方便的是对于具有唯一成员且成员名为value的annotation（如上文），在其使用时可以忽略掉成员名和赋值号（=）：<br /><b>清单6:</b><br /><pre class="overflow"><br />    @Copyright("2002 Yoyodyne Propulsion Systems")<br />    public class OscillationOverthruster { ... }<br /></pre><br /><br /><b>3。一个使用实例：</b><br />结合上面所讲的，我们在这里建立一个简单的基于annotation测试框架。首先我们需要一个annotation类型来表示某个方法是一个应该被测试工具运行的测试方法。<br /><b>清单7:</b><br /><pre class="overflow"><br />    import java.lang.annotation.*;<br /><br />    /**<br />     * Indicates that the annotated method is a test method.<br />     * This annotation should be used only on parameterless static methods.<br />     */<br />    @Retention(RetentionPolicy.RUNTIME)<br />    @Target(ElementType.METHOD)<br />    public @interface Test { }<br /></pre><br /><br />值得注意的是annotaion类型声明是可以标注自己的，这样的annotation被称为“meta-annotations”。<br /><br />在上面的代码中，@Retention(RetentionPolicy.RUNTIME)这个meta-annotation表示了此类型的annotation将被虚拟机保留使其能够在运行时通过反射被读取。而@Target(ElementType.METHOD)表示此类型的annotation只能用于修饰方法声明。<br /><br />下面是一个简单的程序，其中部分方法被上面的annotation所标注：<br /><b>清单8:</b><br /><pre class="overflow"><br />    public class Foo {<br />        @Test public static void m1() { }<br />        public static void m2() { }<br />        @Test public static void m3() {<br />            throw new RuntimeException("Boom");<br />        }<br />        public static void m4() { }<br />        @Test public static void m5() { }<br />        public static void m6() { }<br />        @Test public static void m7() {<br />            throw new RuntimeException("Crash");<br />        }<br />        public static void m8() { }<br />    }<br /><br />Here is the testing tool:<br /><br />    import java.lang.reflect.*;<br /><br />    public class RunTests {<br />       public static void main(String[] args) throws Exception {<br />          int passed = 0, failed = 0;<br />          for (Method m : Class.forName(args[0]).getMethods()) {<br />             if (m.isAnnotationPresent(Test.class)) {<br />                try {<br />                   m.invoke(null);<br />                   passed++;<br />                } catch (Throwable ex) {<br />                   System.out.printf("Test %s failed: %s %n", m, ex.getCause());<br />                   failed++;<br />                }<br />             }<br />          }<br />          System.out.printf("Passed: %d, Failed %d%n", passed, failed);<br />       }<br />    }<br /></pre><br /><br />这个程序从命令行参数中取出类名，并且遍历此类的所有方法，尝试调用其中被上面的测试annotation类型标注过的方法。在此过程中为了找出哪些方法被annotation类型标注过，需要使用反射的方式执行此查询。如果在调用方法时抛出异常，此方法被认为已经失败，并打印一个失败报告。最后，打印运行通过/失败的方法数量。<br />下面文字表示了如何运行这个基于annotation的测试工具：<br /><br /><b>清单9:</b><br /><pre class="overflow"><br />    $ java RunTests Foo<br />    Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom <br />    Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash <br />    Passed: 2, Failed 2<br /></pre><br /><br /><span style="COLOR: blue">四、Annotation分类：</span><br /><br />根据annotation的使用方法和用途主要分为以下几类：<br /><br /><b>1。内建Annotation——Java5.0版在java语法中经常用到的内建Annotation：</b><br />@Deprecated用于修饰已经过时的方法；<br />@Override用于修饰此方法覆盖了父类的方法（而非重载）；<br />@SuppressWarnings用于通知java编译器禁止特定的编译警告。<br /><br />下面代码展示了内建Annotation类型的用法：<br /><b>清单10:</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation;<br /><br />/**<br /> * 演示如何使用java5内建的annotation<br /> * 参考资料：<br /> * http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html<br /> * http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html<br /> * http://mindprod.com/jgloss/annotations.html<br /> * @author cleverpig<br /> *<br /> */<br />import java.util.List;<br /><br />public class UsingBuiltInAnnotation {<br />        //食物类<br />        class Food{}<br />        //干草类<br />        class Hay extends Food{}<br />        //动物类<br />        class Animal{<br />                Food getFood(){<br />                        return null;<br />                }<br />                //使用Annotation声明Deprecated方法<br />                @Deprecated<br />                void deprecatedMethod(){<br />                }<br />        }<br />        //马类-继承动物类<br />        class Horse extends Animal{<br />                //使用Annotation声明覆盖方法<br />                @Override<br />                Hay getFood(){<br />                        return new Hay();<br />                }<br />                //使用Annotation声明禁止警告<br />                @SuppressWarnings({"deprecation","unchecked"})<br />                void callDeprecatedMethod(List horseGroup){<br />                        Animal an=new Animal();<br />                        an.deprecatedMethod();<br />                        horseGroup.add(an);<br />                }<br />        }<br />}<br /></pre><br /><br /><b>2。开发者自定义Annotation：由开发者自定义Annotation类型。</b><br />下面是一个使用annotation进行方法测试的sample：<br /><br />AnnotationDefineForTestFunction类型定义如下：<br /><b>清单11:</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation;<br /><br />import java.lang.annotation.*;<br />/**<br /> * 定义annotation<br /> * @author cleverpig<br /> *<br /> */<br />//加载在VM中，在运行时进行映射<br />@Retention(RetentionPolicy.RUNTIME)<br />//限定此annotation只能标示方法<br />@Target(ElementType.METHOD)<br />public @interface AnnotationDefineForTestFunction{}<br /></pre><br /><br />测试annotation的代码如下：<br /><br /><b>清单12:</b><br /><pre class="overflow"><br />package com.bjinfotech.practice.annotation;<br /><br />import java.lang.reflect.*;<br /><br />/**<br /> * 一个实例程序应用前面定义的Annotation：AnnotationDefineForTestFunction<br /> * @author cleverpig<br /> *<br /> */<br />public class UsingAnnotation {<br />        @AnnotationDefineForTestFunction public static void method01(){}<br />        <br />        public static void method02(){}<br />        <br />        @AnnotationDefineForTestFunction public static void method03(){<br />                throw new RuntimeException("method03");<br />        }<br />        <br />        public static void method04(){<br />                throw new RuntimeException("method04");<br />        }<br />        <br />        public static void main(String[] argv) throws Exception{<br />                int passed = 0, failed = 0;<br />                //被检测的类名<br />                String className="com.bjinfotech.practice.annotation.UsingAnnotation";<br />                //逐个检查此类的方法，当其方法使用annotation声明时调用此方法<br />            for (Method m : Class.forName(className).getMethods()) {<br />               if (m.isAnnotationPresent(AnnotationDefineForTestFunction.class)) {<br />                  try {<br />                     m.invoke(null);<br />                     passed++;<br />                  } catch (Throwable ex) {<br />                     System.out.printf("测试 %s 失败: %s %n", m, ex.getCause());<br />                     failed++;<br />                  }<br />               }<br />            }<br />            System.out.printf("测试结果： 通过: %d, 失败： %d%n", passed, failed);<br />        }<br />}<br /></pre><br /><br /><b>3。使用第三方开发的Annotation类型</b><br />这也是开发人员所常常用到的一种方式。比如我们在使用Hibernate3.0时就可以利用Annotation生成数据表映射配置文件，而不必使用Xdoclet。<br /><br /><span style="COLOR: blue">五、总结：</span><br /><br />1。前面的文字说明了annotation的使用方法、定义方式、分类。初学者可以通过以上的说明制作简单的annotation程序，但是对于一些高级的annotation应用（例如使用自定义annotation生成javabean映射xml文件）还需要进一步的研究和探讨。<br /><br />2。同时，annotation运行存在两种方式：运行时、编译时。上文中讨论的都是在运行时的annotation应用，但在编译时的annotation应用还没有涉及，因为编译时的annotation要使用annotation processing tool。<br /><br />涉及以上2方面的深入内容，作者将在后文《Java Annotation高级应用》中谈到。<br /><br /><span style="COLOR: blue">六、参考资源：</span><br />·Matrix-Java开发者社区:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn</a><br />·<a href="http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html" target="_new">http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html</a><br />·<a href="http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html" target="_new">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</a><br />·<a href="http://www.javaworld.com/javaworld/jw-03-2005/jw-0321-toolbox.html" target="_new">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</a><br />·<a href="http://java.sun.com/j2se/1.5.0/docs/guide/language/annotations.html" target="_new">http://java.sun.com/j2se/1.5.0/docs/guide/apt/GettingStarted.html</a><br />·作者的Blog:<a href="http://blog.matrix.org.cn/page/cleverpig" target="_new">http://blog.matrix.org.cn/page/cleverpig</a><br /><img src ="http://www.blogjava.net/Weibo-Forum/aggbug/40833.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Weibo-Forum/" target="_blank">微薄社区</a> 2006-04-13 11:16 <a href="http://www.blogjava.net/Weibo-Forum/archive/2006/04/13/40833.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Spring与EJB3.0的比较</title><link>http://www.blogjava.net/Weibo-Forum/archive/2006/03/03/33395.html</link><dc:creator>微薄社区</dc:creator><author>微薄社区</author><pubDate>Fri, 03 Mar 2006 02:53:00 GMT</pubDate><guid>http://www.blogjava.net/Weibo-Forum/archive/2006/03/03/33395.html</guid><wfw:comment>http://www.blogjava.net/Weibo-Forum/comments/33395.html</wfw:comment><comments>http://www.blogjava.net/Weibo-Forum/archive/2006/03/03/33395.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Weibo-Forum/comments/commentRss/33395.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Weibo-Forum/services/trackbacks/33395.html</trackback:ping><description><![CDATA[<P>摘要:<BR>Spring框架虽然很流行但并不是一个标准的开源框架。EJB3.0是由Java Community Process (JCP)制订的标准框架.这两个框架结构都有一个共同核心设计理念：将中间件服务传递给耦合松散的POJOS (Plain Old Java Objects, 简单洁净Java对象)。 本文将对Srping和EJB3.0框架背后的关键不同处进行考察，并讨论其优缺点。本文的观点也适用于其它更少为人知的框架，因为他们都是对“耦合松散的POJO”的设计 <BR>POJO应用框架：Spring与EJB3.0的比较</P>
<P><BR>作者：Michael Juntao Yuan</P>
<P><BR>06/29/2005</P>
<P><BR>翻译：loryliu</P>
<P><BR>版权声明：可以任意转载，转载时请务必以超链接形式标明文章原始出处和作者信息及本声明<BR>英文原文地址:<BR><A href="http://www.onjava.com/pub/a/onjava/2005/06/29/spring-ejb3.html">http://www.onjava.com/pub/a/onjava/2005/06/29/spring-ejb3.html</A><BR>中文地址:<BR><A href="http://www.matrix.org.cn/resource/article/43/43718_Spring_EJB.html">http://www.matrix.org.cn/resource/article/43/43718_Spring_EJB.html</A><BR>关键词： Spring EJB</P>
<P>&nbsp;</P>
<P>艾伯特.爱因斯坦曾经说过：“一切都应该尽可能地简单，但是不能更简单。”确实如此，简化一门理论的基本假设，使我们可以专注于真正关键的地方，这正是一直以来对科学真理的追求。企业软件开发同样如此。</P>
<P>提供一个将复杂的事物（例如，事务、安全或持久性）对开发者进行隐藏的应用框架是简化企业软件开发的关键。一个设计良好的框架可以提高代码重用率、开发者的生产力及软件的质量。然而，现有J2EE1.4的EJB2.1框架被普遍认为设计差，且过于复杂。不满于EJB2.1的框架结构，Java开发者尝试了各种各样的中间件服务传递方法。最引人注目的是，以下两个框架引起了开发者极大兴趣并得到了大量正面的反馈。他们以未来企业Java应用所选框架的姿态展现。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Spring框架虽然很流行但并不是一个标准的开源框架。它主要由Interface21 Inc开发和控制。Spring框架结构是基于依赖注入(Dependency Injection (DI))的设计模式。它可以独立或在现有的应用服务器上运行，而且大量地使用了xml配置文件</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EJB3.0是由Java Community Process (JCP)制订的标准框架，为所有主要的J2EE厂商支持。JBoss已经提供了试用版EJB3.0标准的开源或商业性质实现。EJB3.0充分利用了Java的注释</P>
<P>这两个框架结构都有一个共同核心设计理念：将中间件服务传递给耦合松散的POJOS (Plain Old Java Objects, 简单洁净Java对象)。 这样的框架利用截取执行上下文或在运行时将服务对象注入POJO来把应用服务“缠绕”到POJO。POJO本身并不关心这种“缠绕”，对这种框架结构也没有什么依赖。因此，开发者可专注于业务逻辑和脱离框架的POJO单元测试。除此之外， 由于POJO并不须要继承框架的类或实现其接口，开发者能够极其灵活地搭建继承结构和建造应用。</P>
<P>然而，在拥有同一理念的同时，两个框架结构使用不同的方式来传递POJO服务。许多书籍或文章都将Spring 或EJB3.0和EJB2.1做了比较，但是对Spring 和EJB3.0的比较并没有仔细研究过。在本文中，我将对Srping和EJB3.0框架背后的关键不同处进行考察，并讨论其优缺点。本文的观点也适用于其它更少为人知的框架，因为他们都是对“耦合松散的POJO”的设计。希望这篇文章可以帮助你选择适合你需求的最好框架。</P>
<P>厂商无关性<BR>开发者选择Java平台其中最引人注目的理由之一：厂商无关性。EJB3.0正是一套设计为厂商无关的开放性标准。EJB3.0标准为所有企业Java社团里开源或商业性质厂商所开发和支持。它将开发者与应用服务器实现完全隔离。例如，JBoss的 EJB3.0实现基于Hibernate，Oracle的基于TopLink，但是开发者并不须要学习Hibernate- 或TopLink的具体API来使应用可在Jboss或Oracle上运行。厂商无关性使EJB3.0与现今其它POJO中间件框架区别开来。</P>
<P>但是，正如许多EJB3.0评论家迅速所指出的，在本文撰写时EJB3.0标准还没有到达一个最终版本。大概还有一到两年的时间EJB3.0才能广泛地为所有主要J2EE厂商所支持。即使你的应用服务器本身不支持EJB3.0，你仍然可以通过下载安装”内嵌的”EJB3.0产品来运行EJB3.0的应用。例如，JBoss的内嵌EjB3.0是开源产品且可以在任何J2SE5.0兼容的环境运行(例如, 在任何Java服务器上)，此产品正处于软件测试阶段。其它厂商不久也将发布自己的内嵌EJB3.0产品，特别是针对标准中关于数据持久性的部分。</P>
<P>另一方面，Spring一直以来都是非标准的技术，在未来可预知的一段时间内这种情况将持续下去。虽然你可以在任何应用服务器上使用Spring框架，Spring应用会被锁入在Spring本身和你选择整合进Spring的具体服务中。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Spring框架是一个开源项目，但同时它有一个XML格式的配置文件和编程接口。当然任何一个非标准的产品都会有这种“锁入”(lock-in)的情况，并不是Spring特有的。但Spring应用的长期生存能力仍然还得托Spring这个项目的福（或者是Interface21公司，它雇佣了大部分Spring核心开发人员）。除此之外，假如你用到任何一个具体的Spring服务，例如，Spring事务管理器或则Spring MVC，你也会被锁入到这些API里。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Spring的应用对终端用户是不可知的。例如，对数据持久服务，Spring框架兼容不同的DAO和JDBC的模版帮助类，如Hibernate, iBatis, 和 JDO。所以假如你需要为spring应用切换在数据持久化服务（例如从JBDC到Hibernate），你需要修改你的代码以适合新的模版帮助类。</P>
<P>服务整合<BR>从一个很高的角度上看，Spring框架处于应用服务器和服务库的上方。服务整合的代码（如，数据访问模板和帮助类）属于框架，并暴露于应用开发者。相反，EJB3.0框架与应用服务器高度整合，服务整合代码也包装在一个标准接口后面。</P>
<P>因此，实现EJB3.0的厂商可以大大地优化整体性能和提升开发者的体验。例如，在JBoss EJB3.0的实现中，当你在用EntityManager持久化一个Entity Bean时，后台的Hibernate会话事务已经自动地帮定到调用方法的JTA 的事务上，在JTA 事务提交的同时Hibernate会话事务也提交了。你甚至可以使用一个简单的 @PersistenceContext 注释（稍候例子演示）将EntityManager和它后台的Hibernate事务绑定到一个stateful session bean的应用事务中。在一个会话中应用事务横跨多个线程，这在事务性网页应用很有用，例如，多页面的购物车。<BR>由于高度整合的EJB3.0的框架，使简单、集成的编程接口成为可能。Oracle EJB3.0框架和其后台的Toplink持久化服务也同样程度地整合。</P>
<P>另一个EJB3.0整合服务的绝好例子就是集群支持。假如你在一个服务器集群上部署了一个EJB3.0的应用，所有容错(fail-over)、负载均衡、分布式缓冲和状态复制都已经自动为应用所获得可用。后台的集群支持被隐藏在EJB3.0的框架后面，对EJB3.0开发者来说这些都是完全透明不可见的。</P>
<P>在Spring里，很难优化框架和服务之间的通讯。例如，为了使用Spring里的声明事务服务来管理Hibernate事务，你必须显示地在XML文件中配置Spring TransactionManager和Hibernate SessionFactory对象。Spring必须电显示地管理横跨多个HTTP请求的事务。除此之外，没有别的方法均衡Spring应用里的集群。</P>
<P>服务组合的弹性<BR>由于Spring的服务整合代码作为编程接口的一部份暴露在外，应用开发者有按自己需求装配服务的弹性。这个特点使你能够组合自己的轻量级应用服务器。Spring的一个普遍用法就是将Tomcat和Hibernate组合在一起支持数据库驱动的web应用。在这种情况，Spring本身提供事务服务，Hibernat提供持久化服务——这种设置创建了一个袖珍型的应用服务器。</P>
<P>EJB3.0应用服务器典型地不提供这种根据需求任你挑捡服务的弹性空间。大多数时间，你得到的只是一系列包装好的特性，其中一些你可能根本就不需要。但是如果应用服务器像JBoss一样提供一个模块性的内部设计，那么你可以只取其中一部分，而把不必要的部分剥去。在任何情况，去自定义一个功能强大的应用服务器是没有什么价值的。</P>
<P>当然，假如应用已经超过单个点，那么你应该加入常用服务器上的服务，例如，资源池（resource pooling），消息队列（message queuing）和集群（clustering）。就总体的资源消耗而言，Spring解决方法和其他EJB3.0解决方法一样是重量级的。</P>
<P>在Spring框架里，具有弹性的服务装配使得将虚拟对象而不是真正的业务对象绑定到应用中做脱离容器的单元测试更简单。在EJB3.0应用中，大多数组件都是简单POJO，他们可以很容易地在容器外被测试。但是对于与容器服务相关的对象（例如持久化实实体管理器EntityManager）建议用容器内测试。因为这样会比虚拟对象测试方法更简单，强壮及准确。</P>
<P>XML Vs.注解<BR>从应用开发者的观点上来看，Spring的编程开发接口主要基于XML配置文件而EJB3.0广泛地应用Java注解。XML可以表达复杂的关系，但是它也冗长且不够健壮；注解简单明了，但是很难在注解里表达复杂或继承性的关系。</P>
<P>Spring选择XML或EJB3.0选择注解都是有他们两者框架后的体系结构决定的。因为注解只能容纳很少的配置信息，只有整合前的框架（重头戏都在框架里）才可以把广泛地使用注解作为配置选择。正如我们所讨论过的，EJB3.0刚好符合这个要求，而Spring作为一个普通的DI框架并不符合。 </P>
<P>当然，EJB3.0和Spring都相互取长补短，在某种程度上他们都支持XML和注解。例如，在EJB3.0中，XML配置文件作为一个可选的重载机制来改变注解的默认行为。注解也可以配置一些Spring服务。</P>
<P>通过例子是学习XML和注解方式之间差异的最好方法。在下面几个环节里，让我们来看看Spring和EJB3.0是怎样提供关键服务给应用的。</P>
<P>声明性服务<BR>Spring和EJB3.0都将运行时服务（例如，事务、安全、日志和配置服务）绑定到应用。因为这些服务于应用的业务逻辑是没有直接联系，他们只是由应用本身管理。换句话说，这些服务在运行时由容器透明地应用到应用中。开发者或是管理者配置容器，准确地告诉它什么时候怎样应用这些服务。</P>
<P>EJB3.0运用Java注解来配置声明性服务，而Sring使用XML配置文件。在大多数情况下，EJB3.0注解方式对于这种服务更简单明了。这里有一个在EJB3.0中将事务服务运用到POJO的例子。</P>
<P>public class Foo {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @TransactionAttribute(TransactionAttributeType.REQUIRED)&nbsp;&nbsp;&nbsp; public bar () {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // do something ...&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp; }</P>
<P>你也可以为一个代码段声明多个属性，应用多个服务。这是一个在EJB3.0里同时应用事务和安全服务到POJO的例子。</P>
<P>@SecurityDomain("other")public class Foo {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @RolesAllowed({"managers"})&nbsp;&nbsp;&nbsp; @TransactionAttribute(TransactionAttributeType.REQUIRED)&nbsp;&nbsp;&nbsp; public bar () {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // do something ...&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp; }</P>
<P>使用XML说明代码属性和配置声明性服务会导致冗长和不稳定的配置文件。下面是一个在Spring应用中的XML片段，其应用一个非常简单的Hibernate事务到方法Foo.bar()中。</P>
<P>&lt;!-- Setup the transaction interceptor --&gt;&lt;bean id="foo"&nbsp;&nbsp; class="org.springframework.transaction&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .interceptor.TransactionProxyFactoryBean"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="target"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;bean class="Foo"/&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="transactionManager"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="transactionManager"/&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="transactionAttributeSource"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="attributeSource"/&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&lt;/bean&gt;&lt;!-- Setup the transaction manager for Hibernate --&gt;&lt;bean id="transactionManager"&nbsp;&nbsp; class="org.springframework.orm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .hibernate.HibernateTransactionManager"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;!-- you need to setup the sessionFactory bean in&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; yet another XML element -- omitted here --&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="sessionFactory"/&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&lt;/bean&gt;&lt;!-- Specify which methods to apply transaction --&gt;&lt;bean id="transactionAttributeSource"&nbsp; class="org.springframework.transaction&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .interceptor.NameMatchTransactionAttributeSource"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;property name="properties"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;props&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;prop key="bar"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/props&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&lt;/bean&gt;</P>
<P>XML的复杂度会以几何级数增长，如果你向同一个POJO添加更多的拦截器(interceptors)（例如安全拦截器）。意识到只有XML配置文件的局限，Spring使用Apache Commons 元数据在Java源码中来说明事务属性。最新版本的Spring1.2也支持JDK-1.5风格注解。要使用事务元数据，你须要将上面的transactionAttributeSourc bean变成一个AttributesTransactionAttributeSource实例。并为元数据拦截器添加额外邦定。</P>
<P>&nbsp;&nbsp;&nbsp; class="org.springframework.aop.framework.autoproxy&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .DefaultAdvisorAutoProxyCreator"/&gt;&lt;bean id="transactionAttributeSource"&nbsp;&nbsp;&nbsp; class="org.springframework.transaction.interceptor&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .AttributesTransactionAttributeSource"&nbsp;&nbsp;&nbsp; autowire="constructor"/&gt;&lt;bean id="transactionInterceptor"&nbsp;&nbsp;&nbsp; class="org.springframework.transaction.interceptor&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .TransactionInterceptor"&nbsp;&nbsp;&nbsp; autowire="byType"/&gt;&lt;bean id="transactionAdvisor"&nbsp;&nbsp;&nbsp; class="org.springframework.transaction.interceptor&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .TransactionAttributeSourceAdvisor"&nbsp;&nbsp;&nbsp; autowire="constructor"/&gt;&lt;bean id="attributes"&nbsp;&nbsp;&nbsp; class="org.springframework.metadata.commons&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .CommonsAttributes"/&gt;</P>
<P>当你有很多事务性方法时，Spring元数据可以简化transactionAttributeSource。但是这并没有解决XML配置文件的根本问题。冗长而又繁琐的事务拦截器, transactionManager,和transactionAttributeSource仍然需要。</P>
<P>依赖注入(Dependency Injection, DI)</P>
<P>中间件容器的一个关键好处之一就是它可以让开发者建造一个关系耦合松散的应用。服务端客户只需要知道服务的接口。容器依据具体的实现实例化服务对象，使他们为客户端所用。在不改变接口和客户端代码的情况下，这使得容器可以在多种服务实现之间切换。</P>
<P>依赖注入的模式是实现耦合松散应用的最好方法之一。它更易用，比其他方法也明了多了，比如通过JNDI依赖性查询或容器回调。使用DI，框架就像一个对象工厂，它创建服务对象然后按照运行时配置将这些服务对象注入到应用的POJO里。站在应用开发者的角度，客户端POJO在被使用时可自动获得正确的服务对象。</P>
<P>Spring和EJB3.0都提供广泛的DI模式支持。但是他们之间仍存在很大的不同之处。Spring支持一般意义上且复杂的DI API，其基于XML配置文件。EJB3.0支持大多数普通服务对象（如EJB及context对象）的注入和任何简单注解的JDNI。 </P>
<P>EJB63.0注解非常简单易用。@Resource 标记表示注入大多数普通服务对象和JDNI对象。以下例子展示了怎样把服务的JDNI的默认DataSource 对象注入到POJO的一个属性变量中。DefaultDS是DataSource.的JDNI名字。MyDb变量在第一次被使用时被赋上了正确的值。</P>
<P>public class FooDao {&nbsp;&nbsp;&nbsp; @Resource (name="DefaultDS")&nbsp;&nbsp;&nbsp; DataSource myDb;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Use myDb to get JDBC connection to the database}</P>
<P>除了直接属性变量注入，Ejb3.0的@Resource注解也可以用来在setter方法里面注入对象。例如，下面的例子就是注入session context对象。应用从不会显示地调用setter方法，其在其他方法被调用前由容器来触发。</P>
<P>@Resource public void setSessionContext (SessionContext ctx) {&nbsp;&nbsp;&nbsp;&nbsp; sessionCtx = ctx; } </P>
<P>针对更复杂的服务对象，定义了专用的注入注解。例如，@EJB注释用来注入EJB的Stub，@PersistenceContext注解用来注入处理EJB3.0实体bean访问数据库的EntityManager对象。下面是一个怎样将EntityManager对象注入有状态的 session bean的例子。@PersistenceContext的type属性具体说明了被注入的EntityManager有一个扩展的事务transaction context。transaction context并不会同JTA transaction manager一起自动提交。因此它可以用在在一个会话横跨多个线程的应用事务中。</P>
<P>@Statefulpublic class FooBean implements Foo, Serializable {&nbsp;&nbsp;&nbsp; @PersistenceContext(&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type=PersistenceContextType.EXTENDED&nbsp;&nbsp;&nbsp; )&nbsp;&nbsp;&nbsp; protected EntityManager em;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public Foo getFoo (Integer id) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (Foo) em.find(Foo.class, id);&nbsp;&nbsp;&nbsp; }}</P>
<P>EJB3.0标准通过注解可以被注入的服务器资源。但是它并支持将用户定义的应用POJO之间的相互注入。</P>
<P>在Spring中，首先你必须为POJO中的服务对象定义一个setter方法。下面的例子说明POJO需要一个Hibernate session 的引用</P>
<P>public class FooDao {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; HibernateTemplate hibernateTemplate;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void setHibernateTemplate (HibernateTemplate ht) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; hibernateTemplate = ht;&nbsp;&nbsp;&nbsp; }&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Use hibernateTemplate to access data via Hibernate&nbsp;&nbsp;&nbsp; public Foo getFoo (Integer id) {&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return (Foo) hibernateTemplate.load (Foo.class, id);&nbsp;&nbsp;&nbsp; }}</P>
<P>然后，以XML里的元素作为桥梁具体描述容器怎样在运行时得到服务对象并将其注入到POJO里。以下是一个XML例子，具体描述了将一个数据源绑定到一个Hibernate session factory，然后从Hibernate session factory到Hibernate template object，最后从template object到应用的POJO。Spring代码如此复杂的部分原因是因为我们须手手动注入后台Hibernate plumbing objects。而EJB3.0 EntityManager是自动被服务器管理和配置。这又将我们带回到Spring并不像EJB3.0那样高度与服务整合的论点上。</P>
<P>&lt;bean id="dataSource"&nbsp;&nbsp; class="org.springframework&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .jndi.JndiObjectFactoryBean"&gt;&nbsp;&nbsp;&nbsp; &lt;property name="jndiname"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;value&gt;java:comp/env/jdbc/MyDataSource&lt;/value&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&lt;/bean&gt;&lt;bean id="sessionFactory"&nbsp;&nbsp; class="org.springframework.orm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .hibernate.LocalSessionFactoryBean"&gt;&nbsp;&nbsp;&nbsp; &lt;property name="dataSource"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="dataSource"/&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&lt;/bean&gt;&lt;bean id="hibernateTemplate"&nbsp;&nbsp; class="org.springframework.orm&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; .hibernate.HibernateTemplate"&gt;&nbsp;&nbsp;&nbsp; &lt;property name="sessionFactory"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="sessionFactory"/&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&nbsp;&nbsp;&nbsp; &lt;/bean&gt;&lt;bean id="fooDao" class="FooDao"&gt;&nbsp;&nbsp;&nbsp; &lt;property name="hibernateTemplate"&gt;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ref bean="hibernateTemplate"/&gt;&nbsp;&nbsp;&nbsp; &lt;/property&gt;&lt;/bean&gt;&lt;!-- The hibernateTemplate can be injected&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; into more DAO objects --&gt;</P>
<P>虽然，Spring里基于XML的依赖注入语法复杂，但却功能强大。你可以将任何POJO注入到另一个POJO，包括你自己在应用定义的那些POJO。假如你想在EJB3.0应用中用Spring的DI功能 ，你可以通过JNDI把一个Spring bean factory注入到EJB。在一些EJB3.0的应用服务器里，厂商可能会额外定义非标准的POJO注入API。一个很好的例子就是JBoss MicroContainer。它比Spring更一般化，因为它处理Aspect-Oriented Programming（AOP）的依赖。</P>
<P>结论</P>
<P>Spring和Ejb3.0虽然都是为了向企业服务提供耦合松散的POJO，但是使用了不同方法来达到这个目的。两者都大量地使用了依赖注入。</P>
<P>对于EJB3.0，基于标准的方案、注解的广泛使用、与应用服务器的高度整合都使得EJB3.0拥有更好的厂商无关性，更高的开发效率。依赖注入和集中的XML配置文件协调一致的使用使开发者能够构建更有弹性的应用，并且可以同时和几个应用服务提供者一起协作。</P>
<P>鸣谢<BR>作者感谢tephen Chambers,、ill Burke、Andy Oliver的珍贵意见。</P>
<P>资源<BR>Spring框架(参见CodeZoo: Spring) <BR>EJB 3.0 <BR>JBoss EJB 3.0 <BR>Oracle Application Server EJB 3.0 Preview </P>
<P>Michael Juntao Yuan 善长于提供end-to-end的企业解决方案， 也是一个移动方面的专家，是avid这个开源项目的支持者。</P><img src ="http://www.blogjava.net/Weibo-Forum/aggbug/33395.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Weibo-Forum/" target="_blank">微薄社区</a> 2006-03-03 10:53 <a href="http://www.blogjava.net/Weibo-Forum/archive/2006/03/03/33395.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>J2EE架构的6个最佳实践</title><link>http://www.blogjava.net/Weibo-Forum/archive/2006/03/03/33393.html</link><dc:creator>微薄社区</dc:creator><author>微薄社区</author><pubDate>Fri, 03 Mar 2006 02:49:00 GMT</pubDate><guid>http://www.blogjava.net/Weibo-Forum/archive/2006/03/03/33393.html</guid><wfw:comment>http://www.blogjava.net/Weibo-Forum/comments/33393.html</wfw:comment><comments>http://www.blogjava.net/Weibo-Forum/archive/2006/03/03/33393.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/Weibo-Forum/comments/commentRss/33393.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/Weibo-Forum/services/trackbacks/33393.html</trackback:ping><description><![CDATA[<P>--利用高级J2EE最佳实践来改善现有和将来的J2EE应用程序的架构和设计<BR>虽然许多文章曾经讨论过J2EE最佳实践。那么，为什么我还要再写一篇文章呢？本文究竟与以前的文章有何不同或者说比其他文章好在哪呢？<BR>　　首先，本文的目标读者是正在从事技术工作的架构师。为了避免浪费大家的才智，我会避免讲述一些陈腐的最佳实践，例如“日常构建（build daily）”、“测试一切（test everything）”和“经常集成（ integrate often）。 任何具有称职架构师的项目都有分工明确的、定义良好的团队结构。他们还为进行编码检查、构建代码（每日或在需要时）、进行测试（单元、集成和系统的）、部署和配置/释放管理而具备已记录的过程。<BR>　　其次，我将跳过通常吹捧的最佳实践，例如“基于接口的设计”、“使用著名的设计模型”以及“使用面向服务的架构”等。相反，我将集中讲述我曾学过并且使用了若干年的6（不是很多）个方面的in-the-trench课程。最后，本文的目的是让您思考一下自己的架构，提供工作代码示例或者解决方案超出了本文的范围。下面就让我介绍一下这6课：</P>
<P>第1课：切勿绕过服务器端验证<BR>　　作为一位软件顾问，我曾有机会不但设计并实现了Web应用程序，而且还评估/审核了许多Web应用程序。在复杂的、并且用JavaScript客户端封装的应用程序内，我经常遇到对用户输入信息执行大量检查的Web页面。即使HTML元素具有数据有效性的属性也如此，例如MAXLENGTH。只有在成功验证所有输入信息后，才能提交HTML表单。结果，一旦服务器端收到通知表单（请求），便恰当地执行业务逻辑。<BR>　　在此，您发现问题了么？开发人员已经做了许多重要的假设。例如，他们假设所有的Web应用程序用户都同样诚实。开发人员还假设所有用户将总是使用他们测试过的浏览器访问Web应用程序。还有很多其他的假设。这些开发人员忘记了利用可以免费得到的工具，通过命令行很容易地模拟类似浏览器的行为。事实上，通过在浏览器窗口中键入适当的URL，您可以发送任何“posted”表单，尽管如此，通过禁用这些页面的GET请求，您很容易地阻止这样的“表单发送”。但是，您不能阻止人们模拟甚至创建他们自己的浏览器来入侵您的系统。<BR>根本的问题在于开发人员不能确定客户端验证与服务器端验证的主要差别。两者的主要差别不在于验证究竟发生在哪里，例如在客户端或者在服务器端。主要的差别在于验证背后的目的不同。<BR>　　客户端验证仅仅是方便。执行它可为用户提供快速反馈——使应用程序似乎做出响应，给人一种运行桌面应用程序的错觉。<BR>　　另一方面，服务器端验证是构建安全Web应用程序必需的。不管在客户端一侧输入的是什么，它可以确保客户端送往服务器的所有数据都是有效的。<BR>　　因而，只有服务器端验证才可以提供真正应用程序级的安全。许多开发人员陷入了错误感觉的圈套：只有在客户端进行所有数据的验证才能确保安全。下面是说明此观点的一个常见的示例：<BR>　　一个典型的登录页面拥有一个用来输入用户名的文本框和一个输入密码的文本框。在服务器端，某人在接收servlet中可能遇到一些代码，这些代码构成了下面形式的SQL查询：<BR>"SELECT * FROM SecurityTable WHERE username = '" + form.getParameter("username") + "' AND password = '" + form.getParameter("password") + "';"，并执行这些代码。如果查询在结果集的某一行返回，则用户登录成功，否则用户登录失败。<BR>　　第一个问题是构造SQL的方式，但现在让我们暂时忽略它。如果用户在用户名中输入“Alice'--”会怎样呢？假设名为“Alice”的用户已经在SecurityTable中，这时此用户（更恰当的说法是黑客）成功地登录。我将把找出为什么会出现这种情况的原因做为留给您的一道习题。<BR>　　许多创造性的客户端验证可以阻止一般的用户从浏览器中这样登录。但对于已经禁用了JavaScript的客户端，或者那些能够使用其他类似浏览器程序直接发送命令（HTTP POST和GET命令）的高级用户（或者说黑客）来说，我们又有什么办法呢？服务器端验证是防止这种漏洞类型所必须的。这时，SSL、防火墙等都派不上用场了。</P>
<P>第2课：安全并非是附加物<BR>　　如第1课所述，我曾有幸研究过许多Web应用程序。我发现所有的JavaServer Page（JSP）都有一个共同的主题，那就是具有类似下面伪代码的布局：</P>
<P>&lt;%<BR>User user = <BR>session.getAttribute("User");<BR>if(user == null)<BR>{<BR>// redirect to <BR>// the logon page…<BR>} <BR>if(!user.role.equals("manager"))<BR>{<BR>// redirect to the<BR>// "unauthorized" page…<BR>}<BR>%&gt;<BR>&lt;!-<BR>HTML, JavaScript, and JSP<BR>code to display data and<BR>allow user interaction --&gt;</P>
<P>　　如果项目使用诸如Struts这样的MVC框架，所有的Action Bean都会具有类似的代码。尽管最后这些代码可能运行得很好，但如果您发现一个bug，或者您必须添加一个新的角色（例如，“guest”或者“admin”），这就会代表一场维护恶梦。<BR>　　此外，所有的开发人员，不管您多年轻，都需要熟悉这种编码模式。当然，您可以用一些JSP标签来整理JSP代码，可以创建一个清除派生Action Bean的基本Action Bean。尽管如此，由于与安全相关的代码会分布到多个地方，所以维护时的恶梦仍旧存在。由于Web应用程序的安全是强迫建立在应用程序代码的级别上（由多个开发人员），而不是建立在架构级别上，所以Web应用程序还是很可能存在弱点。<BR>　　很可能，根本的问题是在项目接近完成时才处理安全性问题。最近作为一名架构师，我曾在一年多的时间里亲历了某一要实现项目的6个版本，而直到第四版时我们才提到了安全性——即使该项目会将高度敏感的个人数据暴露于Web上，我们也没有注意到安全性。为了更改发布计划，我们卷入了与项目资助人及其管理人员的争斗中，以便在第一版中包含所有与安全相关的功能，并将一些“业务”功能放在后续的版本中。最终，我们赢得了胜利。而且由于应用程序的安全性相当高，能够保护客户的私有数据，这一点我们引以为荣，我们的客户也非常高兴。<BR>　　遗憾的是，在大多数应用程序中，安全性看起来并未增加任何实际的商业价值，所以直到最后才解决。发生这种情况时，人们才匆忙开发与安全相关的代码，而丝毫没有考虑解决方案的长期可维护性或者健壮性。忽视该安全性的另一个征兆是缺乏全面的服务器端验证，如我在第1课中所述，这一点是安全Web应用程序的一个重要组成部分。<BR>　　记住：J2EE Web应用程序的安全性并非仅仅是在Web.xml 和ejb-jar.xml文件中使用合适的声明，也不是使用J2EE技术，如Java 认证和授权服务（Java Authentication and Authorization Service，JAAS）。而是经过深思熟虑后的设计，且实现一个支持它的架构。</P>
<P>第3课：国际化（I18N）不再是纸上谈兵 <BR>　　当今世界的事实是许多英语非母语的人们将访问您的公共Web应用程序。随着电子政务的实行，由于它允许人们（某个国家的居民）在线与政府机构交互，所以这一点特别真实。这样的例子包括换发驾照或者车辆登记证。许多第一语言不是英语的人们很可能将访问这样的应用程序。国际化（即：“i18n”，因为在“internationalization”这个单词中，字母i和字母n之间一共有18个字母）使得您的应用程序能够支持多种语言。<BR>　　显然，如果您的JSP 页面中有硬编码的文本，或者您的Java代码返回硬编码的错误消息，那么您要花费很多时间开发此Web应用程序的西班牙语版本。然而，在Web应用程序中，为了支持多种语言，文本不是惟一必须“具体化”的部分。因为许多图像中嵌有文字，所以图形和图像也应该是可配置的。在极端的情况下，图像（或者颜色）在不同的文化背景中可能有完全不同的意思。类似地，任何格式化数字和日期的Java代码也必须本地化。但问题是：您的页面布局可能也需要更改。<BR>　　例如，如果您使用HTML表格来格式化和显示菜单选项、应用程序题头或注脚，则您可能必须为每一种支持的语言更改每一栏的最小宽度和表格其他可能的方面。为了适应不同的字体和颜色，您可能必须为每一种语言使用单独的样式表。<BR>　　显然，现在创建一个国际化的Web应用程序面临的是架构挑战而不是应用程序方面的挑战。一个架构良好的Web应用程序意味着您的JSP页面和所有与业务相关的（应用程序特有的）Java代码都不知不觉地选择了本地化。要记住的教训是：不要因为Java、J2EE支持国际化而不考虑国际化。您必须从第一天起就记住设计具有国际化的解决方案。</P>
<P>第4课：在MVC表示中避免共同的错误 <BR>　　J2EE开发已经足够成熟，在表示层，大多数项目使用MVC架构的某些形式，例如Struts。在这样的项目中，我常见到的现象是对MVC模式的误用。下面是几个示例。<BR>　　常见的误用是在模型层（例如，在Struts的Action Bean中）实现了所有的业务逻辑。不要忘了，表示层的模型层仍然是表示层的一部分。使用该模型层的正确方法是调用适当的业务层服务（或对象）并将结果发送到视图层（view layer）。用设计模式术语来说，MVC表示层的模型应该作为业务层的外观（Fa?ade）来实现。更好的方法是，使用核心J2EE模式（Core J2EE Patterns）中论述到的Business Delegate模式。这段自书中摘录的内容精彩地概述了将您的模型作为Business Delegate来实现的要点和优点：<BR>　　Business Delegate起到客户端业务抽象化的作用。它抽象化，进而隐藏业务服务的实现。使用Business Delegate，可以降低表示层客户端和系统的业务服务.之间的耦合程度。根据实现策略不同，Business Delegate可以在业务服务API的实现中，保护客户端不受可能的变动性影响。这样，在业务服务API或其底层实现变化时，可以潜在地减少必须修改表示层客户端代码的次数。<BR>　　另一个常见的错误是在模型层中放置许多表示类型的逻辑。例如，如果JSP页面需要以指定方式格式化的日期或者以指定方式排序的数据，某些人可能将该逻辑放置在模型层，对该逻辑来说，这是错误的地方。实际上，它应该在JSP页面使用的一组helper类中。当业务层返回数据时，Action Bean应该将数据转发给视图层。这样，无需创建模型和视图之间多余的耦合，就能够灵活支持多个视图层（JSP、Velocity、XML等）。也使视图能够确定向用户显示数据的最佳方式。<BR>　　最后，我见过的大多数MVC应用程序都有未充分应用的控制器。例如，绝大多数的Struts应用程序将创建一个基本的Action类，并完成所有与安全相关的功能。其他所有的Action Bean都是此基类的派生类。这种功能应该是控制器的一部分，因为如果没有满足安全条件，则首先调用不应该到达Action Bean（即：模型）。记住，一个设计良好的MVC架构的最强大功能之一是存在一个健壮的、可扩展的控制器。您应该利用该能力以加强自己的优势。</P>
<P>第5课：不要被JOPO束缚住手脚<BR>　　我曾目睹许多项目为了使用Enterprise JavaBean而使用Enterprise JavaBean。因为EJB似乎给项目带来优越感和妄自尊大的表现，所以有时它是显酷的要素（coolness factor）。而其他时候，它会使J2EE和EJB引起混淆。记住，J2EE和EJB不是同意词。EJB只是J2EE 的一部分，J2EE 是包含JSP、servlet、Java 消息服务（JMS）、Java数据库连接（JDBC）、JAAS、 Java管理扩展（JMX）和EJB在内的一系列技术，同样也是有关如何共同使用这些技术建立解决方案的一组指导原则和模式。<BR>　　如果在不需要使用EJB的情况下使用EJB，它们可能会影响程序的性能。与老的Web服务器相比，EJB一般对应用服务器有更多的需求。EJB提供的所有增值服务一般需要消耗更大的内存和更多的CPU时间。许多应用程序不需要这些服务，因此应用服务器要与应用程序争夺资源。<BR>　　在某些情况下，不必要地使用EJB可能使应用程序崩溃。例如，最近我遇到了一个在开源应用服务器上开发的应用程序。业务逻辑封装在一系列有状态会话bean（EJB）中。开发人员为了在应用服务器中完全禁用这些bean的“钝化”费了很大的劲。客户端要求应用程序部署在某一商用应用服务器上，而该服务器是客户端技术栈的一部分。该应用服务器却不允许关闭“钝化”功能。事实上，客户端不想改变与其合作的应用服务器的设任何置。结果，开发商碰到了很大的麻烦。（似乎）有趣的事情是开发商自己都不能给出为什么将代码用EJB（而且还是有状态会话bean）实现的好理由。不仅仅是开发商会遇到性能问题，他们的程序在客户那里也无法工作。<BR>　　在Web应用程序中，无格式普通Java 对象（POJO）是EJB强有力的竞争者。POJO是轻量级的，不像EJB那样负担额外的负担。在我看来，对许多EJB的优点，例如对象入池，估计过高。POJO是您的朋友，不要被它束缚住手脚。</P>
<P>第6课：数据访问并不能托管O/R映射 <BR>　　我曾参与过的所有Web应用程序都向用户提供从其他地方存取的数据，并且因此需要一个数据访问层。这并不是说所有的项目都需要标识并建立这样一个层，这仅仅说明这样层的存在不是隐含的就是明确的。如果是隐含的数据层，数据层是业务对象（即：业务服务）层的一部分。这适用于小型应用程序，但通常与大一些项目所接受的架构指导原则相抵触。<BR>　　总之，数据访问层必须满足或超出以下四个标准：<BR>　　具有透明性 <BR>　　业务对象在不知道数据源实现的具体细节情况下，可以使用数据源。由于实现细节隐藏在数据访问层的内部，所以访问是透明的。<BR>　　易于迁移<BR>　　数据访问层使应用程序很容易迁移到其他数据库实现。业务对象不了解底层的数据实现，所以迁移仅仅涉及到修改数据访问层。进一步地说，如果您正在部署某种工厂策略，您可以为每个底层的存储实现提供具体的工厂实现。如果是那样的话，迁移到不同的存储实现意味着为应用程序提供一个新的工厂实现。<BR>　　尽量减少业务对象中代码复杂性 <BR>　　因为数据访问层管理着所有的数据访问复杂性，所以它可以简化业务对象和使用数据访问层的其他数据客户端的代码。数据访问层，而不是业务对象，含有许多与实现相关的代码（例如SQL语句）。这样给开发人员带来了更高的效率、更好的可维护性、提高了代码的可读性等一系列好处。<BR>　　把所有的数据访问集中在单独的层上<BR>　　由于所有的数据访问操作现在都委托给数据访问层，所以您可以将这个单独的数据访问层看做能够将应用程序的其他部分与数据访问实现相互隔离的层。这种集中化可以使应用程序易于维护和管理。<BR>　　注意：这些标准都不能明确地调出对O/R（对象到关系）映射层的需求。O/R映射层一般用O/R映射工具创建，它提供对象对关系数据结构的查看和感知（look-and-feel）。在我看来，在项目中使用O/R映射与使用EJB类似。在大多数情况下，并不要求它。对于包含中等规模的联合以及多对多关系的关系型数据库来说，O/R映射会变得相当复杂。由于增加O/R 映射解决方案本身的内在复杂性，例如延迟加载（lazy loading）、高速缓冲等，您将为您的项目带来更大的复杂性（和风险）。<BR>　　为了进一步支持我的观点，我将指出按照Sun Microsystem所普及的实体Bean（O/R映射的一种实现）的许多失败的尝试，这是自1.0版以来一直折磨人的难题。在SUN的防卫措施中，一些早期的问题是有关EJB规范的开发商实现的。这依次证明了实体Bean规范自身的复杂性。结果，大多数J2EE架构师一般认为从实体Bean中脱离出来是一个好主意。<BR>　　大多数应用程序在处理他们的数据时，只能进行有限次数的查询。在这样的应用程序中，访问数据的一种有效方法是实现一个数据访问层，该层实现执行这些查询的一系列服务（或对象、或API）。如上所述，在这种情况下，不需要O/R映射。当您要求查询灵活性时，O/R映射正合适，但要记住：这种附加的灵活性并不是没有代价的。<BR>　　就像我承诺的那样，在本文中，我尽量避免陈腐的最佳实践。相反，关于J2EE项目中每一位架构师必须做出的最重要的决定，我集中讲解了我的观点。最后，您应该记住：J2EE并非某种具体的技术，也不是强行加入到解决方案中的一些首字母缩写。相反，您应该在适当的时机，恰当的地方，使用合适的技术，并遵循J2EE的指导原则和J2EE中所包含的比技术本身重要得多的实践。</P>
<P>关于作者<BR>Tarak Modi是North Highland（一家管理和技术咨询公司）的高级专家。他在COM、MTS、COM+、.NET、J2EE以及 CORBA等方面有丰富的专业经验。2002年曾与人合作编写Professional Java Web Services （Wrox Press）。作者的个人网站是：<A href="http://www.tekNirvana.com">http://www.tekNirvana.com</A>。</P><img src ="http://www.blogjava.net/Weibo-Forum/aggbug/33393.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/Weibo-Forum/" target="_blank">微薄社区</a> 2006-03-03 10:49 <a href="http://www.blogjava.net/Weibo-Forum/archive/2006/03/03/33393.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>