<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://dev.kingdee.com/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>我的企业架构视点</title><link>http://dev.kingdee.com/pages/hjm/default.aspx</link><description /><dc:language>en-US</dc:language><generator>Kingdee Spaces 2.1</generator><item><title>应用模式分析系列四（Identity Field）</title><link>http://dev.kingdee.com/pages/hjm/blog/archive/2007/09/14/239811.aspx</link><pubDate>Fri, 14 Sep 2007 08:42:00 GMT</pubDate><guid isPermaLink="false">d85b68fd-db4c-4672-8cdd-89d3217b2e06:239811</guid><dc:creator>hujinmin</dc:creator><slash:comments>1</slash:comments><comments>http://dev.kingdee.com/pages/hjm/blog/comments/239811.aspx</comments><wfw:commentRss>http://dev.kingdee.com/pages/hjm/blog/commentrss.aspx?PostID=239811</wfw:commentRss><description>&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; EAS使用UUID作主键已经很多年了，K3 HR也是。但遇到的问题应该都不少，今天在Martin Fowler的书中读到这么一句话：GUID唯一的缺点在于生成的串比较大，这可能会成为一个大问题。&lt;br&gt;后面还紧跟了一句：长键总是难于输入也难于读写，这也会带来性能问题，尤其是对索引。&lt;br&gt;&lt;br&gt;呵呵，看来Martin大叔是也是深有体会的，其实我们后来遇到的很多问题都是与之相关的。&lt;br&gt;1）数据太大导致数据库过大。和K3同等数据量的账套相比，EAS的账套要大许多，这中间GUID的贡献就&amp;#8220;功不可没&amp;#8221;&lt;br&gt;2）索引问题：使用GUID之后关联属性都是大字符串，而这都是索引字段，于是乎索引性能问题出来了。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 假设数据库页大小为m，索引项大小为n，那么一个页上所能容纳的索引个数 t= [m/n]，可以认为磁盘的读写速度恒定（假设1秒读入p页），那么索引的访问速度是v=p*t=p*m/n，即访问速度与索引项大小成反比。（此处没考虑B树本身的查询时间，只是考虑叶子节点的访问时间）。&lt;br&gt;3）虚模式问题。由于GUID过大，JVM又有最大内存限制，因此使用GUID做PK的虚模式在高并发下极易造成OOM，后期被迫改为TempTable方案，可惜又遇到不同数据库物理临时表的问题。这就是为什么让人感觉EAS的虚模式很复杂的原因，其实本质和K3没什么区别，只是牺牲在GUID上而已。&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 其实我们可以分析一下使用GUID的一个主要原因。我们的GUID本质就是一个IdentityField，即对应BOS中的主键（PK），对于PK有三点需要考虑：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1）有意义还是无意义。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 比如凭证，如果我们选择凭证编号做PK，就是有意义PK，我们目前使用的应该是无意义PK，仅做内部标识。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2）单一还是复合&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 我们使用一个ID做PK，很明显是单一，慢着，真的是单一吗？一张表里可以有多种单据的数据啊？类型去哪里了呢？其实我们的ID现在应该是ID ＋ BOSType的复合，只是由于BOSType的唯一性，使得我们可以将两个信息融合到一个ID中。这样合理是否真的合适我不敢肯定。&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;3）唯一性&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;所谓的唯一性实际上是由一个作用域的，就像我们进行业务建模，也是有领域一说，这就是域模型的一个特征吧。唯一可以是当前库表唯一（或者叫做域唯一），当前系统唯一，全球唯一，很显然EAS是选择后者。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;在我心目中对于Identity&amp;nbsp;应该是这样的：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1）Identity应该只是一种唯一性标志，一个对象可以有多个IdentityField，适用于不同场景。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2）Identity应该是一个对象，这个对象可以只有一个属性，也可以有多个属性，即可以单一可以复合。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3）频繁使用的Identity应该是域唯一的，这样可以做到短小精干。&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 以EAS作例子，我认为Identity应该分为两种：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; InnerIdentity和OuterIdentity&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; InnerIdentity应该包含：一个Int型的Identity列，一个BOSType列，一个Version列（String型）。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;InnerIdentity只在系统内部使用，比如关联属性就变为一个复合关联，ORMMapping也许就会变成：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;A.FinterID=B.FxxxInterID AND A.FVersion = B.FxxxVersion AND A.FBOSType =B.FxxxBosType&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; OuterIdentity则包含：一个GUID列，一个BOSType列，一个Version（String型）。其中后两个信息是对InnerIdentity中相同属性的引用。&lt;/div&gt;
&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; OuterIdentity的主要用于外部数据交换。比如WEBService，异构数据导入导出等场合。&lt;br&gt;&lt;/div&gt;
&lt;div&gt;&lt;br&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 新版本的EAS中，已经越来越接近了。&lt;/div&gt;&lt;img src="http://dev.kingdee.com/aggbug.aspx?PostID=239811" width="1" height="1"&gt;</description></item><item><title>应用模式分析系列三（Lazy Load）</title><link>http://dev.kingdee.com/pages/hjm/blog/archive/2007/09/14/239809.aspx</link><pubDate>Fri, 14 Sep 2007 08:41:00 GMT</pubDate><guid isPermaLink="false">d85b68fd-db4c-4672-8cdd-89d3217b2e06:239809</guid><dc:creator>hujinmin</dc:creator><slash:comments>1</slash:comments><comments>http://dev.kingdee.com/pages/hjm/blog/comments/239809.aspx</comments><wfw:commentRss>http://dev.kingdee.com/pages/hjm/blog/commentrss.aspx?PostID=239809</wfw:commentRss><description>&lt;div&gt;延迟加载/惰性加载（Lazy Load）
&lt;div&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 一个对象，它并未包含所有数据，但是知道如何获取这些数据。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 上述是定义，但我觉得这个定义并未体现出LazyLoad的精华，因为我们真正需要的不是它知道如何取得，而是能够根据需要取得，从而根据实际情况来优化性能或内存。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 刚进EAS的时候正处于LazyLoad应用高峰期，整天慢耳都是&amp;#8220;惰性加载&amp;#8221;几个字，其实惰性加载的实现方式很多，常见的有：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 延迟初始化(Lazy Initialization)：说白了就是一个对象一开始就是空对象（除了对象标识符就是赤裸裸的空了），获取其任何数据都先判断是否为空，不为空就单独获取。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 虚代理（Virtual Proxy）：通过一个代理对象对其进行包装，当调用代理对象的方法时才会真正加载数据。这和WEB通过Proxy加载图片类似。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 值保持器（ValueHolder）：也就是个值对象，这个翻译感觉太直白了。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 重影（ghost）：这个和LazyInitialization类似，不同之处在于它一开始除了ID都是NULL，但是一旦有方法被访问就会加载其他属性。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LazyLoad的优缺点技术人员都应该比较清楚，我就说说缺点：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; LazyInitialization和Ghost都会有加大数据库访问次数的问题。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ValueHolder和Proxy则存在强类型转换的问题。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 具体处理方案则需要开发人员自行选择了，我个人比较推崇Ghost。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 但是Ghost也会存在问题，如果是一个大对象的访问，那么在第一次访问其方法时就可能出现性能问题，从而未达到LazyLoad的本来目的。幸好还有一种变通，通过对对象属性进行分类，可以将第一次访问加载变更为根据特定方法加载一批特定属性的方式。比如对对象中的图片就可以在运行时放到需要显示图片时单独加载。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在EAS元数据中如果针对属性增加Zorder参数，对属性进行分层，再通过层次与方法的绑定，是否可以做到特定方法加载特定层次的数据呢？默认行为可以是Ghost的默认方式。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 复杂度是大大增加了，但是对性能应该还是有益的，就是要看实现的信价比了。&lt;/div&gt;&lt;/div&gt;&lt;img src="http://dev.kingdee.com/aggbug.aspx?PostID=239809" width="1" height="1"&gt;</description></item><item><title>应用模式分析系列二（Identity Map）</title><link>http://dev.kingdee.com/pages/hjm/blog/archive/2007/09/14/239807.aspx</link><pubDate>Fri, 14 Sep 2007 08:40:00 GMT</pubDate><guid isPermaLink="false">d85b68fd-db4c-4672-8cdd-89d3217b2e06:239807</guid><dc:creator>hujinmin</dc:creator><slash:comments>1</slash:comments><comments>http://dev.kingdee.com/pages/hjm/blog/comments/239807.aspx</comments><wfw:commentRss>http://dev.kingdee.com/pages/hjm/blog/commentrss.aspx?PostID=239807</wfw:commentRss><description>&lt;div&gt;
&lt;div&gt;&lt;strong&gt;标识映射（Identity Map）&lt;/strong&gt;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 通过内存映射标志，确保每个对象只加载一次。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 上面是定义，实际上就是在内存中维持一个对象缓存（通常是Map缓存），每次获取对象的时候，先到缓存中获取，不存在再访问数据库，当然需要考虑更新时的同步。这不是个什么新鲜东西，给一种常用的做法赋予一个深奥的名字就是大师们常干的事情。对于这个模式我没有太多个人的想法，只有以下2点：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;1、Map的层次。如果一个对象会存在并发更新，则只能作为会话层缓存映射。如果一个对象属于只读对象，则可以作为全局缓存映射。在EAS的Cache中提供了CacheRegion的划分方式，结合Session对象，我们可以灵活定义映射层次。嗯，我认为Session是个好东西，如果不被滥用的话。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;2、映射的大小：由于JAVA虚拟机的要求，在32位的服务端对内存是有严格要求的，基本上就在1.3G左右，以前元数据是做了映射缓存，可惜占用内存太多，被无情地Cut了，改为采用惰性加载＋WeakReferrence的处理方式，我还是认为缓存起来比较好，以前也思考过对应的方案。其中和Anderson就讨论过是否可以通过专用的CacheServer来满足需求。以前没有集群方案，现在有了集群，似乎该方案开始具备可行行了。&lt;/div&gt;&lt;/div&gt;&lt;img src="http://dev.kingdee.com/aggbug.aspx?PostID=239807" width="1" height="1"&gt;</description></item><item><title>J2EE基础知识温故一：JNDI</title><link>http://dev.kingdee.com/pages/hjm/blog/archive/2007/09/14/239804.aspx</link><pubDate>Fri, 14 Sep 2007 08:39:00 GMT</pubDate><guid isPermaLink="false">d85b68fd-db4c-4672-8cdd-89d3217b2e06:239804</guid><dc:creator>hujinmin</dc:creator><slash:comments>1</slash:comments><comments>http://dev.kingdee.com/pages/hjm/blog/comments/239804.aspx</comments><wfw:commentRss>http://dev.kingdee.com/pages/hjm/blog/commentrss.aspx?PostID=239804</wfw:commentRss><description>&lt;div&gt;
&lt;div&gt;&lt;font face="Courier New"&gt;InitialContext ic = new InitialContext();&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;&lt;font face="Courier New"&gt;这句代码很是容易，在JAVA入门学习过程中，应该每个人都写过。不过是否每个人都知道这句代码做了什么事情呢？为什么后面的代码就像是魔术师的帽子，可以从里面拿出你所需要的一切？&lt;/font&gt;&lt;/div&gt;
&lt;div&gt;如果对于上述问题不清楚的朋友可以参考以下内容：&lt;/div&gt;
&lt;div&gt;
&lt;blockquote&gt;从单机编程转向 EJB 技术和分布式计算这些更复杂领域的 Java 开发人员常常会陷入困境：编写成功地游历 JDNI 迷宫的代码会很困难，多计算机和配置也增加了出错的可能性。在本文中，EJB开发人员 Daniel Would 解释了如何编写可以成功地找到在 JNDI 名称空间中发布的 EJB 组件的客户代码。他向您展示了使处理更容易的各种编程选项，并提供了一些可以在您自己的应用程序中作为实用工具类使用的代码。&lt;/blockquote&gt;
&lt;p&gt;如果您在让客户机应用程序看到 EJB 组件这一方面从来都没有任何问题，并且认为在 Java 平台的不同安装上、或者在完全不同的计算机上运行您的客户机和 bean 一点也不复杂，那么本文可能不会吸引您。不过，如果您才刚刚开始，并且在试图用真实的配置做任何事情时看见弹出了许多奇怪和意义不明的错误消息，那么就请读下去。&lt;/p&gt;
&lt;p&gt;&lt;a name=1&gt;&lt;span class=atitle&gt;EJB 错误？不要慌！&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;您已经在自己所钟爱的 Java 书籍中读过了关于企业 Javabean 技术的那一章，也已经练习过了简单的 HelloWorld bean，并遵循所建议的部署过程发布了它。现在您得编写一个客户机，以便通过这个客户机来调用这个杰作。因此您写出了类似清单 1 中的代码：&lt;/p&gt;&lt;br&gt;&lt;br&gt;&lt;a name=listing1&gt;&lt;b&gt;清单 1. 一个调用 bean 的非常简单的客户机&lt;/b&gt;&lt;/a&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=5 bgColor=#eeeeee border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;/code&gt;&lt;pre class=section&gt;InitialContext ic = new InitialContext();
Object or = ic.lookup("ejb/HelloWorldHome");
if (or != null) {
  // Narrow the return object to the Home class type
    HelloWorldHome home = 
      (HelloWorldHome)PortableRemoteObject.narrow(or, 
        HelloWorldHome.class);
  // Create an EJB object instance using the home interface.
    HelloWorld hw = home.create();
  // Invoke the method
    System.out.Println(hw.hello());
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;
&lt;p&gt;在命令行中运行这个客户机，使用手头最方便的一个 Java 安装 ―― 即应用服务器使用的那一个。所有事情都很完美！带着成功的喜悦，您转移到第二台计算机上运行您的客户机。这回，您得到了一个可怕的错误消息。首先，您可能得到 &lt;code&gt;java.lang.NoClassDefFoundError: javax/ejb/EJBObject&lt;/code&gt; ，然后是一大堆其他的 &lt;code&gt;NoClassDefFoundError&lt;/code&gt; s，因为您忘记提交一个带有必需的 stub 和 tie 的 JAR 文件，并且没有提供或者考虑到其他各种 EJB 相关的内容。不过最终，您的客户机运行到了第一行有意思的代码（ &lt;code&gt;InitialContext ic = new InitialContext();&lt;/code&gt; ）。在到达这一行时得到的异常 ―― 您几乎肯定会得到一个异常 ―― 将会根据您所选择的特定 &lt;i&gt;上下文 provider&lt;/i&gt;而有所不同。 &lt;/p&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=0 border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"&gt;&lt;br&gt;&lt;img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;table class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;tbody&gt;
&lt;tr align=right&gt;
&lt;td&gt;&lt;img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=0 border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td vAlign=center&gt;&lt;img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0&gt;&lt;br&gt;&lt;/td&gt;
&lt;td vAlign=top align=right&gt;&lt;a class=fbox href="http://www.ibm.com/developerworks/cn/java/j-namespace/#main"&gt;&lt;b&gt;&lt;font color=#0000ff&gt;回页首&lt;/font&gt;&lt;/b&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;&lt;br&gt;
&lt;p&gt;&lt;a name=2&gt;&lt;span class=atitle&gt;解释这些术语&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在我们继续往下之前，定义几个术语会很有帮助。计算世界使用的都是一些奇怪的术语、时髦的语汇和首字母缩写词，Java 技术也不例外（也许这应该是 &lt;code&gt;JavaIsNoException&lt;/code&gt; ？）。如果您遇到了上面所说的问题，那么这里面的术语可能会让您感到有些无所适从。所以让我们讨论在本文中将会遇到的术语，搞明白它们的意思是一个好主意。 &lt;/p&gt;&lt;b&gt;名称空间、上下文、初始上下文和子上下文&lt;/b&gt; 这些术语都是有关位置的 ―― 是从客户机的角度看时 EJB 组件所在的概念性的位置。将一个 &lt;i&gt;名称空间&lt;/i&gt; 想像为一个城镇，城镇中的商店由 EJB home接口（我们将在稍后讨论它）表示。 &lt;i&gt;上下文&lt;/i&gt;是城镇中的一个位置。 &lt;i&gt;初始上下文&lt;/i&gt; 是您开始时所在的位置 ―― 就像它是到城镇的道路。而 &lt;i&gt;子上下文&lt;/i&gt;是街道名。 &lt;br&gt;&lt;b&gt;home接口（home interface）和远程接口（remote interface）&lt;/b&gt; 企业 JavaBean 组件有三个部分。首先是 bean 代码本身。然后是 &lt;i&gt;home接口&lt;/i&gt;，它定义了创建您自己的 EJB bean 的方法。home接口是在名称空间中发布的。当您有了home接口后，就可以调用 &lt;code&gt;Create()&lt;/code&gt; 以从应用服务器获得远程接口。获得了远程接口后，就可以调用构成实际的 EJB 代码的方法了。 &lt;br&gt;如何将这些术语应用到您的城镇模拟中去呢？到达正确的城镇并找到正确的地址后，您需要走进商店或者按铃（调用 &lt;code&gt;Create()&lt;/code&gt; ）。这个过程对于您要去的所有商店都是一样的，不过，您所收到的响应取决于是由谁来提供服务 ―― 比如是一位屠夫、一位面包师还是一位烛台制作者。这个响应代表了 &lt;i&gt;远程接口&lt;/i&gt;。每个人都是不同的并且可以要求他提供不同的东西。您必须知道与您交谈的人（即 bean）的职业才能提出正确的问题(即调用正确的方法) ―― 向一位屠夫要一条面包可不妥当。 &lt;br&gt;&lt;b&gt;CosNaming、LDAP 和 JNDI&lt;/b&gt; Java 命名和目录接口（Java Naming and Directory Interface &lt;i&gt;JNDI&lt;/i&gt;）提供了一个标准接口，它指明您需要如何与名称空间交互。我们所提到的 &lt;i&gt;LDAP&lt;/i&gt;和 &lt;i&gt;CosNaming&lt;/i&gt; 就是 JDNI 名称空间类型。现在扩展我们的比喻：JNDI 是城镇的模板，而 CosNaming 和 LDAP 是特定的城镇。它们以相似的方式操作，但是有不同的布局。 &lt;br&gt;
&lt;table cellSpacing=0 cellPadding=0 border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"&gt;&lt;br&gt;&lt;img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;table class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;tbody&gt;
&lt;tr align=right&gt;
&lt;td&gt;&lt;img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=0 border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td vAlign=center&gt;&lt;img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0&gt;&lt;br&gt;&lt;/td&gt;
&lt;td vAlign=top align=right&gt;&lt;a class=fbox href="http://www.ibm.com/developerworks/cn/java/j-namespace/#main"&gt;&lt;b&gt;&lt;font color=#0000ff&gt;回页首&lt;/font&gt;&lt;/b&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;&lt;br&gt;
&lt;p&gt;&lt;a name=3&gt;&lt;span class=atitle&gt;属性提供了一个映射&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;让我们看一看如何使用所有这些元素以成功地从远程计算机上调用我们的 EJB 组件上的方法。为了让客户程序连接到您精心打造的 EJB 组件，需要几样东西。首先，它需要客户代码的所有 JAR 文件、一般性的 EJB 相关 JAR 文件如 J2EE.jar 以及在部署 bean 时生成的 stub 和 tie。这些文件让您的客户机可以一直到达初始上下文。&lt;/p&gt;
&lt;p&gt;接下来您的客户机需要的信息是一些属性的值。首先，您将需要几个 &lt;code&gt;java.naming.factory.initial&lt;/code&gt; 的值。该属性指向一个提供初始上下文工厂的类。该属性的一个典型值是 &lt;code&gt;com.sun.jndi.cosnaming.CNCtxFactory&lt;/code&gt; ，这也是我们在这里的几个例子中所使用的值。这个类存在于 &lt;code&gt;rt.jar&lt;/code&gt; 中，因而它是基本 JVM 的一部分。工厂是由 CosNaming 命名服务器所使用的，但是 JVM 还包括一个 LDAP 工厂。我们在后面将会看到，不同的应用服务器提供它们自己的初始上下文工厂。 &lt;/p&gt;
&lt;p&gt;这个类连同命名服务器 URL 和端口号的详细信息，用于生成与名称空间交互的 &lt;code&gt;InitialContext&lt;/code&gt; 类。不过，如果没有 provider URL，那么它将连接到 &lt;code&gt;localhost&lt;/code&gt; 的 900 端口（或者您的上下文工厂的其他默认端口）。要连接到远程服务器，您需要有属性 &lt;code&gt;java.naming.provider.url&lt;/code&gt; 的一个值。 &lt;/p&gt;
&lt;p&gt;新程序员对于所有这些觉得很难理解的原因是：不管您在应用服务器本地运行任何东西，这东西通常都会听话地工作。这是由于环境照管了一切，当您要求一个 &lt;code&gt;InitialContext&lt;/code&gt; 时，环境就会给您提供您想要的那个。但是当您将客户即转移到不同的计算机上时，就得靠自己了。您需要知道拷贝哪一个 JAR 文件，以及要做哪些设置。我知道有些人为使他们的客户机正确工作，将应用服务器上的所有 JAR 文件都拷贝到第二台计算机上！ &lt;/p&gt;
&lt;p&gt;在默认情况下， &lt;code&gt;InitialContext&lt;/code&gt; 工厂是在 &lt;code&gt;jndi.properties&lt;/code&gt; 中定义的，这个工厂类有默认的服务器 URL 和端口号默认值。这个文件在类路径中（这一般意味着在本地目录）或者在您的类路径中的任何 JAR 中。不同的应用服务器可能在不同的 JAR 文件中提供它们的默认值，WebSphere Application Server 在 &lt;code&gt;namingclient.jar&lt;/code&gt; 中储存一个默认副本。要指定您自己的默认值，只需要编辑在类路径中的第一个副本。这是配置属性的一种方法，如果缺少命令行或者代码驱动的设置，那么客户机将使用 &lt;code&gt;jndi.properties&lt;/code&gt; 中的值。不过，虽然这可能适合于简单的设置，但是如果处理多个服务器和名称空间，那么您可能希望一个客户一个客户地进行配置。 &lt;/p&gt;
&lt;p&gt;这些属性是如何根据我们要使用的名称空间而使用不同的值的呢？正如前面提到的，有两种形式的 JNDI 名称空间：CosNaming 和 LDAP。其中每一个都有与之相关联的传输：分别是 IIOP 和 LDAP。一个 LDAP 名称空间使用 LDAP 传输（您将用一个像 &lt;code&gt;ldap://myldapnameserver&lt;/code&gt; 这样的 URL 连接到它），而 CosNaming 使用一个 IIOP 传输（您将用一个像 &lt;code&gt;iiop://mycosnamingserver&lt;/code&gt; 这样的 URL 连接到它）。CosNaming 的默认端口号是 900，而 LDAP 的默认端口号是 389。不过，任何给定的名称空间服务器实现使用的默认值可能是不同的。 &lt;/p&gt;
&lt;p&gt;&lt;a name=N100F0&gt;&lt;span class=smalltitle&gt;用命令行配置属性&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;让我们看一下如何用命令行配置属性。如果您要在家里自己练习，进入 JDK 安装中的 &lt;code&gt;bin&lt;/code&gt; 目录。在这个文件夹中，可以找到一个名为 &lt;code&gt;tnameserv.exe&lt;/code&gt; 的程序（对于 Windows）或者只是 &lt;code&gt;tnameserv&lt;/code&gt; （对于基于 UNIX 的系统）。通过执行这个程序将会在端口 900 启动一个示例 CosNmaing 命名服务器。 &lt;/p&gt;
&lt;p&gt;现在正好可以用一个可以查看 CosNaming 名称空间的实用工具来装备您自己。我本人使用 Eclipse 作为开发环境，我在下面的 &lt;a href="http://www.ibm.com/developerworks/cn/java/j-namespace/#resources"&gt;&lt;font color=#0000ff&gt;参考资料&lt;/font&gt;&lt;/a&gt; 部分中提供了到 JNDI 浏览器插件的链接。理论上，您应该可以将一个名称空间浏览器指向自己计算机的端口 900，并看到一个非常无聊的空名称空间（尽管一些应用服务器在默认情况下会用很多不同的内容填充名称空间）。为了丰富我们的名称空间，我们现在将编写一个简单的程序以在它里面放一些内容，如清单 2 所示： &lt;/p&gt;&lt;br&gt;&lt;br&gt;&lt;a name=listing2&gt;&lt;b&gt;清单 2. 一个简单的 cosNaming 名称空间交互&lt;/b&gt;&lt;/a&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=5 bgColor=#eeeeee border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;/code&gt;&lt;pre class=section&gt;package example.publisher;

import javax.naming.InitialContext;

public class Publish {

    public static void main(String[] args) {
        //
        //This example creates a subcontext in a namespace
        //
        try{
            InitialContext ic = new InitialContext();
            ic.createSubcontext("Test");
        }catch(Exception e){
            System.out.println(e);
            e.printStackTrace();
            
        }
    }
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;
&lt;p&gt;这个应用程序将假定为得到正确的初始上下文件所需的所有属性都是可用的。所以现在可以从命令行运行它并在运行时提供这些属性（其中 URL 要根据您的环境作调整）：&lt;/p&gt;
&lt;table cellSpacing=0 cellPadding=5 bgColor=#eeeeee border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;/code&gt;&lt;pre class=section&gt;java -Djava.naming.factory.initial=com.sun.jndi.cosnaming.CNCtxFactory 
     -Djava.naming.provider.url=iiop://mymachine:900 
       example.publisher.Publish
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;
&lt;p&gt;一切正常，我们的客户会找到示例名称空间的上下文并创建名为 &lt;code&gt;Test&lt;/code&gt; 的子上下文。您可以用名称空间浏览器确认这一点。 &lt;/p&gt;
&lt;p&gt;现在试着在一台计算机上运行命名服务器，用同一个命令行（当然，对 URL 再次做了调整）在另一台计算机上运行清单 2 中的应用程序。它运行起来应该没有问题（您可能需要修改这个例子以改变所限定的内容，甚至删除子上下文而不是创建它，这样在第二次运行时您就可以确信它已经起过作用了）。&lt;/p&gt;
&lt;p&gt;&lt;a name=N10127&gt;&lt;span class=smalltitle&gt;在应用程序中配置属性&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;那么，如果不希望在命令行中设置这些属性怎么办？还有另外一个方法。可以在程序中显式地声明这些属性。这意味着您不需要为 &lt;code&gt;java&lt;/code&gt; 命令提供特殊的选项。改变清单 2 中的代码以显式地设置所需要的属性后，它看起来与清单 3 中的代码一样： &lt;/p&gt;&lt;br&gt;&lt;br&gt;&lt;a name=listing3&gt;&lt;b&gt;清单 3. 简单的 cosNaming 名称空间交互，在应用程序代码中设置属性&lt;/b&gt;&lt;/a&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=5 bgColor=#eeeeee border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;/code&gt;&lt;pre class=section&gt;package example.publisher;

import javax.naming.InitialContext;

public class Publish {

    public static void main(String[] args) {
        //
        //This example creates a subcontext in a namespace
        //
        try{
            Properties prop = new Properties();
            prop.setProperty("java.naming.factory.initial",
              "com.sun.jndi.cosnaming.CNCtxFactory");
            prop.setProperty("java.naming.provider.url",
              "iiop://mymachine:900");
            InitialContext ic = new InitialContext(prop);
            ic.createSubcontext("Test");
        }catch(Exception e){
            System.out.println(e);
            e.printStackTrace();
            
        }
    }
} 
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;
&lt;p&gt;现在这个程序不再需要长长的命令行配置，不过要记住，以这种方式编写的应用程序硬编码了这些设置。&lt;/p&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=0 border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"&gt;&lt;br&gt;&lt;img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;table class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;tbody&gt;
&lt;tr align=right&gt;
&lt;td&gt;&lt;img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=0 border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td vAlign=center&gt;&lt;img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0&gt;&lt;br&gt;&lt;/td&gt;
&lt;td vAlign=top align=right&gt;&lt;a class=fbox href="http://www.ibm.com/developerworks/cn/java/j-namespace/#main"&gt;&lt;b&gt;&lt;font color=#0000ff&gt;回页首&lt;/font&gt;&lt;/b&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;&lt;br&gt;
&lt;p&gt;&lt;a name=4&gt;&lt;span class=atitle&gt;寻找通往 bean 的道路&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;到目前为止，我们已经看到了几个可以证明我们已连接到远程名称空间并完成一些任务的例子，尽管这些任务是相当无聊的 ―― 创建一个子上下文。在实际中，一般是由工具来为您完成所有的创建和发布工作，您 &lt;i&gt;真正&lt;/i&gt; 需要的做是查找一个对象。在这一节，我们将在 CosNaming 名称空间中获得已发布的 HelloWorld bean 的 &lt;code&gt;Home&lt;/code&gt; 接口。然后我们再看一下如何在 LDAP 名称空间中找到它的 &lt;code&gt;Home&lt;/code&gt; 接口。 &lt;/p&gt;
&lt;p&gt;为了说明问题，我们假设您已经部署了 HelloWorld bean，它的home接口 &lt;code&gt;HelloWorldHome&lt;/code&gt; 发布在 &lt;code&gt;example/HelloWorldHome&lt;/code&gt; （如果您只想试一试，但是又不想自己创建一个 HellowWorld bean，那么在 &lt;a href="http://www.ibm.com/developerworks/cn/java/j-namespace/#resources"&gt;&lt;font color=#0000ff&gt;参考资料&lt;/font&gt;&lt;/a&gt;中有一个下载预打包的 bean JAR 文件的链接以及一个使用它的客户机的文件）。 &lt;/p&gt;
&lt;table cellSpacing=0 cellPadding=0 align=right border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width=10&gt;&lt;img height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10&gt;&lt;/td&gt;
&lt;td&gt;
&lt;table cellSpacing=0 cellPadding=5 border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td bgColor=#eeeeee&gt;&lt;a name=sidebar1&gt;&lt;b&gt;一个上下文技巧&lt;/b&gt;&lt;/a&gt;&lt;br&gt;
&lt;p&gt;在名称空间 URL 格式中，您可以设置自己的初始上下文，使之从树上比默认值更高的位置开始。例如，如果在我们的例子中对于 provider URL 使用 &lt;code&gt;iiop://mymachine:900/example&lt;/code&gt; ，那么您只需要查询 &lt;code&gt;HelloWorldHome&lt;/code&gt; ，而不是 &lt;code&gt;example/HelloWorldHome&lt;/code&gt; ，初始上下文将在 &lt;code&gt;example&lt;/code&gt; 内。如果您在一个名称空间中在同一个结构下进行几次查询，那么这将会有所帮助，这样，如果改变了设置，代码中要改变的惟一部分只是 provider URL。 &lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;在上一节，我们进行了连接到命名服务器的艰苦工作，现在我们所需要的就只是查询 EJB 组件了。这需要我们向查询方法传递一个字符串，它表示从 &lt;code&gt;InitialContext&lt;/code&gt; （您在城镇中的出发点）到想要去的 &lt;code&gt;HomeInterface&lt;/code&gt; （房屋或者商店）的方向。听起来简单 ―― 但是这里您所选择的特定上下文工厂就要产生影响了。像 WebSphere 这样的应用服务器所带的工厂类并不总是把您放到名称空间的根上。所以我们为了查询 &lt;code&gt;HomeInterface&lt;/code&gt; 而需要的字符串会根据 &lt;code&gt;InitialContext&lt;/code&gt; 将您所放到城镇中的位置而变化。并且，在本地服务器上，上下文工厂可能将您放到与在远程服务器上不同的起始位置。 &lt;/p&gt;
&lt;p&gt;因为这个原因，我建议您不要像在清单 3 中那样硬编码所使用的查询字符串，而是用命令行或者属性文件传递。特别是对于具有多步的体系结构更应如此。例如，您可能有一个调用一个 EJB 组件的客户机，这个 bean 可能又需要调用也许是在不同的服务器上的第二个 EJB 组件！在这种情况下，属性应该在每一步中传递。这为反复实验（trial-and-error）查询提供了一种简单的机制，并且只需要相对较少的改变就可以得到最终应用程序的灵活性。因此让我们看一个示例查询应用程序。在清单 4 中，属性是在程序中设置的，但是它又以命令行值为依据。这样命令行与在我们前一个例子中使用的稍有不同，如我们在下面所看到的。&lt;/p&gt;&lt;br&gt;&lt;br&gt;&lt;a name=listing4&gt;&lt;b&gt;清单 4. 查询一个home接口&lt;/b&gt;&lt;/a&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=5 bgColor=#eeeeee border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;/code&gt;&lt;pre class=section&gt;package example.lookup;
import java.util.Properties;
import javax.naming.InitialContext;
import javax.rmi.PortableRemoteObject;

import example.HelloWorld;
import example.HelloWorldBean;
import example.HelloWorldHome;
import javax.naming.InitialContext;

public class Lookup {

    public static void main(String[] args) {
        //
        //This example looks up the home interface of an EJB to a namespace
        //
        try{
            Properties prop = new Properties();
            prop.setProperty("java.naming.factory.initial",args[0]);
            prop.setProperty("java.naming.provider.url",args[1]);
            InitialContext ic = new InitialContext(prop);
            Object or = ic.lookup(args[2]);
            if (or != null) {
                // Narrow the return object to the Home class type
                HelloWorldHome home = 
                  (HelloWorldHome)PortableRemoteObject.narrow(or, 
                      HelloWorldHome.class);
                // Create an EJB object instance using the home interface.
                HelloWorld hw = home.create();
                // Invoke the method
                System.out.Println(hw.hello());
            }
        }catch(Exception e){
            System.out.println(e);
            e.printStackTrace();
            
        }
    }
}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;
&lt;p&gt;这个程序是用三个参数调用的：要使用的上下文工厂、provider URL 和包含要查询的名字的字符串。我们已经知道前两个是什么，那么第三个呢？&lt;/p&gt;
&lt;p&gt;如果您仍然使用 &lt;code&gt;tnameserv&lt;/code&gt; 作为命名服务器，那么您很可能将 bean 直接发布到 &lt;code&gt;/example/HelloWorldHome&lt;/code&gt; 。在这种情况下，只要将 &lt;code&gt;/example/HelloWorldHome&lt;/code&gt; 作为第三个参数传递就可以进行成功的查询。不过，如果您使用的命名服务器有一个更复杂的命名空间，那么可能会存在由所使用的部署工具增加的额外的层。例如，WebSphere 在默认情况下将 JavaBean 部署到 &lt;code&gt;ejb/&lt;/code&gt; ，但是这不是名称空间的根，并且只有当使用 WebSphere 的上下文工厂时，通过传入字符串 &lt;code&gt;/ejb/example/HelloWorldHome&lt;/code&gt; 才会使您处于名称空间中的正确位置。 如果您使用一个与应用服务器提供的不同的上下文工厂（例如在一台只有标准 Java 安装的计算机上运行客户机时就需要这样做）时，这个问题会更加恶化。不过，应用服务器的命名服务器文档应当说明在查询 EJB 组件时将会从名称空间的什么地方开始。看一下文档中的例子，再用浏览器查看名称空间以确定其客户机的 &lt;code&gt;InitialContext&lt;/code&gt; 会将它们放到什么地方。名称空间往往会循环，这样您就可以试着沿着一个分枝到无限。这意味着从最开始的上下文可以找到一条回家的道路。 &lt;/p&gt;
&lt;p&gt;总之，下面是传递相应参数给清单 4 中的应用程序的命令行：&lt;/p&gt;
&lt;table cellSpacing=0 cellPadding=5 bgColor=#eeeeee border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;/code&gt;&lt;pre class=section&gt;java Lookup com.sun.jndi.cosnaming.CNCtxFactory 
  iiop://mymachine:900 example/HelloWorldHome
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;
&lt;p&gt;在 CosNaming 中，名称空间子上下文由斜线(/)字符分隔，这与标准 URL 一样。LDAP 的语法则不同，我们在下面将会看到。&lt;/p&gt;
&lt;p&gt;&lt;a name=N101C8&gt;&lt;span class=smalltitle&gt;介绍 LDAP&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;现在让我们再加上 LDAP。就本文的内容来讲，LDAP 是另一个 JNDI 名称空间，但是它的结构的表示方法与 CosNaming 名称空间的表示方法截然不同。它还需要一个不同的上下文工厂 ―― 但是这不成问题，因为我们总会在命令行指定正确的工厂（在我们的例子中，我们将使用属于基本 JVM 一部分的工厂，但是要记住不同的应用服务器可能有自己的工厂）。并且它需要一个指向不同命名服务器的指针 ―― 并且，幸运的是，我们也是在命令行中指定它的。当然，表示home接口位置的字符串是不同的，但是您猜如何？是的，我们还是在命令行中指定它。您可以看到使用这些命令行调用的好处：所有要做的只是改变调用我们的测试程序的方式，理论上我们可以到达任何 JDNI 命名服务器，甚至可以顺利地从 CosNaming 转移到 LDAP 而不用改变任何代码。是的，这是只是理论，当然无论如何，关键是要有正确的参数。&lt;/p&gt;
&lt;p&gt;一些命名服务器会保护部分命名空间，这意味着只能发布到允许的区域。假设您有一个运行的 LDAP 服务器，它的细节如下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;URL: &lt;code&gt;ldap://mymachine:1389&lt;/code&gt; 
&lt;li&gt;BaseDN: &lt;code&gt;c=myldap&lt;/code&gt; &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;LDAP 名称空间中的树结构一般来说像下面这样：&lt;/p&gt;
&lt;table cellSpacing=0 cellPadding=5 bgColor=#eeeeee border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;/code&gt;&lt;pre class=section&gt;ibm-wsnName=MyServer,ibm-wsnName=HelloWorldHome
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;
&lt;p&gt;不过，当我们将这个字符串传递给程序时，我们需要反转它（不要问我为什么）。所以我们使用的字符串看起来是这样的：&lt;/p&gt;
&lt;table cellSpacing=0 cellPadding=5 bgColor=#eeeeee border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;/code&gt;&lt;pre class=section&gt;ibm-wsnName=HelloWorldHome,ibm-wsnName=MyServer,
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;
&lt;p&gt;BaseDN 表示在名称空间中您希望开始的位置。对于给定的 LDAP 命名服务器来说这可以是很多位置，这取决它是如何构造的。在这个例子中，我们直接到 &lt;code&gt;c=myldap&lt;/code&gt; 的根。但是如果我们希望跳到名称空间中的一个树，那么可以指定 &lt;code&gt;ibm-wsnTree=myTree,c=myldap&lt;/code&gt; 作为 BaseDN 而不是跳到那一点。 &lt;/p&gt;
&lt;p&gt;这样，我们将传递给程序的命令行参数就像下面这样：&lt;/p&gt;
&lt;table cellSpacing=0 cellPadding=5 bgColor=#eeeeee border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;&lt;/code&gt;&lt;pre class=section&gt;java Lookup com.sun.jndi.ldap.LdapCtxFactory ldap://mymachine:1389/c=myldap/ 
     ibm-wsnName=HelloWorldHome,ibm-wsnName=MyServer
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=0 align=right border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td width=10&gt;&lt;img height=1 alt="" src="http://www.ibm.com/i/c.gif" width=10&gt;&lt;/td&gt;
&lt;td&gt;
&lt;table cellSpacing=0 cellPadding=5 border=1&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td bgColor=#eeeeee&gt;&lt;a name=sidebar2&gt;&lt;b&gt;可能出现的错误消息&lt;/b&gt;&lt;/a&gt;&lt;br&gt;
&lt;p&gt;无论如何，您毫无疑问会熟悉在部署 EJB 组件时出现的各种异常消息。下面是您将会经常看到的几个异常消息（反正我常见到它们），以及您遇到它们的原因： &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CORBA.OBJECT_NOT_EXIST&lt;/code&gt; : 您在错误的位置上查找 EJB。 &lt;br&gt;
&lt;li&gt;&lt;code&gt;CORBA.MARSHALL _ &lt;i&gt;something&lt;/i&gt; &lt;/code&gt;: CORBA marshall 异常经常发生。它们有不同的细节，但是它们基本上都表明同一件事情：您得到一些数据，但这不是您的客户机所期待的。也许您查询了错误的东西，也许您的客户机所知道的 EJB 组件类的版本与实际部署的不一样。或者出现了问题，客户机 ORB 不能理解服务器 ORB 所发送的内容。 &lt;br&gt;
&lt;li&gt;&lt;code&gt;javax.naming.NoInitialContextException&lt;/code&gt; : 噢！您没有指定上下文工厂或者 provider URL，也许命名服务器没有运行。 &lt;/li&gt;&lt;/ul&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;这里我们指定一个 LDAP 上下文工厂 ，然后传递 LDAP 服务器的名字以及我们想要开始的位置。然后是到要查询的 EJB 组件的反转的路径。我们可以用这个命令行调用在 CosNaming 例子中使用的同一段代码（ &lt;a href="http://www.ibm.com/developerworks/cn/java/j-namespace/#listing4"&gt;&lt;font color=#0000ff&gt;清单 4&lt;/font&gt;&lt;/a&gt;）。 &lt;/p&gt;
&lt;p&gt;当然，本文中使用的代码没有理由不能构成一个助手类的一个方法 ―― 它带三个参数，并且返回 &lt;code&gt;Object or&lt;/code&gt; ，这个对象是在试图做任何事情之前调用 &lt;code&gt;ic.lookup(args[2])&lt;/code&gt; 时返回的。然后，当您需要进行一次查询时，只需使用这个助手类，向它传递适合于当前情况的适当参数，取回您所需要的对象引用，并准备将它窄化到实际的类。（ &lt;b&gt;注意&lt;/b&gt;：我不保证这种类的性能，而只是提供这段原本就是如此的代码，我或者 IBM 对此不作任何保证。）当然，可以通过反射实现一种完全通用的方式，但这会使事情复杂得多，也超出了本文的范围。 &lt;/p&gt;
&lt;p&gt;在我们结束之前还有最后一件事要考虑。您可以编写一个结合了我们在清单 3 和 4 中使用的技术的客户机。它会检查命令行中是否给出了一个值；如果有，它就设置这些值，如果没有，它就使用硬编码的值。这样，在程序中可以有有意义的默认值，但是如果需要，也可以用命令行选项覆盖它们。只需要对代码进行微不足道的更改。&lt;/p&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=0 border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;img height=1 alt="" src="http://www.ibm.com/i/v14/rules/blue_rule.gif" width="100%"&gt;&lt;br&gt;&lt;img height=6 alt="" src="http://www.ibm.com/i/c.gif" width=8 border=0&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;table class=no-print cellSpacing=0 cellPadding=0 align=right&gt;
&lt;tbody&gt;
&lt;tr align=right&gt;
&lt;td&gt;&lt;img height=4 alt="" src="http://www.ibm.com/i/c.gif" width="100%"&gt;&lt;br&gt;
&lt;table cellSpacing=0 cellPadding=0 border=0&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td vAlign=center&gt;&lt;img height=16 alt="" src="http://www.ibm.com/i/v14/icons/u_bold.gif" width=16 border=0&gt;&lt;br&gt;&lt;/td&gt;
&lt;td vAlign=top align=right&gt;&lt;a class=fbox href="http://www.ibm.com/developerworks/cn/java/j-namespace/#main"&gt;&lt;b&gt;&lt;font color=#0000ff&gt;回页首&lt;/font&gt;&lt;/b&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br&gt;&lt;br&gt;
&lt;p&gt;&lt;a name=5&gt;&lt;span class=atitle&gt;安全到家&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;作为回顾：下面是在有多个 EJB 查询和多个应用服务器的情况下，要使任何系统运行而应该有或者应该知道的最重要的四件事情：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在任何给定阶段，下一阶段的所有 stub 和 tie 都必须在类路径上。除非环境知道这个类是什么样的，否则您不能窄化一个对象以使用它。 
&lt;li&gt;每一阶段都需要与 EJB 相关的一般性 JAR 文件，如 &lt;code&gt;J2EE.jar&lt;/code&gt; 。 &lt;br&gt;
&lt;li&gt;以参数的形式传递上下文工厂类型、命名服务器名和 JDNI 查询字符串。以便能够轻松顺应变化。 
&lt;li&gt;知道您的名称空间。记住您的 JDNI 查询字符串需要将您从在名称空间中开始的位置移动到您的对象所储存的位置。但是您并不总是在同一位置开始！用一个工具浏览名称空间，并了解在本地和远程查询中是从什么位置开始的。 &lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;对于习惯于编写全部在同一台计算机上执行的代码的开发人员来说，浏览远程名称空间可能是一个困难的过程。希望本文的提示和代码可以帮助您设置并运行您的分布式 EJB 应用程序。当您掌握了 JNDI 名称空间后，再去看一下 developerWorks 上由 Brett McLaughlin 所写的 &lt;i&gt;EJB 最佳实践&lt;/i&gt;系列（请参阅 &lt;a href="http://www.ibm.com/developerworks/cn/java/j-namespace/#resources"&gt;&lt;font color=#0000ff&gt;参考资料&lt;/font&gt;&lt;/a&gt;），以获得用于优化代码的一些很棒的技巧。 &lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;img src="http://dev.kingdee.com/aggbug.aspx?PostID=239804" width="1" height="1"&gt;</description></item><item><title>应用模式分析系列一（UOW）</title><link>http://dev.kingdee.com/pages/hjm/blog/archive/2007/09/14/239802.aspx</link><pubDate>Fri, 14 Sep 2007 08:37:00 GMT</pubDate><guid isPermaLink="false">d85b68fd-db4c-4672-8cdd-89d3217b2e06:239802</guid><dc:creator>hujinmin</dc:creator><slash:comments>3</slash:comments><comments>http://dev.kingdee.com/pages/hjm/blog/comments/239802.aspx</comments><wfw:commentRss>http://dev.kingdee.com/pages/hjm/blog/commentrss.aspx?PostID=239802</wfw:commentRss><description>&lt;div&gt;&lt;strong&gt;工作单元模式（UOW）&lt;br&gt;&lt;/strong&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这个模式的作用在于维护一个对象列表，能协调变化的写入和并发问题的处理。这个说法过于抽象，实际上我们可以将其想象为一个特殊的容器，一个对象被构造出来后就丢到这个容器里，对于这个对象的任何变动，容器都能知晓，最后由容器来决定提交的时候要做什么。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 举个最直接的例子：我们有个操作，需要更新一张凭证的某个科目，在直接写SQL的过程中很直观，我们可能会通过Update voucherentry set Faccount='xxxx' where ......来完成我们的需求。在OO模型下，我们就改变为:&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;VoucherEntry ve = vo.getEntry();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;ve.setAccount('xxx');&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;ve.update();&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这种行为很也很直观，但是我们需要思考一下ve.update()这个行为会做什么。最直接的考量应该是ORMMapping引擎或DAO入口对象会生成上述的SQL，那我们要继续思考为什么会生成上面的SQL，而不是update VoucherEntry set FID='ddd',FAcount='xxx',FAmount=3432......这种语句呢（即对每个属性无论是否修改都予以更新）。其实很不幸的是很多引擎正是生成了后面这种SQL，BOS ORMMapping以前正是这么干的，：（&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这就反映出了UOW这个容器的作用。比较常见的有两种UOW模型：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 模型一（工作控制单元）：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 刚从数据库中得到的对象被clone一份，并被标记为&amp;#8220;Clean&amp;#8221;，在提交时通过比较两个对象来生成对应的SQL完成更新。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 模型二（调用注册方式）：&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 刚从数据库中得到的对象被标记为"Clean",对其任何变动都需要在容器中注册，并将其标记为&amp;#8220;Dirty&amp;#8221;，提交时根据&amp;#8220;Dirty&amp;#8221;标志完成提交。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;模型一比较容易理解，就是新旧对象的差异比较，可以认为是一种被动处理策略。模型二则是任何修改都需要标记，根据标记来处理，可以认为是一种主动处理策略。这两种模型都尤其有缺点，模型一对开发人员有要求，如果遗漏了注册，就可能出错，目前比较可行的方式是通过setter切面做处理。模型二则对读操作有一定性能上的负面影响。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 在EAS实际使用中，采用的Agent的机制我认为是上述两个方案的结合体：在Read的时候会去Clone一个Old对象出来，由于Agent特殊的实现机制，对于Agent对象的任何修改也会在对象自身做标志：isModified。真正提交操作的时候只会提交做了标记的属性，在服务端又会重新get一个对象出来，然后作对象比较，来生成SQL。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 这中间的Clone操作给性能带来了意想不到的负面效果，由于Agent采用继承的实现方式，因此在Clone时使用了最直接的DeepCopy，而这个DeepCopy由于关联属性太多，耗时非常大。测试中一张单据（100条分录）的DeepCopy居然要2－3s。&lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 上述描述并不是说明我们的Agent机制不好，其实它还是比较合理的实现了UOW模式，只是细节还要考虑清楚而已。抛砖引玉：我们是否缺少一个BizEntity的概念呢？我们的一张单据是由多个实体构成的，比如凭证就是有凭证头、凭证体、辅助账三个实体构成，我们可以认为这三个实体构成了一个BizEntity，在做DeepCopy的时候如果只Copy BizEntity中实体的简单属性及关联属性的Key，性能应该还是很快的。&lt;br&gt;&lt;/div&gt;&lt;img src="http://dev.kingdee.com/aggbug.aspx?PostID=239802" width="1" height="1"&gt;</description></item><item><title>产品性能与用户操作的关系</title><link>http://dev.kingdee.com/pages/hjm/blog/archive/2007/09/14/239796.aspx</link><pubDate>Fri, 14 Sep 2007 08:30:00 GMT</pubDate><guid isPermaLink="false">d85b68fd-db4c-4672-8cdd-89d3217b2e06:239796</guid><dc:creator>hujinmin</dc:creator><slash:comments>2</slash:comments><comments>http://dev.kingdee.com/pages/hjm/blog/comments/239796.aspx</comments><wfw:commentRss>http://dev.kingdee.com/pages/hjm/blog/commentrss.aspx?PostID=239796</wfw:commentRss><description>&lt;p&gt;&amp;nbsp; &lt;em&gt;&lt;font face="Courier New" size=2&gt;一点想法，还不够成熟，暂记录在此。&lt;/font&gt;&lt;/em&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;div align=left&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;不常用操作&lt;/span&gt;&lt;/b&gt;&lt;span&gt;：指执行频率在&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;次&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;天，甚至更少的操作。&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;div align=left&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;常用操作&lt;/span&gt;&lt;/b&gt;&lt;span&gt;：可以分为业务操作与辅助操作。&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;div align=left&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;业务操作&lt;/span&gt;&lt;/b&gt;&lt;span&gt;：如：单据提交、审核、查看等关键业务功能的操作。&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;div align=left&gt;&lt;span&gt;&lt;b&gt;&lt;span&gt;辅助操作&lt;/span&gt;&lt;/b&gt;&lt;span&gt;：为完成主要业务功能，而进行的必要操作。比如：为提交单据，必须进行&lt;/span&gt;&lt;span&gt;F7&lt;/span&gt;&lt;span&gt;查询、录入物料、科目等。这里提交是业务操作，而&lt;/span&gt;&lt;span&gt;F7&lt;/span&gt;&lt;span&gt;、录入物料、科目属于辅助操作。&lt;/span&gt;&lt;/div&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p align=left&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;
&lt;blockquote dir=ltr style="MARGIN-RIGHT: 0px"&gt;
&lt;p align=left&gt;&lt;span&gt;用户对于&lt;b&gt;辅助操作&lt;/b&gt;要求非常高，一般不宜大于&lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;span&gt;秒。而对于&lt;b&gt;业务操作&lt;/b&gt;相对要求比较低，可以放宽到&lt;/span&gt;&lt;span&gt;5&lt;/span&gt;&lt;span&gt;秒。&lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;用户的对于性能感受首先是&lt;b&gt;辅助操作&lt;/b&gt;的响应性。其次&lt;b&gt;业务操作&lt;/b&gt;响应性。而对于不常用操作一般不会太多关心或者说要求不高。&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;img src="http://dev.kingdee.com/aggbug.aspx?PostID=239796" width="1" height="1"&gt;</description></item><item><title>我们能应用AOP么？</title><link>http://dev.kingdee.com/pages/hjm/blog/archive/2007/09/14/239787.aspx</link><pubDate>Fri, 14 Sep 2007 08:25:00 GMT</pubDate><guid isPermaLink="false">d85b68fd-db4c-4672-8cdd-89d3217b2e06:239787</guid><dc:creator>hujinmin</dc:creator><slash:comments>1</slash:comments><comments>http://dev.kingdee.com/pages/hjm/blog/comments/239787.aspx</comments><wfw:commentRss>http://dev.kingdee.com/pages/hjm/blog/commentrss.aspx?PostID=239787</wfw:commentRss><description>&lt;p&gt;&amp;nbsp; &lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;此文主要是转贴，不过在转贴前我还是有些个人想法，&lt;span&gt;AOP&lt;/span&gt;这个概念已经出来很久了，实际上&lt;span&gt;EAS BOS&lt;/span&gt;也有一定程度的应用，正如&lt;span&gt;CoreJava &lt;/span&gt;在&lt;span&gt;DynamicProxy&lt;/span&gt;一章中所述：动态代理对于应用程序员来说不是很常见，但是对于系统编程来说却是非常重要。我想这里的应用程序员应该可以对应为&lt;span&gt;EAS&lt;/span&gt;项目组的开发人员，系统编程则应该对应&lt;span&gt;BOS&lt;/span&gt;及基础部门的开发人员了。&lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;由于今年一个可预期的工作是对&lt;span&gt;BOS&lt;/span&gt;一些底层功能进行重构，因此如何保证现有产品的稳定，同时能够改造有缺陷的实现成了一个焦点问题，我想&lt;span&gt;AOP&lt;/span&gt;应该是可以大有作为的&lt;span&gt;&amp;lt;ZT&amp;gt;&lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;Aspect Oriented Programming(AOP)&lt;/span&gt;&lt;span&gt;，面向切面编程，是一个比较热门的话题。&lt;span&gt;AOP&lt;/span&gt;主要实现的目的是针对业务处理过程中的切面进行提取，它所面对的是处理过程中的某个步骤或阶段，以获得逻辑过程中各部分之间低耦合性的隔离效果。比如我们最常见的就是日志记录了，举个例子，我们现在提供一个服务查询学生信息的，但是我们希望记录有谁进行了这个查询。如果按照传统的&lt;span&gt;OOP&lt;/span&gt;的实现的话，那我们实现了一个查询学生信息的服务接口&lt;span&gt; (StudentInfoService)&lt;/span&gt;和其实现类（&lt;span&gt;StudentInfoServiceImpl.java&lt;/span&gt;），同时为了要进行记录的话，那我们在实现类&lt;span&gt;(StudentInfoServiceImpl.java)&lt;/span&gt;中要添加其实现记录的过程。这样的话，假如我们要实现的服务有多个呢？那就要在每个实现的类都添加这些记录过程。这样做的话就会有点繁琐，而且每个实现类都与记录服务日志的行为紧耦合，违反了面向对象的规则。那么怎样才能把记录服务的行为与业务处理过程中分离出来呢？看起来好像就是查询学生的服务自己在进行，但是背后日志记录对这些行为进行记录，但是查询学生的服务不知道存在这些记录过程，这就是我们要讨论&lt;span&gt;AOP&lt;/span&gt;的目的所在。&lt;span&gt;AOP&lt;/span&gt;的编程，好像就是把我们在某个方面的功能提出来与一批对象进行隔离，这样与一批对象之间降低了耦合性，可以就某个功能进行编程。&lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;我们直接从代码入手吧，要实现以上的目标，我们可以使用一个动态代理类&lt;span&gt;(Proxy)&lt;/span&gt;，通过拦截一个对象的行为并添加我们需要的功能来完成。&lt;span&gt;Java&lt;/span&gt;中的&lt;span&gt;java.lang.reflect.Proxy&lt;/span&gt;类和&lt;span&gt; java.lang.reflect.InvocationHandler&lt;/span&gt;接口为我们实现动态代理类提供了一个方案，但是该方案针对的对象要实现某些接口；如果针对的目的是类的话，&lt;span&gt;cglib&lt;/span&gt;为我们提供了另外一个实现方案。等下会说明两者的区别。&lt;span&gt; &lt;br&gt;&lt;/span&gt;一、接口的实现方案：&lt;span&gt; &lt;br&gt;1&lt;/span&gt;）首先编写我们的业务接口（&lt;span&gt;StudentInfoService.java&lt;/span&gt;）：&lt;span&gt; &lt;br&gt;public interface StudentInfoService{ &lt;br&gt;&amp;nbsp;void findInfo(String studentName); &lt;br&gt;} &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;及其实现类（&lt;span&gt;StudentInfoServiceImpl.java&lt;/span&gt;）：&lt;span&gt; &lt;br&gt;public class StudentInfoServiceImpl implements StudentInfoService{ &lt;br&gt;&amp;nbsp;public void findInfo(String name){ &lt;br&gt;&amp;nbsp; System.out.println("&lt;/span&gt;你目前输入的名字是&lt;span&gt;:"+name); &lt;br&gt;&amp;nbsp;} &lt;br&gt;} &lt;br&gt;2&lt;/span&gt;）现在我们需要一个日志功能，在&lt;span&gt;findInfo&lt;/span&gt;行为之前执行并记录其行为，那么我们就首先要拦截该行为。在实际执行的过程中用一个代理类来替我们完成。&lt;span&gt;Java&lt;/span&gt;中为我们提供了实现动态代理类的方案： &lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;1'&lt;/span&gt;&lt;span&gt;处理拦截目的的类（&lt;span&gt;MyHandler.java&lt;/span&gt;）&lt;span&gt; &lt;br&gt;import org.apache.log4j.Logger; &lt;br&gt;import java.lang.reflect.InvocationHandler; &lt;br&gt;import java.lang.reflect.Proxy; &lt;br&gt;import java.lang.reflect.Method; &lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;public class MyHandler implements InvocationHandler{ &lt;br&gt;&amp;nbsp;private Object proxyObj; &lt;br&gt;&amp;nbsp;private static Logger log=Logger.getLogger(MyHandler.class); &lt;br&gt;&amp;nbsp; &lt;br&gt;&amp;nbsp;public Object bind(Object obj){ &lt;br&gt;&amp;nbsp; this.proxyObj=obj; &lt;br&gt;&amp;nbsp; return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this); &lt;br&gt;&amp;nbsp;} &lt;br&gt;&amp;nbsp; &lt;br&gt;&amp;nbsp;public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{ &lt;br&gt;&amp;nbsp; Object result=null; &lt;br&gt;&amp;nbsp; try{ &lt;br&gt;&amp;nbsp;&amp;nbsp; //&lt;/span&gt;&lt;span&gt;请在这里插入代码，在方法前调用&lt;span&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp; log.info("&lt;/span&gt;调用&lt;span&gt;log&lt;/span&gt;日志方法&lt;span&gt;"+method.getName()); &lt;br&gt;&amp;nbsp;&amp;nbsp; result=method.invoke(proxyObj,args); //&lt;/span&gt;原方法&lt;span&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp; //&lt;/span&gt;请在这里插入代码，方法后调用&lt;span&gt; &lt;br&gt;&amp;nbsp; }catch(Exception e){ &lt;br&gt;&amp;nbsp;&amp;nbsp; e.printStackTrace(); &lt;br&gt;&amp;nbsp; } &lt;br&gt;&amp;nbsp; return result; &lt;br&gt;&amp;nbsp;} &lt;br&gt;} &lt;br&gt;2'&lt;/span&gt;我们实现一个工厂，为了方便我们使用该拦截类&lt;span&gt;(AOPFactory.java)&lt;/span&gt;：&lt;span&gt; &lt;br&gt;public class AOPFactory{ &lt;br&gt;&amp;nbsp;private static Object getClassInstance(String clzName){ &lt;br&gt;&amp;nbsp; Object obj=null; &lt;br&gt;&amp;nbsp; try{ &lt;br&gt;&amp;nbsp;&amp;nbsp; Class cls=Class.forName(clzName); &lt;br&gt;&amp;nbsp;&amp;nbsp; obj=(Object)cls.newInstance(); &lt;br&gt;&amp;nbsp; }catch(ClassNotFoundException cnfe){ &lt;br&gt;&amp;nbsp;&amp;nbsp; System.out.println("ClassNotFoundException:"+cnfe.getMessage()); &lt;br&gt;&amp;nbsp; }catch(Exception e){ &lt;br&gt;&amp;nbsp;&amp;nbsp; e.printStackTrace(); &lt;br&gt;&amp;nbsp; } &lt;br&gt;&amp;nbsp; return obj; &lt;br&gt;&amp;nbsp;} &lt;br&gt;&amp;nbsp; &lt;br&gt;&amp;nbsp;public static Object getAOPProxyedObject(String clzName){ &lt;br&gt;&amp;nbsp; Object proxy=null; &lt;br&gt;&amp;nbsp; MyHandler handler=new MyHandler(); &lt;br&gt;&amp;nbsp; Object obj=getClassInstance(clzName); &lt;br&gt;&amp;nbsp; if(obj!=null) { &lt;br&gt;&amp;nbsp;&amp;nbsp; proxy=handler.bind(obj); &lt;br&gt;&amp;nbsp; }else{ &lt;br&gt;&amp;nbsp;&amp;nbsp; System.out.println("Can't get the proxyobj"); &lt;br&gt;&amp;nbsp;&amp;nbsp; //throw &lt;br&gt;&amp;nbsp; } &lt;br&gt;&amp;nbsp; return proxy; &lt;br&gt;&amp;nbsp;} &lt;br&gt;} &lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;3)&lt;/span&gt;&lt;span&gt;基本的拦截与其工厂我们都实现了，现在测试（&lt;span&gt;ClientTest.java&lt;/span&gt;）：&lt;span&gt; &lt;br&gt;public class ClientTest{ &lt;br&gt;&amp;nbsp;public static void main(String[] args){ &lt;br&gt;&amp;nbsp; StudentInfoService studentInfo=(StudentInfoService)AOPFactory.getAOPProxyedObject("StudentInfoServiceImpl"); &lt;br&gt;&amp;nbsp; studentInfo.findInfo("&lt;/span&gt;阿飞&lt;span&gt;"); &lt;br&gt;&amp;nbsp;} &lt;br&gt;} &lt;br&gt;&lt;/span&gt;输出结果（看你的&lt;span&gt;log4j&lt;/span&gt;设置）：&lt;span&gt; &lt;br&gt;[INFO]&lt;/span&gt;调用&lt;span&gt;log&lt;/span&gt;日志方法&lt;span&gt;findInfo &lt;br&gt;&lt;/span&gt;你目前输入的名字是&lt;span&gt;:&lt;/span&gt;阿飞&lt;span&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;这样我们需要的效果就出来了，业务处理自己在进行，但是我们实现了日志功能，而业务处理&lt;span&gt;(StudentInfoService)&lt;/span&gt;根本不知道存在该行为的。但是&lt;span&gt;Java&lt;/span&gt;中提供的动态代理类的实现是针对实现了某些接口的类，如果没有实现接口的话，不能创建代理类，看以上部分：&lt;span&gt; &lt;br&gt;return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this); &lt;br&gt;&lt;/span&gt;看到了没有？&lt;span&gt;obj.getClass().getInterfaces()&lt;/span&gt;要求实现了某些接口。以下提供哪些没有实现接口的实现方案： &lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;二、子类的实现方案。&lt;span&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;首先，请上网下&lt;span&gt;CGLib&lt;/span&gt;的包，&lt;span&gt;&lt;a href="http://sourceforge.net/project/showfiles.php?group_id=56933"&gt;http://sourceforge.net/project/showfiles.php?group_id=56933&lt;/a&gt; &lt;/span&gt;。设置好&lt;span&gt;classpath&lt;/span&gt;路径，&lt;span&gt;CGLib&lt;/span&gt;与&lt;span&gt;java&lt;/span&gt;标准库提供的实现方案不同，&lt;span&gt;cglib&lt;/span&gt;主要是基于实现类（如 &lt;span&gt;StudentInfoServiceImpl.java)&lt;/span&gt;扩展一个子类来实现。与&lt;span&gt;Dynamic Proxy&lt;/span&gt;中的&lt;span&gt;Proxy&lt;/span&gt;和&lt;span&gt;InvocationHandler&lt;/span&gt;相对应，&lt;span&gt;net.sf.cglib.proxy.Enhancer&lt;/span&gt;和&lt;span&gt; MethodInterceptor&lt;/span&gt;在&lt;span&gt;CGLib&lt;/span&gt;中负责完成代理对象创建和方法截获处理&lt;span&gt;,&lt;/span&gt;产生的是目标类的子类而不是通过接口来实现方法拦截的，&lt;span&gt; Enhancer&lt;/span&gt;主要是用于构造动态代理子类来实现拦截，&lt;span&gt;MethodInterceptor&lt;/span&gt;（扩展了&lt;span&gt;Callback&lt;/span&gt;接口）主要用于实现&lt;span&gt;around advice&lt;/span&gt;（&lt;span&gt;AOP&lt;/span&gt;中的概念）：&lt;span&gt; &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 1&lt;/span&gt;）我们的业务处理（&lt;span&gt;StudentInfoServiceImpl.java&lt;/span&gt;）：&lt;span&gt; &lt;br&gt;public class StudentInfoServiceImpl{ &lt;br&gt;&amp;nbsp;public void findInfo(String name){ &lt;br&gt;&amp;nbsp; System.out.println("&lt;/span&gt;你目前输入的名字是&lt;span&gt;:"+name); &lt;br&gt;&amp;nbsp;} &lt;br&gt;} &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 2&lt;/span&gt;）实行一个工具来处理日志功能（&lt;span&gt;AOPInstrumenter.java&lt;/span&gt;）：&lt;span&gt; &lt;br&gt;import net.sf.cglib.proxy.MethodInterceptor; &lt;br&gt;import net.sf.cglib.proxy.Enhancer; &lt;br&gt;import net.sf.cglib.proxy.MethodProxy; &lt;br&gt;import java.lang.reflect.Method; &lt;br&gt;import org.apache.log4j.Logger; &lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;public class AOPInstrumenter implements MethodInterceptor{ &lt;br&gt;&amp;nbsp;private Logger log=Logger.getLogger(AOPInstrumenter.class); &lt;br&gt;&amp;nbsp;private Enhancer enhancer=new Enhancer(); &lt;br&gt;&amp;nbsp; &lt;br&gt;&amp;nbsp;public Object getInstrumentedClass(Class clz){ &lt;br&gt;&amp;nbsp; enhancer.setSuperclass(clz); &lt;br&gt;&amp;nbsp; enhancer.setCallback(this); &lt;br&gt;&amp;nbsp; return enhancer.create(); &lt;br&gt;&amp;nbsp;} &lt;br&gt;&amp;nbsp; &lt;br&gt;&amp;nbsp;public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy) throws Throwable{ &lt;br&gt;&amp;nbsp; log.info("&lt;/span&gt;&lt;span&gt;调用日志方法&lt;span&gt;"+method.getName()); &lt;br&gt;&amp;nbsp; Object result=proxy.invokeSuper(o,args); &lt;br&gt;&amp;nbsp; return result; &lt;br&gt;&amp;nbsp;} &lt;br&gt;&amp;nbsp; &lt;br&gt;} &lt;br&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; 3&lt;/span&gt;）我们来测试一下（&lt;span&gt;AOPTest.java&lt;/span&gt;）：&lt;span&gt; &lt;br&gt;public class AOPTest{ &lt;br&gt;&amp;nbsp;public static void main(String[] args){ &lt;br&gt;&amp;nbsp; AOPInstrumenter instrumenter=new AOPInstrumenter(); &lt;br&gt;&amp;nbsp; StudentInfoServiceImpl studentInfo=(StudentInfoServiceImpl)instrumenter.getInstrumentedClass(StudentInfoServiceImpl.class); &lt;br&gt;&amp;nbsp; studentInfo.findInfo("&lt;/span&gt;阿飞&lt;span&gt;"); &lt;br&gt;&amp;nbsp;} &lt;br&gt;} &lt;br&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;输出结果与以上相同。&lt;span&gt; &lt;br&gt;&amp;nbsp;CGLib&lt;/span&gt;中为实现以上目的，主要提供的类&lt;span&gt; &lt;br&gt;1)Enhancer&lt;/span&gt;：&lt;span&gt;setCallback(Callback) ,setSuperclass(Class) ,create()&lt;/span&gt;返回动态子类&lt;span&gt;Object &lt;br&gt;2)MethodInterceptor&lt;/span&gt;必须实现的接口：&lt;span&gt;intercept(Object,Method,Object[],MethodProxy)&lt;/span&gt;返回的是原方法调用的结果。和&lt;span&gt;Proxy&lt;/span&gt;原理一样。 &lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;三、以上的两个简单实现&lt;span&gt;AOP&lt;/span&gt;的方案都为你准备好了，你可以自己编写测试一下，以下简单介绍一下&lt;span&gt;AOP&lt;/span&gt;的基本概念：&lt;span&gt; &lt;br&gt;1&lt;/span&gt;）&lt;span&gt;aspect&lt;/span&gt;（切面）：实现了&lt;span&gt;cross-cutting&lt;/span&gt;功能，是针对切面的模块。最常见的是&lt;span&gt;logging&lt;/span&gt;模块，这样，程序按功能被分为好几层，如果按传统的继承的话，商业模型继承日志模块的话根本没有什么意义，而通过创建一个&lt;span&gt;logging&lt;/span&gt;切面就可以使用&lt;span&gt;AOP&lt;/span&gt;来实现相同的功能了。&lt;span&gt; &lt;br&gt;2&lt;/span&gt;）&lt;span&gt;jointpoint&lt;/span&gt;（连接点）：连接点是切面插入应用程序的地方，该点能被方法调用，而且也会被抛出意外。连接点是应用程序提供给切面插入的地方，可以添加新的方法。比如以上我们的切点可以认为是&lt;span&gt;findInfo(String)&lt;/span&gt;方法。&lt;span&gt; &lt;br&gt;3&lt;/span&gt;）&lt;span&gt; advice&lt;/span&gt;（处理逻辑）：&lt;span&gt;advice&lt;/span&gt;是我们切面功能的实现，它通知程序新的行为。如在&lt;span&gt;logging&lt;/span&gt;里，&lt;span&gt;logging advice&lt;/span&gt;包括&lt;span&gt;logging&lt;/span&gt;的实现代码，比如像写日志到一个文件中。&lt;span&gt;advice&lt;/span&gt;在&lt;span&gt;jointpoint&lt;/span&gt;处插入到应用程序中。以上我们在&lt;span&gt; MyHandler.java&lt;/span&gt;中实现了&lt;span&gt;advice&lt;/span&gt;的功能&lt;span&gt; &lt;br&gt;4&lt;/span&gt;）&lt;span&gt;pointcut&lt;/span&gt;（切点）：&lt;span&gt;pointcut&lt;/span&gt;可以控制你把哪些&lt;span&gt;advice&lt;/span&gt;应用于&lt;span&gt;jointpoint&lt;/span&gt;上去，通常你使用&lt;span&gt;pointcuts&lt;/span&gt;通过正则表达式来把明显的名字和模式进行匹配应用。决定了那个&lt;span&gt;jointpoint&lt;/span&gt;会获得通知。&lt;span&gt; &lt;br&gt;5&lt;/span&gt;）&lt;span&gt;introduction&lt;/span&gt;：允许添加新的方法和属性到类中。&lt;span&gt; &lt;br&gt;6&lt;/span&gt;）&lt;span&gt;target&lt;/span&gt;（目标类）：是指那些将使用&lt;span&gt;advice&lt;/span&gt;的类，一般是指独立的那些商务模型。比如以上的&lt;span&gt;StudentInfoServiceImpl. &lt;/span&gt;&lt;/p&gt;
&lt;p align=left&gt;&lt;span&gt;7)proxy&lt;/span&gt;&lt;span&gt;（代理类）：使用了&lt;span&gt;proxy&lt;/span&gt;的模式。是指应用了&lt;span&gt;advice&lt;/span&gt;的对象，看起来和&lt;span&gt;target&lt;/span&gt;对象很相似。&lt;span&gt; &lt;br&gt;8&lt;/span&gt;）&lt;span&gt;weaving(&lt;/span&gt;插入）：是指应用&lt;span&gt;aspects&lt;/span&gt;到一个&lt;span&gt;target&lt;/span&gt;对象创建&lt;span&gt;proxy&lt;/span&gt;对象的过程：&lt;span&gt;complie time&lt;/span&gt;，&lt;span&gt;classload time&lt;/span&gt;，&lt;span&gt;runtime &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;/p&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;v&lt;img src="http://dev.kingdee.com/aggbug.aspx?PostID=239787" width="1" height="1"&gt;</description></item><item><title>金蝶BOS发展远景（概述）</title><link>http://dev.kingdee.com/pages/hjm/blog/archive/2007/08/07/226896.aspx</link><pubDate>Tue, 07 Aug 2007 08:29:00 GMT</pubDate><guid isPermaLink="false">d85b68fd-db4c-4672-8cdd-89d3217b2e06:226896</guid><dc:creator>hujinmin</dc:creator><slash:comments>0</slash:comments><comments>http://dev.kingdee.com/pages/hjm/blog/comments/226896.aspx</comments><wfw:commentRss>http://dev.kingdee.com/pages/hjm/blog/commentrss.aspx?PostID=226896</wfw:commentRss><description>&lt;p&gt;&amp;nbsp; &lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span style="FONT-FAMILY: 宋体"&gt;20&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体"&gt;世纪著名建筑设计大师、家具设计大师和城市规划大师小沙里宁有一条非常经典的设计理念：&lt;span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span style="FONT-FAMILY: 宋体"&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;b&gt;&lt;i&gt;&lt;span&gt;&amp;#8220;Always design a thing by considering it in its next larger context - &lt;span style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;a chair in a room, &lt;span style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;a room in a house, &lt;span style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;/span&gt;a house in an environment, &lt;span style="mso-tab-count: 1"&gt;&amp;nbsp;&amp;nbsp; &lt;/span&gt;an environment in a city plan.&lt;/span&gt;&lt;/i&gt;&lt;/b&gt;&lt;b&gt;&lt;i&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;&amp;#8221;&lt;/span&gt; &lt;/i&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;i&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-weight: bold"&gt;（译：&lt;/span&gt;&lt;span style="mso-bidi-font-weight: bold"&gt;&amp;#8220;&lt;/span&gt;&lt;/i&gt;&lt;i&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-weight: bold"&gt;通常设计一样东西需要把它置于它所属的更大的环境中&lt;/span&gt;&lt;span style="mso-bidi-font-weight: bold"&gt;——&lt;/span&gt;&lt;/i&gt;&lt;i&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-weight: bold"&gt;就像将椅子置于一个房间中；将房间置于一栋房子中；将一栋房子置于周围的环境中；将周围的环境置于一个城市的规划中。&lt;/span&gt;&lt;span style="mso-bidi-font-weight: bold"&gt;&amp;#8221;&lt;/span&gt;&lt;/i&gt;&lt;i&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'; mso-bidi-font-weight: bold"&gt;）&lt;/span&gt;&lt;span style="mso-bidi-font-weight: bold"&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/i&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%; TEXT-ALIGN: right" align=right&gt;&lt;b style="mso-bidi-font-weight: normal"&gt;&lt;span&gt;- Eero Saarinen&lt;/span&gt;&lt;/b&gt;&lt;b style="mso-bidi-font-weight: normal"&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;（小沙里宁）&lt;/span&gt;&lt;span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/span&gt;&lt;/b&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span&gt;&lt;o:p&gt;&amp;nbsp;&lt;/o:p&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;的设计也遵循此条设计理念。金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;是企业业务应用的设计、开发、运行和管理环境。&amp;#8220;企业业务&amp;#8221;是金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;设计时考虑的&amp;#8220;更大的环境&amp;#8221;。&lt;/span&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;在当今激烈市场竞争环境下，企业为了求得生存，必须具有在无法预测的、持续快速变化的竞争环境中提高自身的敏捷化竞争的能力。企业信息化作为提高企业竞争能力最重要技术手段之一必须能够支持企业针对复杂变化做出快速响应。&lt;/span&gt; &lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;因此，作为支持企业应用系统开发和运行平台——金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;，必须具备满足客户业务变化的需求。总体上说，企业对变化的响应可以归结为企业组织结构的变革或调整以及业务处理逻辑的变化。企业组织机构及业务流程的持续优化是这种变化的最直接的需求。&lt;/span&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;基于&lt;/span&gt;&lt;span&gt;SOA&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;架构和采用模型驱动架构&lt;/span&gt;&lt;span&gt;MDA&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;方法体系构建的金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;目前已经能较好支持企业系统对企业内部组织变革和企业内部流程优化。同时，金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;作为一个平台和工具，它也是金蝶公司的合作伙伴、行业开发伙伴、客户的增值应用开发伙伴的利器。围绕金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;可以形成以满足企业各种变化需求为目标的企业应用系统生态链。&lt;/span&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span&gt;SOA&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;的进一步发展，必将为构建应用软件合作生态链提供更好的机遇和方法。服务本身具备的良好封装性、可重用和可组合性、可互操作性、自治性和松散耦合等特点使得业务服务的供应商、服务开发商、增值开发商、服务整合商（类似现在的集成商）、服务运营商（类似中国电信）、服务消费者等能够基于服务形成合作生态链。随着&lt;/span&gt;&lt;span&gt;SOA&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;深入人心、&lt;/span&gt;&lt;span&gt;SOA&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;标准的发展、&lt;/span&gt;&lt;span&gt;SOA&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;实现技术的发展，信息技术将能够使&lt;b style="mso-bidi-font-weight: normal"&gt;业务实现服务化、服务实现组件化&lt;/b&gt;。目前，&lt;/span&gt;&lt;span&gt;SCA/SDO&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;等相关标准和实现技术已经在这一目标上取得了很大的进步，这又被称为下一代&lt;/span&gt;&lt;span&gt;SOA&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;技术。&lt;/span&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;金蝶作为企业管理软件的服务提供商，管理软件开发平台提供商将致力提供企业管理标准服务、企业管理服务开发平台、企业管理服务运营平台，从而构建基于企业管理软件的企业合作生态链。金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;将通过提供标准的企业应用服务库来大大加速合作伙伴和最终用户开发新服务新应用系统的效率；金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;将通过提供服务整合工具和平台来融合更多的行业解决方案服务和增值服务来为最终客户提供综合服务方案；金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;运行平台将发展成为企业管理服务运营平台给更多地企业提供企业管理服务，即提供&lt;/span&gt;&lt;span&gt;SaaS(Software as a Service&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;）模式，也可以给大型企业集团作为集团内部服务管理和运营。近&lt;/span&gt;&lt;span&gt;30&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;年来的经济发展证明，信息技术不仅有效地支持了企业业务的运营，同时更是催生新的业务模型（&lt;/span&gt;&lt;span&gt;Business Model&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;）的主要动力，大量成功的互连网公司、电子商务公司是最好的例证。我们相信，目前的热点技术——&lt;/span&gt;&lt;span&gt;SOA&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;技术也将会给企业带来在&amp;#8220;业务模型&amp;#8221;层面的创新与变革，&lt;/span&gt;&lt;span&gt; SaaS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;就是一个典型的新&lt;/span&gt;&lt;span&gt;&amp;#8221;&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;业务模型&lt;/span&gt;&lt;span&gt;&amp;#8221;&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;。&lt;/span&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;通过对企业管理需求的分析和预测，金蝶还认识到构建产业生态链（或企业联盟）是大多数企业发展和提升竞争的要求。特别对于中国企业，因为&lt;/span&gt;&lt;span&gt;99%&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;的企业都是所谓的&lt;/span&gt;&lt;span&gt;SMB(&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;中小企业&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;，他们资源有限，要应对全球&amp;#8220;变化&amp;#8221;这一主题的竞争，必须和其他企业实现高效的业务协同。因此，跨企业协同、企业群协同将是未来&amp;#8220;企业业务&amp;#8221;这一大环境的主要表现形式。&lt;/span&gt;&lt;/p&gt;
&lt;p class=MsoNormal style="TEXT-INDENT: 21pt; LINE-HEIGHT: 150%"&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;基于以上分析，金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;将采用新一代的&lt;/span&gt;&lt;span&gt;SOA&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;技术，构建基于服务的管理软件生态链、构建企业管理服务运营平台、支持构建可自组织的基于服务的企业协同网络。这就是金蝶&lt;/span&gt;&lt;span&gt;BOS&lt;/span&gt;&lt;span style="FONT-FAMILY: 宋体; mso-ascii-font-family: 'Times New Roman'; mso-hansi-font-family: 'Times New Roman'"&gt;的发展远景。&lt;/span&gt;&lt;/p&gt;&lt;img src="http://dev.kingdee.com/aggbug.aspx?PostID=226896" width="1" height="1"&gt;</description></item></channel></rss>