﻿<?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-可以调素琴,阅金经,无丝竹之乱耳,无案垛之劳形-文章分类-java基础</title><link>http://www.blogjava.net/aojilee/category/5108.html</link><description /><language>zh-cn</language><lastBuildDate>Fri, 02 Mar 2007 06:52:08 GMT</lastBuildDate><pubDate>Fri, 02 Mar 2007 06:52:08 GMT</pubDate><ttl>60</ttl><item><title>利用反射机制实现XML-RPC</title><link>http://www.blogjava.net/aojilee/articles/47763.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Wed, 24 May 2006 02:08:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/47763.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/47763.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/47763.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/47763.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/47763.html</trackback:ping><description><![CDATA[
		<span style="COLOR: red">
				<span style="FONT-SIZE: 16px">动态回调基于XML的远程过程调用</span>
		</span>
		<br />
		<br />
		<b>
				<span style="FONT-SIZE: 16px">摘要</span>
		</b>
		<br />
		<br />Java 反射机制为使用XML-RPC（XML-based Remote Procedure Call,基于XML的远程过程调用）远程过程调用提供了一种简便又高效的实现方法，这种方法隐蔽掉了一些远程过程调用过程中的复杂操作。在这篇文章里， Stephan Maier展示给你如何从反射包中使用一些类去包装XML-RPC去调用远程接口：Proxy类，Array类, 和BeanInfo类。这篇文章也将要讨论这种方法的多重实现和在RMI（Remote Method Invocation，远程方法调用）中可反射方法的使用。<br /><br />因为它作为远程方法调用的一个简单协议，人们偶尔关注基于XML的远程过程调用（XML-RPC）。它易于使用，又着可以运用的实现-Apache XML-RPC。<br /><br />如果是一个小应用程序或应用程序中使用有限数量的远程过程，那么不应该趋向于正式地定义远程过程的名字和方法声明，取而代之，应该直接使用XML-RPC。甚至，应用程序规模增大和远程接口数量增加，将可发现对远程方法和数据对象必要的约定必定是一种莫名的一致。在这篇文章里，将展示Java提供的所有需要去实现的定义远程接口和访问远程方法：过程，使用Java接口定义的过程签名，和使用XML-RPC的远程过程调用-这种调用把一个通信信道的两侧封装成仅仅是接口和相对的数据对象。<br /><br />本文也展示当给定的描述远程过程和数据的Java接口构建与 JavaBean规范一致，可以使用Java反射机制，把反射机制和JavaBean整合使用可以透明地调用远程方法，轻松地在XML-RPC数据类型和Java数据类型之间进行转换。<br /><br /><br />实际中隐藏复杂性是个好的事情。无需说，不是所有的复杂性能或者应该被隐藏。针对分布式计算，这个观点已经在“分布式计算笔记”有了个著名的论著。（Sun Microsystems，1994.11）。本文中展现的框架不特意去隐藏分布式计算的复杂性，但这个框架帮助使用者减少在调用远程过程时的繁琐。简单点说，本文只讨论并发远程过程调用，热心的读者可以自己去研究觳皆冻坦痰饔谩?<br /><br />XML-RPC可以被看作RPC跨SOAP协议的一个简化。扩展点说，本文所讨论的这个简易框架必须被认为SOAP引擎的简化版本，就像Axis。本文主要以教学位目标：希望展示在目前的XML-RPC框架上层是怎样通过反射来建立一个简化的XML-RPC引擎。这些帮助读者理解相似且更复杂的其他协议的引擎的内部实现机理，或怎样应用反射去解决复杂问题。一个RPC引擎必须在SOAP引擎可实现的环境中使用，就好比中间件控件不可用，那么应用程序就不能通过Web服务器发布给广大用户。Roy Miller’s “XML-RPC Java 编程”对这个作了很好的解释。<br /><br />在本文中，使用XML-RPC的Apache组件去安装这个框架。读者不需奥区知道XML-RPC，也不需要理解Apache XML-RPC框架，就是只有一个基本的了解都能使你理解下面的讲述。本文重点放在框架内部精确的运作机理，但不涉及协议的细节。<br /><br /><span style="COLOR: red">版权声明：任何获得Matrix授权的网站，转载时请务必保留以下作者信息和链接</span><br />作者:<a href="http://www.matrix.org.cn/user.shtml?username=steven_guo" target="_new">steven_guo</a>(作者的blog:<a href="http://blog.matrix.org.cn/page/steven_guo" target="_new">http://blog.matrix.org.cn/page/steven_guo</a>)<br />原文:<a href="http://www.matrix.org.cn/resource/article/44/44437_XML-RPC.html" target="_new">http://www.matrix.org.cn/resource/article/44/44437_XML-RPC.html</a><br />关键字:XML-RPC;Reflective<br /><br /><b><span style="FONT-SIZE: 16px">消除习惯</span></b><br /><br />有时候，我更喜欢非传统的编程。谈到这点，我必须使你确信，我不是一个总打破旧习俗的人，我不反对好的编程习惯；恰恰与此完全相反。“非传统”在这里主要表明我喜欢去避免程序中到处的定义字符串，而且这些代码可以在可编程的API里定义。考虑下面代码片断。<br /><br />代码1. 调用远程过程<br /><pre class="overflow">Vector paras = new Vector();<br />paras.add("Herbert");<br />Object result = client.execute("app.PersonHome.getName", paras); </pre><br /><br />代码1展示了通过Apzche XML-RPC的实现如何调用远程过程。可以注意到，使用者需要去指导远程过程和传递到远程方法中参数的名字。也必须知道远程过程返回的对象类型。除非你已经实现了一个类去验证你使用的这些名字（app.PersonHome 和 getName）是否正确，那么你局需要去查找这些名字和声明，通常这些信息保存在文本文件或常量接口中（一个接口提供所有参数名字）。也可能放置到 Javadoc中适当的地方。大家可以发现，这种约定有造成运行时错误的隐患，因为这些错误只在运行时才能显现出来，而在编译时不能显现。<br /><br />现在，对比考虑下面一段代码：<br /><br />代码2调用远程过程<br /><pre class="overflow">Person person = ((PersonHome)Invocator.getProxy(PersonHome.class)).getPerson("Herbert");</pre><br /><br />在这，我们调用Invocator类的静态方法getProxy()去检索PersonHome接口的实现。在这个接口里，可以调用getPerson()，得到Person对象。<br /><br />代码2相对于代码1是一个较简洁的方式。在代码2种，使用简洁的定义在接口中的方法，在接口里可以把可用的方法，方法声明，返回类型都一起定义。因为不需要强制转换对象，所以也是类型安全。因为不需要额外的像Vector类的构造函数，代码可读性也较好。<br /><br />而且，如果你使用了一个功能强大的IDE，代码助手将罗列所有可用的方法和他们的实现。因此，在类型安全远程方法调用上从IDE获得支持。<br /><br />我必须承认，没有规范我们不能作任何事。我们必须坚持（除非我们准备接受高昂的成本和复杂化）的一个规则就是：假设所有的数据对象都遵从JavaBean规范。简单地说，对象的属性操作必须通过getter/setter方法。在我们讨论把XML-RPC数据结构转换成Java对象时，这个假设的重要性将显现出来。<br /><br />把所有数据对象遵从JavaBean规约是比在XML-RPC应用程序中的规约更高级一些，因为后者是一个通用规范。它也是所有Java 程序员的的通用规范。在本文结尾部分，我讨论XML-RPC的限制，还要建议其他一些有用的规范，而且可以更好的让你理解那些限制。<br /><br />随后的部分，一起浏览Invocator类的实现和一个提供框架通信信道的一个本地服务器。<br /><br /><b><span style="FONT-SIZE: 16px">实现调用</span></b><br /><br />让我们首先看看提供一个接口实现的方法。<br /><br />代码3  创建代理<br /><pre class="overflow">public static Object getProxy(Class ifType) {<br />   if (!ifType.isInterface()) {<br />      throw new AssertionError("Type must be an interface");<br />   }<br />   return Proxy.newProxyInstance(Invocator.class.getClassLoader(),<br />      new Class[]{ifType}, new XMLRPCInvocationHandler(ifType));<br />} </pre><br /><br />所有的实现逻辑隐藏在了Proxy.newProxyInstance()方法中。Proxy类从Java1.3开始已经是Java Reflection包的一部分。经由newProxyInstance()方法，一些列的接口可以自动被实现。当然，一个proxy类不知道怎样处理方法调用。因此，它必须把调用传送给一个合适的处理器 – 一个实现 java.lang.reflect.InvocationHandler类的作业中。在这里，我已经选择取调用这个实现类 XMLRPCInvocationHandler。InvocationHandler接口定义单一的方法，如代码4所示。<br /><br />代码4 InvocationHandler<br /><pre class="overflow">public interface InvocationHandler {<br />   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;<br />} </pre><br /><br />当一个proxy实例调用了一个方法，proxy实例传递这个方法和他的参数叨处理器类的invoke（）方法，同时要识别它。让我们看看处理器的实现：<br /><br />代码5 InvocationHandler<br /><pre class="overflow">private static class XMLRPCInvocationHandler implements InvocationHandler {<br /><br />   private Class type;<br /><br />   public XMLRPCInvocationHandler(Class ifType) {<br />      this.type = ifType;<br />   }<br /><br />   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {<br /><br />      XmlRpcClient client = getClient(); // Get a reference to the client<br />      Vector paras = null; // This will hold the list of parameters<br />      if (args != null){<br />         paras = new Vector();<br />         for (int i = 0; i &lt; args.length; i++) {<br />               paras.add(ValueConverter.convertFromType(args[i]));<br />            }<br />         }<br />         else{<br />            paras = new Vector(); // The vector holding the parameters must not be null<br />         }<br />         Class retType = method.getReturnType();<br />         Object ret = client.execute(type.getName() + '.' + method.getName(), paras);<br />         return ValueConverter.convertToType(ret, retType);<br />   }<br />}</pre><br /><br />在创建过程中，XMLRPCInvocationHandler就是有远程接口的实现类。我们使用这个类仅仅是为了获得远程接口的名字和可供调用的方法名。可以观察到远程方法的调用总是动态的：我们既不需要在桩类里调用方法，也不需要从外部获得任何接口的信息。<br /><br />Client类从getClient()方法获得:<br /><br />代码6. 获得client类<br /><pre class="overflow">protected static XmlRpcClient getClient() throws MalformedURLException {<br />   return new XmlRpcClient("localhost", 8080);<br />} </pre><br /><br />这里，我们能使用Apache XML-RPC去获得处理远程调用的Client类。可以看到我们返回一个Client类而没有考虑我们所有调用的方法的接口。<br /><br />更重要的代码是在ValueConverter类中用静态方法调用。在那个方法里反射提供了这种方式的魅力所在。在下面部分我们分析这些代码：<br /><br /><b><span style="FONT-SIZE: 16px">XML-RPC 和Java之间的转换</span></b><br /><br />这部分解释XML-RPC架构的核心。这个框架需要作两件事：转换Java对对象成能被XML-RPC所理解的数据结构和一个反向的转换处理。<br /><br />我开始展示如何把一个Java对象转换为能被XML-RPC理解的数据结构：<br /><br />代码7. Java 到 XML-RPC转换<br /><pre class="overflow">public static Object convertFromType(Object obj) throws IllegalArgumentException, <br />      IllegalAccessException, InvocationTargetException, IntrospectionException {<br />   if (obj == null) {<br />      return null;<br /><br />   }<br />   Class type = obj.getClass();<br />   if (type.equals(Integer.class)<br />      || type.equals(Double.class)<br />      || type.equals(Boolean.class)<br />      || type.equals(String.class)<br />      || type.equals(Date.class)) {<br />      return obj;<br />   else if (type.isArray() &amp;&amp; type.getComponentType().equals(byte.class)) {<br />      return obj;<br />   }<br />   else if (type.isArray()) {<br />      int length = Array.getLength(obj);<br />      Vector res = new Vector();<br />      for (int i = 0; i &lt; length; i++) {<br />         res.add(convertFromType(Array.get(obj, i)));<br />      }<br />      return res;<br />   }<br />   else {<br />      Hashtable res = new Hashtable();<br />      BeanInfo info = Introspector.getBeanInfo(type, Object.class);<br />      PropertyDescriptor[] props = info.getPropertyDescriptors();<br />      for (int i = 0; i &lt; props.length; i++) {<br />         String propName = props[i].getName();<br />         Object value = null;<br />         value = convertFromType(props[i].getReadMethod().invoke(obj, null));<br />         if (value != null) res.put(propName, value);<br />      }<br />      return res;<br />   }<br />}</pre><br /><br />转换Java对对象成能被XML-RPC所理解的数据结构，需要考虑如上面代码展示的5种情况：<br />1. Null：如果要转换的对象是Null值，我们必须返回一个Null。<br />2. 原始类型：如果要转换的对象是原始类型（或这些原始类的包装类）-- int、double、Boolean、string或date， 那么就返回该对象即可。<br />3. base64：如果对象是一个字节数组，那么可以认为这个数组是base64类型数据。只需把数组直接返回即可。<br />4. Array：如果对象不是一个字节数组，我们能从Java Reflection包中使用Array工具类获得数组长度。使用这个长度值，在一个循环中读数组的每个数据单元。把每个数据单元的数据传入ValueConverter封装进入一个Vector中。<br />5．复杂类型：如果对象不是上面所叙述的类型，我们可以假定它是一个JavaBean – 在程序开始我们可以设立这么一个共同遵守的假定原则。我们把属性插入HashTable内。要访问这些属性，可以使用JavaBean框架自举的机制：使用工具类Introspector去获得封装在BeanInfo对象里的信息。特别，我们可以循环访问PropertyDescriptor对象数组获得 Bean的属性。从这样一个属性描述器里，可以检索到属性的名字，这些名字也是访问HashTable的键。我们通过读属性描述器获得这些键值，例如属性值。<br /><br />看到如此容易地使用JavaBean框架从Bean里提取信息。我不需要知道所想转换的类型，只需知道这个Bean即可。这个假设是我们的框架完美地运行其俩的先决条件。<br /><br />现在，让我们来看看相反的转换 – 把XML-RPC结构数据转换为Java对象：<br /><br />代码8. 从XML-RPC转换到Java对象<br /><pre class="overflow">public static Object convertToType(Object object, Class type) throws IllegalArgumentException, <br />      IllegalAccessException, InvocationTargetException, IntrospectionException, InstantiationException {<br />   if (type.equals(int.class)<br />      || type.equals(double.class)<br />      || type.equals(boolean.class)<br />      || type.equals(String.class)<br />      || type.equals(Date.class)) {<br />      return object;<br />   }<br />   else if (type.isArray() &amp;&amp; type.getComponentType().equals(byte.class)) {<br />      return object;<br />   }<br />   else if (type.isArray()) {<br />      int length = ((Vector) object).size();<br />      Class compType = type.getComponentType();<br />      Object res = Array.newInstance(compType, length);<br />      for (int i = 0; i &lt; length; i++) {<br />         Object value = ((Vector) object).get(i);<br />         Array.set(res, i, convertToType(value, compType));<br />      }<br />      return res;<br />   }<br />   else {<br />      Object res = type.newInstance();<br />      BeanInfo info = Introspector.getBeanInfo(type, Object.class);<br />      PropertyDescriptor[] props = info.getPropertyDescriptors();<br />      for (int i = 0; i &lt; props.length; i++) {<br />         String propName = props[i].getName();<br />         if (((Hashtable) object).containsKey(propName)) {<br />            Class propType = props[i].getPropertyType();<br />            props[i].getWriteMethod().<br />               invoke(res, new Object[]<br />                  { convertToType(((Hashtable) object).get(propName), propType)});<br />         }<br />      }<br />      return res;<br />   }<br /><br /><br />}</pre><br /><br />转换成一个Java类型需要更多的了解这个我们所要转换的值，我们也必须了解那个值要转换到这个Java类型。这个解释了在代码8中 convertToType()方法的第二个参数的存在性。知道了类型，我们使用Java的自举机制，把XML-RPC数据类型转换成Java类型。下面的列表展示了完成多种转换的约定：<br />1．Null：XML-RPC不传递空值，这个限制在稍后又更详细说明。我们不需要考虑这种情况。<br />2．原始类型：如果对象是原始类型（或原始类型的包装类）- int, double, Boolean, string, 或 date ，那么我们可以把对象本社返回，作为XML-RPC可以识别的原始类型。<br />3．base64：如果对象是一个二进制数组，可以认为代表一个base64类型的实例。我们可以再次把数组本书返回。<br />4．数组：如果对象是一个数组，但不是一个二进制数组，我们首先要在数组类确定所存项目的类型。我们可以根据对象断定类型。可以使用 getComponentType（）。下一步，我们使用Array工具类基于给定的组件类型创建一个新的数组。我们可以使用Array工具类循环遍历数组，设置各个域，使用ValueConveter从每个数组项中获得正确的值。在XML-RPC框架中，可以发现我们所期望的数组数据结构是一个 Vector。<br />5.复杂类型：如果一个对象不是上面所述的类型，我们假设它是一个JavaBean（依照我们的基本约定）。再次，我们使用 Introspector查找Bean的属性描述器，使用属性描述器设置实际的属性通过访问write（）方法。需要注意的是，框架给了我们一个储存在 HashTable的属性。当然，属性类型可能是复杂类型，我们必须使用ValueConverter去获得正确的Java对象。<br /><br />理解了数据转换的约定，我们可以看看处理服务是如何实现的。<br /><br /><b><span style="FONT-SIZE: 16px">实现服务处理</span></b><br /><br />已经解释了一个远程服务是如何被调用和在XML-RPC和Java对象之间转换时包含什么。我了解了所迷惑不解的问题的剩下部分：在一个服务端如何处理请求<br /><br />这里是一个为这篇文章所实现简单的服务器实现代码：<br /><br />代码9. 服务器<br /><pre class="overflow">public class Server {<br />   private WebServer webserver = null;<br /><br />   public void start() {<br />      webserver = new WebServer(8080);<br />      webserver.addHandler<br />          (PersonHome.class.getName(),<br />         new Handler(PersonHome.class,<br />         new PersonHomeImpl()));<br />      webserver.setParanoid(false);<br />      webserver.start();<br />   }<br /><br />   public void stop() {<br />         webserver.shutdown();<br />         webserver = null;<br />   }<br /><br />   private static class Handler implements XmlRpcHandler {<br />      private Object instance;<br />      private Class type;<br /><br />      public Handler(Class ifType, Object impl) {<br />         if (!ifType.isInterface()) {<br />            throw new AssertionError("Type must be an interface");<br />         }<br />         if (!ifType.isAssignableFrom(impl.getClass())) {<br />            throw new AssertionError("Handler must implement interface");<br />         }<br />         this.type = ifType;<br />         this.instance = impl;<br />      }<br /><br />      public Object execute(String method, Vector arguments) throws Exception {<br />         String mName = method.substring(method.lastIndexOf('.') + 1);<br />         Method[] methods = type.getMethods();<br />         for (int i = 0; i &lt; methods.length; i++) {<br />            if (methods[i].getName().equals(mName)){<br />               try {<br />                  Object[] args = new Object[arguments.size()];<br />                  for (int j = 0; j &lt; args.length; j++) {<br />                     args[j] = ValueConverter.convertToType<br />                         (arguments.get(j), methods[i].getParameterTypes()[j]);<br /><br />                  }<br />                  return ValueConverter.convertFromType(methods[i].invoke(instance,args));<br />               }<br />               catch (Exception e) {<br />                  if (e.getCause() instanceof XmlRpcException){<br />                     throw (XmlRpcException)e.getCause();<br />                  }<br />                  else{<br />                     throw new XmlRpcException(-1, e.getMessage());<br />                  }<br />               }<br />            }<br />         }<br />         throw new NoSuchMethodException(mName);<br />      }<br />   }<br /><br />   public static void main(String[] args){<br />      Server server = new Server();<br />      System.out.println("Starting server...");<br />      server.start();<br />      try {<br />         Thread.sleep(30000);<br />      }<br />      catch (InterruptedException e) {<br />         e.printStackTrace();      <br />      }<br />      System.out.println("Stopping server...");<br />      server.stop();<br />   }<br />}</pre><br /><br />关键类是ApacheXML-RPC包中的WebServer类。黑体字代码展示了我们主要的需求：我们必须注册一个服务句柄。这个句柄经由 XmlRpcHandler接口定义，这个接口就像代理机制中InvocationHandler接口，有个方法对应方法调用的委派。在这里，叫做 execute（）方法，有着和InvocationHandler接口相同的实现精髓。最大的不同是，我们须注册联系接口和他的实现的一个句柄，不需要提供服务接口的实现（一桩程序形式）。然而，在服务器里，我们需要定义那块代码负责处理到来的请求。最后，可以看到，通过循环遍历接口方法调用服务方法，我们使用普通的方式去发现了争取的方法。这里，我们不依靠标准的自举JavaBean，因为服务方法不是仅仅有setter和getter方法。<br /><br /><b><span style="FONT-SIZE: 16px">后记</span></b><br /><br />在这个部分，主要讨论在先前讨论中所处想的以下问题。我看到XML-RPC协议和这个文章所述的框架的局限性，但我也考虑这些方式的一定先进性。<br /><br /><b>局限性</b><br />XML-RPC是一个简单协议，很明显它不能为代表面向对象系统特色的远程过程调用实现可编程API。特别地，这样一个API不支持以下的一些实现：<br />&amp;#8226;继承：XML-RPC没能携带充足的信息决定那种类型可以沿着继承的层级结构传递。在远程过程调用和对象传递参数中都存在这种情况。因此，申明所有的类为final类型是一个好的编程习惯<br />&amp; #8226;重载：XML-RPC不允许方法重载。依据这条规则，可以重载那些有原始类型声明的方法，但实际这个选择是不能满足的。当我们需要从方法的声明去推断结构类型是，我们不允许重载。我仅仅允许同一个方法有不同参数个数这种情况除向，因为所有的方法在远程过程调用期间是可用的。我么有以这个方式实现，而是使用了不同的方法名。注意：Web服务在这方面也不提供更多的灵活性。即使灵活性个那个靠的框架Axis也对重载有限制。<br />&amp;#8226; 集合：XML-RPC不允许结合类型出现。和重载相同的原因，我们必须从被给定的集合类型推断集合中项目的类型，这是不可能的。（JDK1.5之前版本）。取而代之，我们使用数组，可以查询组件类型。虽然，Web服务在远程方法调用方面比XML-RPC更强大，但更多的意见是反对使用集合类型。参看 “Web Services Programming Tips and Tricks: Use Collection Types with SOAP and JAX-RPC” Russell Butek and Richard Scheuerle, Jr（2002.4）<br />&amp; #8226;Null值：XML-RPC不支持空值。这可能是这个协议中最令人尴尬的瑕疵，因为这个意味着在数组里不能保存空值。在XML-RPC有过关于Null值得提议，但大多数的实现不支持Null值。无需去说，如果通信连接的两边均是与Java程序交互，通过手动方式在消息里加入一些元数据可以克服这个缺点。而且，这意味着滥用协议，不是一个好的建议。<br /><br /><b>序列化控制</b><br />序列化在下面的场景中出现。特别地，文中所提议的框架在发现属性去自动序列化。有时，你可以在序列化中阻止一些属性值得传递。<br /><br />设想一个Person对象引用多个不同类型的Address对象。特别地，这些Address对象之一是邮件地址，而其他对象在其他的上下文环境中有意义。你可能希望通过Person类的Person.getMailingAddress()方法可以获得邮件地址，这样增强你的Person类。标准的自举机制看到的是一个新的属性-mailingAddress，这个属性在序列化的时候可使用众多的地址列表中初始化。在这种情况下，一个对应的 Person.setMailingAddress()方法将执行这样的操作，不管地址序列化的顺序如何，反序列化将返回一个对应的Address类。当然，你的方法应该如何序列化是无关竟要的，但即使你写的方法是正确的，在其程序接口编程的有些人可能不清楚你想的是什么，增加了发生问题的可能性。在任何情况下，你应该容忍两次序列化邮件地址。<br /><br />但是，这里有个帮助，Introspector可能告诉你，要查找一个类的属性时要去使用反射，而要使用给定的信息。这些信息可以在BeanInfo类里找到，如果你的类名是MyClass，那么你的BeanInfo类应该叫做 MyClassBeanInfo。BeanInfo类因该在MyClass类的相同包里，或者在BeanInfo的搜索路径里。搜索路径可以在 Introspector里设置。作为一个BeanInfo类，应该提供如下的属性：<br /><br />代码10. BeanInfo 例子 1<br /><pre class="overflow">public class MyClassBeanInfo extends SimpleBeanInfo {<br />   public PropertyDescriptor[] getPropertyDescriptors() {<br />      try {<br /><br />         BeanInfo superInfo = Introspector.getBeanInfo(MyClass.class.getSuperclass());<br />         List list = new ArrayList();<br />         for (int i = 0; i &lt; superInfo.getPropertyDescriptors().length; i++) {<br />            list.add(superInfo.getPropertyDescriptors()[i]);<br />         }<br />         //<br />         list.add(new PropertyDescriptor("myProperty", MyClass.class));<br />         //<br />         return (PropertyDescriptor[])list.toArray(new PropertyDescriptor[list.size()]);<br />      } catch (IntrospectionException e) {<br />         return null;<br />      }<br />   }<br />}</pre><br /><br />getPropertyDescriptors()方法必须返回属性描述器所代表的属性。首先，在你的超类里增加这个属性，增加这个你希望发布的这个属性到你的类里，如粗体部分显示。<br /><br />这是一个严重的缺陷：上面的提议包含了很多固定代码，而这些是编程的时候应尽量避免的。正好，增加的这些被序列化的属性是比罗列显示这些属性能更好的运作。当然，一个方式是使用Introspector通过反射机制调用Introspector.getBeanInfo(MyClass.class, Introspector.IGNORE_ALL_BEANINFO)获得所有的属性。你能增加一个过滤器在你的返回结果时。这种方式看起来象如下展示：<br /><br />代码11. BeanInfo例子 2<br /><pre class="overflow">public class MyClassBeanInfo extends SimpleBeanInfo {<br />   public PropertyDescriptor[] getPropertyDescriptors() {<br />      try {<br />            BeanInfo infoByReflection = Introspector.getBeanInfo(MyClass.class, <br />            Introspector.IGNORE_ALL_BEANINFO); PropetyDescriptor allProperies = <br />            infoByReflection.getPropertyDescriptors();<br />            return filter(allProperies);<br />      } catch (IntrospectionException e) {<br />         return null;<br /><br />      }<br />   }<br /><br />   protected PropertyDescriptor[] filter(PropertyDescriptor[] props){<br />      // Remove properties which must not be exposed<br />   }<br />}</pre><br /><br />一个好的方法是使用接口定义语言（IDL）构造一个框架，这样允许你手动去产生Bean和扩阿占属性和方法。这个产生器负责提供通过IDL过滤属性的BeanInfo类。继续看一个这样实现的例子。<br /><br /><b>增加值</b><br />当我们隐藏了实际的传送机制，很容易在收到和发送的消息中增加信息。假若我们需要在每个远程方法调用中传送Session信息。这个信息就可以在调用者那里增加上，处理者把它作为第一个参数（包装所有必需的信息成一个适当德Bean）。在其他的调用里，这些信息能从参数Vector里删除，在方法调用里分别处理。在文后的资源引用中找到更多的可用代码，可以更好的使用这个框架。<br /><br /><b>其他语言</b><br />如果你正视弱点，它可能变成支点。XML-RPC的简易导致了上面所描述的限制。然而，XML-RPC已有了多种语言的实现，例如Ruby，Python，或函数性怨言 Haskell。不是所有的语言支持面向对象系统中所支持的继承，不是所有的语言支持重载。有些语言，例如Haskell，有灵活的列表类型，从Java 语言的角度看，它的这种类型介于数组和列表之间。因此，XML-RPC内在的限制使它适宜于跨语言通信。<br /><br />当选择XML-RPC作为跨越 Java和其他语言的桥梁时，你仍可以使用这个框架，但你仅仅能在与Java通信的一侧使用。而且，可以扩展这个框架去覆盖其他语言。例如，你可以用其他语言重写这个框架，增加对Java接口和数据对象与其他语言对应对象之间转换的支持。另外的方式，我已在上面暗示过，就是写一个编译器把IDL的适当形式转换到其他多种语言的形式，Java是其中之一。在下面，我给出一个例子。<br /><br />无需任何诉说，这种方式扩展文中所提的框架将是比框架更棘手的事，但它们将协同运行。<br /><br /><b>删除或替换XML-RPC实现</b><br />一个有效率的系统更愿意避免使用XML-RPC中间框架，反而通过把XML-RPC的XML数据直接转换成适当的对象。你可能认为，在潜藏在后面的接口里的抽象方法调用能用多种XML-RPC实现。当我认为不需去做什么事时，那就不能实现这些功能。再者，你是被吸引，而去适应这个框架，以满足你的需要。<br /><br /><b>远程方法调用</b><br />伴随着J2SE1.5，RMI将使用代理机制。将不再需要使用RMI编译器产生桩类（除非你要与一些旧的系统协作）。因此，如果不能夹在一个桩类，那么远程对象的桩就被认为是一个java.lang.reflect.Proxy实例。<br /><br /><b>接口定义语言</b><br />去处要查看大两Bean实现规约和XML-RPC限制的麻烦，正如上面所述，就是避免写接口和Bean。取而代之，适用适当的IDL去创建它们。这样的语言看起来就像下面的代码：<br /><br />代码12 . IDL<br /><pre class="overflow">module partner;<br /><br />exception NoPartnerException &lt; 123 : "No partner found" &gt;;<br /><br /><br />struct Partner {<br />   int id;<br />   string name;<br />   int age;<br />   date birthday;<br />};<br /><br />interface PartnerHome {<br />   Partner getPartner(int id) throws NoPartnerException;<br />   Partner[] findPartner(string name, date bday) throws NoPartnerException;<br />};</pre><br /><br />基于IDL编写一个解析器和代码产生器，使得交叉语言通信更加简易。<br /><br /><b><span style="FONT-SIZE: 16px">总结</span></b><br /><br />在这片文章里，展示了如何使用Java反射机制透明地包装经由XML-RPC实现远程方法调用。已经重点展示了已经整合在Proxy类，Array类和 Introspector类中的实现机制。基于这些工具类，一个可适用于多种用途的远程方法调用的中间件框架已经实构造出来。<br /><br /><b>关于作者</b><br />Stephan Maier 拥有数学博士学位，超过五年的软件开发经验。他也是一位职业生涯中艺术级的导师。除了编程，他喜欢唱歌和运动。现在，他在编写一个编译器，这个编译器可以简单地转换IDL定义到其他适当的数据结构和其他语言的远程接口，例如Java，Ruby，或Python，在这些接口里潜在的调用协议都是XML-RPC。<br /><br /><b>参考资料</b><br />Matrix:<a href="http://www.matrix.org.cn/" target="_new">http://www.matrix.org.cn</a><br />Javaworld:<a href="http://www.javaworld.com/" target="_new">http://www.Javaworld.com</a><br />"Web Services Programming Tips and Tricks: Roundtrip Issues in Java Coding Conventions," Russell Butek, Richard Scheuerle, Jr. (developerWorks, April 2004): <br />http://www-106.ibm.com/developerworks/xml/library/ws-tip-roundtrip2.html <br />"XML-RPC in Java Programming," Roy Miller (developerWorks, January 2004): <br />http://www-106.ibm.com/developerworks/library/j-xmlrpc.html <br />"Web Services Programming Tips and Tricks: Use Collection Types with SOAP and JAX-RPC" by Andre Tost and Tony Cowan (developerWorks, May 2004): <br />http://www-106.ibm.com/developerworks/library/ws-tip-coding.html?ca=dnx-420 <br />"A Note on Distributed Computing," Jim Waldo, Geoff Wyant, Ann Wollrath, and Sam Kendall (Sun 1994): <br />http://research.sun.com/techrep/1994/smli_tr-94-29.pdf <br />XML-RPC homepage: <br />http://www.xmlrpc.com <br />Apache XML-RPC implementation: <br />http://ws.apache.org/xmlrpc<img src ="http://www.blogjava.net/aojilee/aggbug/47763.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-05-24 10:08 <a href="http://www.blogjava.net/aojilee/articles/47763.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>JAVA反射(reflection)基本概念(翻译和总结)</title><link>http://www.blogjava.net/aojilee/articles/47733.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Tue, 23 May 2006 16:17:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/47733.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/47733.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/47733.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/47733.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/47733.html</trackback:ping><description><![CDATA[我们经常碰到一些问题，能够通过反射(reflection)简单而优雅的解决,如果没有反射,我们的解决办法将变得凌乱而脆弱。<img src ="http://www.blogjava.net/aojilee/aggbug/47733.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-05-24 00:17 <a href="http://www.blogjava.net/aojilee/articles/47733.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>什么是AspectJ </title><link>http://www.blogjava.net/aojilee/articles/46652.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Wed, 17 May 2006 09:00:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/46652.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/46652.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/46652.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/46652.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/46652.html</trackback:ping><description><![CDATA[
		<div class="postTitle">
				<a href="http://blog.csdn.net/joy_jiang/articles/291639.aspx" aiotitle="What is AspectJ" aiotarget="false"> What is AspectJ </a>
				<script language="javascript"><![CDATA[ocument.title="What is AspectJ  - "+document.title]]&gt;</script>
		</div>
		<div class="postText">
				<blockquote>网上出现了很多讲解AspectJ的资料，但大多是从讲解AspectJ语法开始,本文从另一个角度讲解AspectJ，作者着重介绍了AspectJ的设计思路和运行原理。 </blockquote>
				<p>
						<a name="1">
								<span class="atitle2">1. 序</span>
						</a>
						<br />Aspect Oriented Programming (AOP)是近来一个比较热门的话题。 </p>
				<p>AspectJ是AOP的Java语言的实现，获得了Java程序员的广泛关注。 </p>
				<p>关于AspectJ和AOP的具体资料，请从下列链接中查找： </p>
				<p>
						<a href="http://www.eclipse.org/aspectj/" target="_blank">http://www.eclipse.org/aspectj/</a>
						<br />
						<a href="http://www.parc.com/research/csl/projects/aspectj/" target="_blank">http://www.parc.com/research/csl/projects/aspectj/</a>
						<br />
						<a href="http://aosd.net/" target="_blank">http://aosd.net/</a>
						<br />
				</p>
				<p>网上出现了很多讲解AspectJ的资料，但大多是从讲解AspectJ语法开始，然后讲解如何应用AspectJ，如何分离软件开发过程的不同方面（Aspect）--Log，Session，Authentication and Authorization，Transaction，等等。 </p>
				<p>初次接触AspectJ的读者看到这些资料（或者语法手册），会感到AspectJ有些神秘。他们想知道，AspectJ是如何做到这些的？AspectJ是怎样工作的？AspectJ需要特殊的运行环境吗？ </p>
				<p>本文从另一个角度讲解AspectJ，本文从讲解AspectJ的设计思路、运行原理入手，回答上述问题。 </p>
				<p>本文讲解的主要内容，按照概念的重要程度，排列如下： </p>
				<ol>
						<li>AspectJ是一个代码生成工具（Code Generator）。 </li>
						<li>AspectJ语法就是用来定义代码生成规则的语法。您如果使用过Java Compiler Compiler (JavaCC)，您会发现，两者的代码生成规则的理念惊人相似。 </li>
						<li>AspectJ有自己的语法编译工具，编译的结果是Java Class文件，运行的时候，classpath需要包含AspectJ的一个jar文件（Runtime lib）。 </li>
						<li>AspectJ和xDoclet的比较。AspectJ和EJB Descriptor的比较。 </li>
				</ol>
				<p>本文的原则是，只细讲其他资料没有讲到的东西，其他资料讲过的东西，不讲或略讲。以节省网络资源，更为了节省大家宝贵的时间。J </p>
				<p>
						<a name="2">
								<span class="atitle2">2．Aspect Oriented Programming (AOP)</span>
						</a>
						<br />本节简单介绍AOP的概念，解释我们为什么需要AOP。 </p>
				<p>AOP是Object Oriented Programming（OOP）的补充。</p>
				<p>OOP能够很好地解决对象的数据和封装的问题，却不能很好的解决Aspect（"方面"）分离的问题。下面举例具体说明。</p>
				<p>比如，我们有一个Bank（银行）类。Bank有两个方法，deposit（存钱）和withdraw（取钱）。 </p>
				<p>类和方法的定义如下： </p>
				<p>
				</p>
				<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
						<tbody>
								<tr>
										<td>
												<pre>
														<code>
Code 2.1 Bank.java
class Bank{
public float deposit(AccountInfo account, float money){
  // 增加account账户的钱数，返回账户里当前的钱数
}

public float withdraw(AccountInfo account, float money){
  // 减少account账户的钱数，返回取出的钱数
}
};
</code>
												</pre>
										</td>
								</tr>
						</tbody>
				</table>
				<p>这两个方法涉及到用户的账户资金等重要信息，必须要非常小心，所以编写完上面的商业逻辑之后，项目负责人又提出了新的要求--给Bank类的每个重要方法加上安全认证特性。 </p>
				<p>于是，我们不得不分别在上面的两个方法中加入安全认证的代码。 </p>
				<p>类和方法的定义如下：（新增加的代码用不同的背景标出） </p>
				<p>
				</p>
				<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
						<tbody>
								<tr>
										<td>
												<pre>
														<code>
Code 2.2 Bank.java
class Bank{
public float deposit(AccountInfo account, float money){
  // 验证account是否为合法用户
  // 增加account账户的钱数，返回账户里当前的钱数
}

public float withdraw(AccountInfo account, float money){
  // 验证account是否为合法用户
  // 减少account账户的钱数，返回取出的钱数
}
};
</code>
												</pre>
										</td>
								</tr>
						</tbody>
				</table>
				<p>这两个方法都需要操作数据库，为了保持数据完整性，项目负责人又提出了新的要求--给Bank类的每个操作数据库的方法加上事务控制。 </p>
				<p>于是，我们不得不分别在上面的两个方法中加入安全认证的代码。 </p>
				<p>类和方法的定义如下：（新增加的代码用不同的背景标出） </p>
				<p>
				</p>
				<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
						<tbody>
								<tr>
										<td>
												<pre>
														<code>
Code 2.3 Bank.java
class Bank{
public float deposit(AccountInfo account, float money){
  // 验证account是否为合法用户
  // Begin Transaction
  // 增加account账户的钱数，返回账户里当前的钱数
  // End Transaction
}

public float withdraw(AccountInfo account, float money){
  // 验证account是否为合法用户
  // Begin Transaction
  // 减少account账户的钱数，返回取出的钱数
  // End Transaction
}
};
</code>
												</pre>
										</td>
								</tr>
						</tbody>
				</table>
				<p>我们看到，这些与商业逻辑无关的重复代码遍布在整个程序中。实际的工程项目中涉及到的类和函数，远远不止两个。如何解决这种问题？ </p>
				<p>我们首先来看看OOP能否解决这个问题。 </p>
				<p>我们利用Design Pattern的Template Pattern，可以抽出一个框架，改变上面的例子的整个设计结构。 </p>
				<p>类和方法的定义如下： </p>
				<p>
				</p>
				<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
						<tbody>
								<tr>
										<td>
												<pre>
														<code>
Code 2.4 Base.java
abstract class Base{
public float importantMethod(AccountInfo account, float money){
  // 验证account是否为合法用户
  // Begin Transaction
  
  float result = yourBusiness(account, money)

  // End Transaction
  return result;	
}

protected abstract float yourBusiness(AccountInfo account, float money);
};

Code 2.5 BankDeposit.java
class BankDeposit extends Base{ 
protected float yourBusiness(AccountInfo account, float money){
  // 增加account账户的钱数，返回账户里当前的钱数
}
};

Code 2.6 BankWithdraw.java
class BankWithdraw extends Base{ 
protected float yourBusiness(AccountInfo account, float money){
  // 减少account账户的钱数，返回取出的钱数
}
};
</code>
												</pre>
										</td>
								</tr>
						</tbody>
				</table>
				<p>这里我们用一种很勉强的方法实现了认证和事务代码的重用。而且，有心的读者可能会注意到，这种方法的前提是，强制所有的方法都遵守同样的signature。 </p>
				<p>如果有一个转账方法transfer(AccountInfo giver, AccountInfo receiver, float money)，由于transfer方法的signature不同于yourBusiness的signature，这个方法无法使用上面的框架。 </p>
				<p>这个例子中提到的认证，事务等方面，就是AOP所关心的Aspect。 </p>
				<p>AOP就是为了解决这种问题而出现的。AOP的目的就是--Separation of Aspects (or Separation of Concerns). </p>
				<p>下面的章节，解释EJB Descriptor，AspectJ，xDoclet等工具如何解决Separation of Aspects的问题。 </p>
				<p>
						<a name="3">
								<span class="atitle2">3．EJB Descriptor</span>
						</a>
						<br />如果我们使用EJB实现上面的例子，Bank类可以作为一个Stateless Session Bean实现。 </p>
				<p>在Bank的代码中只用考虑商业逻辑，不用考虑认证和事务等方面。 </p>
				<p>认证和事务等方面在EJB Descriptor中定义，由EJB Container提供这些方面的实现。 </p>
				<p>我们来看一下，如何使用EJB Descriptor描述上面的例子。 </p>
				<p>EJB Descriptor包括一个ejb-jar.xml文件。ejb-jar.xml文件包含两大部分，enterprise-beans和 assembly-descriptor部分。enterprise-beans部分包含EJB的定义--JNDI Name，EJB Home, Interface, Bean Class Path等；assembly-descriptor部分包括配置信息的定义--安全角色，事务控制等等。 </p>
				<p>下面给出上面例子对应的模拟EJB Descriptor。 </p>
				<p>
				</p>
				<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
						<tbody>
								<tr>
										<td>
												<pre>
														<code>
&lt;ejb-jar&gt;
&lt;enterprise-beans&gt;
  &lt;session&gt;
     &lt;ejb-name&gt;Bank&lt;/ejb-name&gt;
     …
     &lt;ejb-class&gt;example.Bank&lt;/ejb-class&gt;
     &lt;session-type&gt;Stateless&lt;/session-type&gt;
     &lt;transaction-type&gt;Container&lt;/transaction-type&gt;
&lt;security-role-ref&gt;
&lt;role-name&gt;bank-account&lt;/role-name&gt;
&lt;/security-role-ref&gt;
  &lt;/session&gt;
&lt;/enterprise-beans&gt;

&lt;assembly-descriptor&gt;
  &lt;security-role&gt;
    &lt;role-name&gt;bank-account&lt;/role-name&gt;
  &lt;/security-role&gt;

&lt;method-permission&gt;
&lt;role-name&gt;employee&lt;/role-name&gt;
&lt;method&gt;
&lt;ejb-name&gt;Bank&lt;/ejb-name&gt;
&lt;method-name&gt;deposit&lt;/method-name&gt;
&lt;/method&gt;
&lt;method&gt;
&lt;ejb-name&gt;Bank&lt;/ejb-name&gt;
&lt;method-name&gt;withdraw&lt;/method-name&gt;
&lt;/method&gt;
&lt;/method-permission&gt;

&lt;container-transaction&gt;
&lt;method&gt;
&lt;ejb-name&gt;Bank&lt;/ejb-name&gt;
&lt;method-name&gt;deposit&lt;/method-name&gt;
&lt;/method&gt;
&lt;method&gt;
&lt;ejb-name&gt;Bank&lt;/ejb-name&gt;
&lt;method-name&gt;withdraw&lt;/method-name&gt;
&lt;/method&gt;

&lt;trans-attribute&gt;Required&lt;/trans-attribute&gt;
&lt;/container-transaction&gt;
&lt;/assembly-descriptor&gt;
&lt;/ejb-jar&gt;
</code>
												</pre>
										</td>
								</tr>
						</tbody>
				</table>
				<p>本文后面会讲到如何用AspectJ实现上例中的Separation of Aspects。 </p>
				<p>读者可以比较一下AspectJ语法和EJB Descriptor定义之间的对应关系。 </p>
				<p>两者都提供了类名、方法名的匹配规则，能够把类的方法映射到认证，事务等Aspect（方面）。 </p>
				<p>
						<a name="4">
								<span class="atitle2">4．AspectJ</span>
						</a>
						<br />这一节我们来看看AspectJ如何实现上例中的Separation of Aspects。 </p>
				<p>使用AspectJ，我们不用对原有的代码做任何修改，就可以为代码提供不同的Aspect（方面）--比如，认证，事务等。 </p>
				<p>我们只需要提供两个不同的Aspect--认证Aspect和事务Aspect。 </p>
				<p>
				</p>
				<table cellspacing="0" cellpadding="5" width="100%" bgcolor="#cccccc" border="1">
						<tbody>
								<tr>
										<td>
												<pre>
														<code>
Code 4.1 AuthAspect.java
aspect AuthAspect{
  pointcut bankMethods() : execution (* Bank.deposit(…)) || execution (* Bank. withdraw (…));

  Object around(): bankMethods(){
  // 验证account是否为合法用户

  return proceed();
  }
};

Code 4.2 TransactionAspect.java
aspect TransactionAspect{
  pointcut bankMethods() : execution(* Bank.deposit(…)) || execution (* Bank. withdraw (…));

  Object around(): bankMethods(){
  // Begin Transaction
  Object result = proceed();
  // End Transaction
  return result;
  }
};
</code>
												</pre>
										</td>
								</tr>
						</tbody>
				</table>
				<p>如果您暂时不能理解这段代码，没有关系，后面会讲到，这些aspect的定义，不过是定义了一些代码生成规则。 </p>
				<p>我们用AspectJ编译器编译Bank文件和含有aspect的这个文件，出来的结果就是带有安全认证和事务处理的Bank类。编译出来的这个 Bank类调用了AspectJ Runtime Lib，所以，如果你要运行这个Bank类，你需要把AspectJ Runtime Lib设置在你的classpath里面。 </p>
				<p>我们来看看，AspectJ编译器为我们做了什么事情。 </p>
				<ol>
						<li>首先，AspectJ从文件列表里取出所有的文件名，然后读取这些文件，进行分析。 </li>
						<li>AspectJ发现一些文件含有aspect的定义，在这个例子里，就是AuthAspect和TransactionAspect的定义；这些aspect就是代码生成规则。 </li>
						<li>AspectJ根据这些aspect代码生成规则，修改添加你的源代码。在这个例子里，就是修改添加Bank文件。 </li>
						<li>AspectJ读取AuthAspect的定义，发现了一个pointcut--bankMethods()；这个pointcut的定义是execution(* Bank.deposit(…)) || execution(* Bank. withdraw (…))，表示所有对Bank类的deposit和withdraw方法的执行点。 </li>
						<li>AspectJ继续读取AuthAspect的定义，发现了一个around()，这在AspectJ中叫做Advice，我不明白为什么叫这个名字，不过没关系，我们只要知道它是干什么的就行了。Advice允许你在某个类的方法的调用之前或调用之后，加入另外的代码。Code 4.1所示代码中的around()的" // 验证account是否为合法用户"部分，就是要加入的代码。这段代码要加在哪里呢？around()后面跟了一个pointcut-- bankMethods()。根据这个pointcut，AspectJ会把这段代码加入到Bank.deposit和Bank.withdraw两个方法的执行之前。达到的效果就如同Code 2.2所示。 </li>
						<li>AspectJ读取TransactionAspect的定义，象第（4）步一样，发现了发现了一个pointcut--bankMethods()。 </li>
						<li>AspectJ继续读取AuthAspect的定义，发现了一个around()。这次AspectJ把"Begin Transaction"和"End Transaction"两段代码加在Bank.deposit和Bank. withdraw两个方法的执行前后。达到的效果就如同Code 2.3所示。 </li>
				</ol>
				<p>如何验证这一点？您可以到<a href="http://www.eclipse.org/aspectj/" target="_blank">http://www.eclipse.org/aspectj/</a>下载安装AspectJ，编译里面的Sample，把编译结果反编译一下，就可以看到AspetJ自动生成的代码。 </p>
				<p>我们看到，AspectJ是一种代码自动生成工具。你编写一段通用的代码，比如认证方面的代码，事务方面的代码，然后根据AspectJ语法定义一套代码生成规则（aspect定义），AspectJ就会帮助你自动把这段通用代码分布到对应的代码里面去，简单快捷，算无遗策。 </p>
				<p>无独有偶，一个著名的编译器生成工具--Java Compiler Compiler (JavaCC)，也采用了非常相似的代码生成机制。JavaCC允许你在语法定义规则文件中，加入你自己的Java代码，用来处理读入的各种语法元素。 </p>
				<p>AspectJ令你的代码更精简，结构更良好。AspectJ的好处，我就不多说了，网上很多精彩的文章探讨AspectJ的各种用途。 </p>
				<p>下面介绍一个著名的代码自动生成器--xDoclet，和EJB Descriptor，AspectJ之间的联系和比较。 </p>
				<p>
						<a name="5">
								<span class="atitle2">5．xDoclet</span>
						</a>
						<br />我们知道，Doclet用来生成Javadoc，xDoclet是Doclet的扩展，不仅仅能生成Javadoc，还能够生成源代码和配置信息等。 </p>
				<p>Doclet和xDoclet的工作原理，就是处理源代码中的注释中的tag，生成相应的信息。这些tag都以@开头，你可以自己定义tag和对tag的处理，生成自定义的信息。 </p>
				<p>（这里提一下Apache Maven Project。Maven是一种Project Build工具。用Maven进行管理的项目，能够同时生成Javadoc和XRef。XRef是Source Code Cross Reference。) </p>
				<p>JBoss就利用xDoclet为EJB自动生成EJB Home和EJB Object Interface源文件，和EJB Descriptor文件。 </p>
				<p>在Sourceforge.net上看到一个叫做Barter的开源项目，利用xDoclet为类方法生成AspectJ代码。 </p>
				<p>请注意，EJB Descriptor和AspectJ都是把方方面面的Aspects集中在一处进行管理，而xDoclet的思想是处理散布在源代码中的各种tag。 </p>
				<p>xDoclet在生成EJB Descriptor和AspectJ等方面的应用，正应了中国的一句古话--分久必合，合久必分。 </p>
				<p>
						<a name="6">
								<span class="atitle2">6．总结</span>
						</a>
						<br />开源项目的出现，打破了软件技术领域的众多壁垒，推动软件技术进程的日新月异。 </p>
				<p>同时，一些新名词，新概念也层出不穷，令人眼花缭乱，无所适从。其实，很多东西都是换汤不换药，我们理解应用这些新技术的时候，要抓住本质，要破除迷信，破除任何人为的神秘感。 </p>
				<p>举个例子，现在炒作的很热的一些概念，"Web Service"，还有"Grid Computation"（网格计算），都是基于原有的各种技术发展出来的。媒体和技术文章不应该人为地制造任何神秘感。 </p>
				<p>互联网时代的权威，不是说出来的，而是做出来的。 </p>
				<p>另外，围绕着一些有前途的新技术，总会出现大量的"快速入门手册"，有些简直就是对该技术帮助文档的翻译，而且，有难度的地方没有翻译出来，大家都明白的地方翻译得非常详尽，详尽到了没有必要的地步。这种因为市场需求而产生的应景时文，大量地出现在技术文章领域。 </p>
				<p>笔者对本文的期望是，决不迷信，决不重复。并试图引入一种洁净的，毫无废话的文风。笔者期待一针见血的驳斥和批评。 </p>
				<br />
		</div>
<img src ="http://www.blogjava.net/aojilee/aggbug/46652.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-05-17 17:00 <a href="http://www.blogjava.net/aojilee/articles/46652.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>jsp防止表单的重复提交</title><link>http://www.blogjava.net/aojilee/articles/45056.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Mon, 08 May 2006 09:50:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/45056.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/45056.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/45056.html#Feedback</comments><slash:comments>1</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/45056.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/45056.html</trackback:ping><description><![CDATA[
		<p>1.javascript ，设置一个变量，只允许提交一次。<br />&lt;script language="javascript"&gt; <br />    var checkSubmitFlg = false; <br />        function checkSubmit() { <br />               if (checkSubmitFlg == true) { <br />                   return false; <br />                }<br />               checkSubmitFlg = true; <br />               return true; <br />          }<br />         document.ondblclick = function docondblclick() { <br />                window.event.returnValue = false; <br />          }<br />          document.onclick = function doconclick() { <br />                if (checkSubmitFlg) { <br />                    window.event.returnValue = false; <br />                } <br />          } <br />&lt;/script&gt; </p>
		<p>　<br /> </p>
		<p> <br />　　&lt;html:form action="myAction.do" method="post" onsubmit="return checkSubmit();"&gt;　　 </p>
		<p>
				<br />　　2 还是javascript，将提交按钮或者image置为disable　　 </p>
		<p>      &lt;html:form action="myAction.do" method="post" onsubmit="getElById('submitInput').disabled = true; return true;"&gt; 　　 <br />　　 &lt;html:image styleId="submitInput" src="images/ok_b.gif" border="0" /&gt;　 <br />　　 &lt;/html:form&gt;</p>
		<p>　　 　　 <br />　　3 利用struts的同步令牌机制　　 <br />　　利用同步令牌（Token）机制来解决Web应用中重复提交的问题，Struts也给出了一个参考实现。 <br />　　基本原理：　 <br />　　服务器端在处理到达的请求之前，会将请求中包含的令牌值与保存在当前用户会话中的令牌值进行比较，看是否匹配。在处理完该请求后，且在答复发送给客户端之前，将会产生一个新的令牌，该令牌除传给客户端以外，也会将用户会话中保存的旧的令牌进行替换。这样如果用户回退到刚才的提交页面并再次提交的话，客户端传过来的令牌就和服务器端的令牌不一致，从而有效地防止了重复提交的发生。　 <br />　　if (isTokenValid(request, true)) { <br />　　 // your code here <br />　　 return mapping.findForward("success"); <br />　　} else { <br />　　 saveToken(request); <br />　　 return mapping.findForward("submitagain"); <br />　　}　 <br />　　Struts根据用户会话ID和当前系统时间来生成一个唯一（对于每个会话）令牌的，具体实现可以参考TokenProcessor类中的generateToken()方法。　　 <br />　　1. //验证事务控制令牌,&lt;html:form &gt;会自动根据session中标识生成一个隐含input代表令牌，防止两次提交 <br />　　2. 在action中：<br />//&lt;input type="hidden" name="org.apache.struts.taglib.html.TOKEN" <br />　　 // value="6aa35341f25184fd996c4c918255c3ae"&gt; <br />　　 if (!isTokenValid(request)) <br />　　 errors.add(ActionErrors.GLOBAL_ERROR, <br />　　 new ActionError("error.transaction.token")); <br />　　 resetToken(request); //删除session中的令牌　　</p>
		<p>　　 <br />　　  <br />　　3. action有这样的一个方法生成令牌　　 <br />　　<br /> protected String generateToken(HttpServletRequest request) {　　 <br />　　 HttpSession session = request.getSession(); <br />　　 try { <br />　　   byte id[] = session.getId().getBytes(); <br />　　   byte now[] = <br />　　   new Long(System.currentTimeMillis()).toString().getBytes(); <br />　　   MessageDigest md = MessageDigest.getInstance("MD5"); <br />　　   md.update(id); <br />　　   md.update(now); <br />　　   return (toHex(md.digest())); <br />　　 } catch (IllegalStateException e) { <br />　　   return (null); <br />　　 } catch (NoSuchAlgorithmException e) { <br />　  　 return (null); <br />　　 } <br />　　 } </p>
		<p> </p>
<img src ="http://www.blogjava.net/aojilee/aggbug/45056.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-05-08 17:50 <a href="http://www.blogjava.net/aojilee/articles/45056.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>强大的javascript表单验证</title><link>http://www.blogjava.net/aojilee/articles/45054.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Mon, 08 May 2006 09:49:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/45054.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/45054.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/45054.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/45054.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/45054.html</trackback:ping><description><![CDATA[
		<p>表单的验证一直是网页设计者头痛的问题，表单验证类 Validator就是为解决这个问题而写的，旨在使设计者从纷繁复杂的表单验证中解放出来，把精力集中于网页的设计和功能上的改进上。 </p>
		<p>
				<br />Validator是基于JavaScript技术的伪静态类和对象的自定义属性，可以对网页中的表单项输入进行相应的验证，允许同一页面中同时验证多个表单，熟悉接口之后也可以对特定的表单项甚至仅仅是某个字符串进行验证。因为是伪静态类，所以在调用时不需要实例化，直接以"类名+.语法+属性或方法名"来调用。此外，Validator还提供3种不同的错误提示模式，以满足不同的需要。 </p>
		<p>
				<br />Validator目前可实现的验证类型有： <br />[JavaScript] 版<br />Validator目前可实现的验证类型有： <br />1.是否为空；<br />2.中文字符；<br />3.双字节字符<br />4.英文；<br />5.数字；<br />6.整数；<br />7.实数；<br />8.Email地址；<br />9.使用HTTP协议的网址；<br />10.电话号码；<br />11.货币；<br />12.手机号码；<br />13.邮政编码；<br />14.身份证号码(1.05增强)；<br />15.QQ号码；<br />16.日期；<br />17.符合安全规则的密码；<br />18.某项的重复值；<br />19.两数的关系比较；<br />20.判断输入值是否在(n, m)区间；<br />21.输入字符长度限制(可按字节比较)；<br />22.对于具有相同名称的单选按钮的选中判断；<br />23.限制具有相同名称的多选按钮的选中数目；<br />24.自定义的正则表达式验证； <br />25.文件上传格式过滤(1.04)<br />运行环境(客户端)： <br />在Windows Server 2003下用IE6.0+SP1和Mozilla Firefox 1.0测试通过；<br />在Lunix RedHat 9下的Netscape测试通过； </p>
		<p>
				<br />对于客户端的表单验证，这个基于JavaScript编写的Validator基本上都可以满足，具体可以下载CHM文件：Validator.CHM下载<br /> <br /> &lt;title&gt;表单验证类 Validator v1.05&lt;/title&gt;<br /> &lt;style&gt;<br /> body,td{font:normal 12px Verdana;color:#333333}<br /> input,textarea,select,td{font:normal 12px Verdana;color:#333333;border:1px solid #999999;background:#ffffff}<br /> table{border-collapse:collapse;}<br /> td{padding:3px}<br /> input{height:20;}<br /> textarea{width:80%;height:50px;overflow:auto;}<br /> form{display:inline}<br /> &lt;/style&gt;<br /> &lt;table align="center"&gt;<br />  &lt;form name="theForm" id="demo" method="get" onSubmit="return Validator.Validate(this,2)"&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;身份证号：&lt;/td&gt;&lt;td&gt;&lt;input name="Card" dataType="IdCard" msg="身份证号错误"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />    &lt;tr&gt;<br />   &lt;td&gt;真实姓名：&lt;/td&gt;&lt;td&gt;&lt;input name="Name" dataType="Chinese" msg="真实姓名只允许中文"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;ID：&lt;/td&gt;&lt;td&gt;&lt;input name="username" dataType="Username" msg="ID名不符合规定"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;英文名：&lt;/td&gt;&lt;td&gt;&lt;input name="Nick" dataType="English" require="false" msg="英文名只允许英文字母"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />    &lt;tr&gt;<br />   &lt;td&gt;主页：&lt;/td&gt;&lt;td&gt;&lt;input name="Homepage" require="false" dataType="Url"   msg="非法的Url"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;密码：&lt;/td&gt;&lt;td&gt;&lt;input name="Password" dataType="SafeString"   msg="密码不符合安全规则" type="password"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;重复：&lt;/td&gt;&lt;td&gt;&lt;input name="Repeat" dataType="Repeat" to="Password" msg="两次输入的密码不一致" type="password"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;信箱：&lt;/td&gt;&lt;td&gt;&lt;input name="Email" dataType="Email" msg="信箱格式不正确"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />    &lt;tr&gt;<br />   &lt;td&gt;信箱：&lt;/td&gt;&lt;td&gt;&lt;input name="Email" dataType="Repeat" to="Email" msg="两次输入的信箱不一致"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;QQ：&lt;/td&gt;&lt;td&gt;&lt;input name="QQ" require="false" dataType="QQ" msg="QQ号码不存在"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />    &lt;tr&gt;<br />   &lt;td&gt;身份证：&lt;/td&gt;&lt;td&gt;&lt;input name="Card" dataType="IdCard" msg="身份证号码不正确"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;年龄：&lt;/td&gt;&lt;td&gt;&lt;input name="Year" dataType="Range" msg="年龄必须在18~28之间" min="18" max="28"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />   &lt;tr&gt;<br />   &lt;td&gt;年龄1：&lt;/td&gt;&lt;td&gt;&lt;input name="Year1" require="false" dataType="Compare" msg="年龄必须在18以上" to="18" operator="GreaterThanEqual"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />   &lt;tr&gt;<br />   &lt;td&gt;电话：&lt;/td&gt;&lt;td&gt;&lt;input name="Phone" require="false" dataType="Phone" msg="电话号码不正确"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />   &lt;tr&gt;<br />   &lt;td&gt;手机：&lt;/td&gt;&lt;td&gt;&lt;input name="Mobile" require="false" dataType="Mobile" msg="手机号码不正确"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />     &lt;tr&gt;<br />   &lt;td&gt;生日：&lt;/td&gt;&lt;td&gt;&lt;input name="Birthday" dataType="Date" format="ymd" msg="生日日期不存在"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />   &lt;tr&gt;<br />   &lt;td&gt;邮政编码：&lt;/td&gt;&lt;td&gt;&lt;input name="Zip" dataType="Custom" regexp="^[1-9]\d{5}$" msg="邮政编码不存在"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;邮政编码：&lt;/td&gt;&lt;td&gt;&lt;input name="Zip1" dataType="Zip" msg="邮政编码不存在"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;操作系统：&lt;/td&gt;&lt;td&gt;&lt;select name="Operation" dataType="Require"  msg="未选择所用操作系统" &gt;&lt;option value=""&gt;选择您所用的操作系统&lt;/option&gt;&lt;option value="Win98"&gt;Win98&lt;/option&gt;&lt;option value="Win2k"&gt;Win2k&lt;/option&gt;&lt;option value="WinXP"&gt;WinXP&lt;/option&gt;&lt;/select&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;所在省份：&lt;/td&gt;&lt;td&gt;广东&lt;input name="Province" value="1" type="radio"&gt;陕西&lt;input name="Province" value="2" type="radio"&gt;浙江&lt;input name="Province" value="3" type="radio"&gt;江西&lt;input name="Province" value="4" type="radio" dataType="Group"  msg="必须选定一个省份" &gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td&gt;爱好：&lt;/td&gt;&lt;td&gt;运动&lt;input name="Favorite" value="1" type="checkbox"&gt;上网&lt;input name="Favorite" value="2" type="checkbox"&gt;听音乐&lt;input name="Favorite" value="3" type="checkbox"&gt;看书&lt;input name="Favorite" value="4" type="checkbox"" dataType="Group" min="2" max="3"  msg="必须选择2~3种爱好"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />   &lt;td&gt;自我介绍：&lt;/td&gt;&lt;td&gt;&lt;textarea name="Description" dataType="Limit" max="10"  msg="自我介绍内容必须在10个字之内"&gt;中文是一个字&lt;/textarea&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />     &lt;td&gt;自传：&lt;/td&gt;&lt;td&gt;&lt;textarea name="History" dataType="LimitB" min="3" max="10"  msg="自传内容必须在[3,10]个字节之内"&gt;中文是两个字节t&lt;/textarea&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />    &lt;tr&gt;<br />   &lt;td&gt;相片上传：&lt;/td&gt;&lt;td&gt;&lt;input name="up" dataType="Filter" msg="非法的文件格式" type="file" accept="jpg, gif, png"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;tr&gt;<br />   &lt;td colspan="2"&gt;&lt;input name="Submit" type="submit" value="确定提交"&gt;&lt;input onClick="Validator.Validate(document.getElementById('demo'))" value="检验模式1" type="button"&gt;&lt;input onClick="Validator.Validate(document.getElementById('demo'),2)" value="检验模式2" type="button"&gt;&lt;input onClick="Validator.Validate(document.getElementById('demo'),3)" value="检验模式3" type="button"&gt;&lt;/td&gt;<br />  &lt;/tr&gt;<br />  &lt;/form&gt;<br /> &lt;/table&gt;<br />&lt;script&gt;<br />  /*************************************************<br /> Validator v1.05<br /> code by 我佛山人<br /> <a href="mailto:wfsr@msn.com">wfsr@msn.com</a><br />*************************************************/<br /> Validator = {<br /> Require : /.+/,<br /> Email : /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/,<br /> Phone : /^((\(\d{2,3}\))|(\d{3}\-))?(\(0\d{2,3}\)|0\d{2,3}-)?[1-9]\d{6,7}(\-\d{1,4})?$/,<br /> Mobile : /^((\(\d{2,3}\))|(\d{3}\-))?13\d{9}$/,<br /> Url : /^http:\/\/[A-Za-z0-9]+\.[A-Za-z0-9]+[\/=\?%\-&amp;_~`@[\]\':+!]*([^&lt;&gt;\"\"])*$/,<br /> IdCard : "this.IsIdCard(value)",<br /> Currency : /^\d+(\.\d+)?$/,<br /> Number : /^\d+$/,<br /> Zip : /^[1-9]\d{5}$/,<br /> QQ : /^[1-9]\d{4,8}$/,<br /> Integer : /^[-\+]?\d+$/,<br /> Double : /^[-\+]?\d+(\.\d+)?$/,<br /> English : /^[A-Za-z]+$/,<br /> Chinese :  /^[\u0391-\uFFE5]+$/,<br /> Username : /^[a-z]\w{3,}$/i,<br /> UnSafe : /^(([A-Z]*|[a-z]*|\d*|[-_\~!@#\$%\^&amp;\*\.\(\)\[\]\{\}&lt;&gt;\?\\\/\'\"]*)|.{0,5})$|\s/,<br /> IsSafe : function(str){return !this.UnSafe.test(str);},<br /> SafeString : "this.IsSafe(value)",<br /> Filter : "this.DoFilter(value, getAttribute('accept'))",<br /> Limit : "this.limit(value.length,getAttribute('min'),  getAttribute('max'))",<br /> LimitB : "this.limit(this.LenB(value), getAttribute('min'), getAttribute('max'))",<br /> Date : "this.IsDate(value, getAttribute('min'), getAttribute('format'))",<br /> Repeat : "value == document.getElementsByName(getAttribute('to'))[0].value",<br /> Range : "getAttribute('min') &lt; (value|0) &amp;&amp; (value|0) &lt; getAttribute('max')",<br /> Compare : "this.compare(value,getAttribute('operator'),getAttribute('to'))",<br /> Custom : "this.Exec(value, getAttribute('regexp'))",<br /> Group : "this.MustChecked(getAttribute('name'), getAttribute('min'), getAttribute('max'))",<br /> ErrorItem : [document.forms[0]],<br /> ErrorMessage : ["以下原因导致提交失败：\t\t\t\t"],<br /> Validate : function(theForm, mode){<br />  var obj = theForm || event.srcElement;<br />  var count = obj.elements.length;<br />  this.ErrorMessage.length = 1;<br />  this.ErrorItem.length = 1;<br />  this.ErrorItem[0] = obj;<br />  for(var i=0;i&lt;count;i++){<br />   with(obj.elements[i]){<br />    var _dataType = getAttribute("dataType");<br />    if(typeof(_dataType) == "object" || typeof(this[_dataType]) == "undefined")  continue;<br />    this.ClearState(obj.elements[i]);<br />    if(getAttribute("require") == "false" &amp;&amp; value == "") continue;<br />    switch(_dataType){<br />     case "IdCard" :<br />     case "Date" :<br />     case "Repeat" :<br />     case "Range" :<br />     case "Compare" :<br />     case "Custom" :<br />     case "Group" : <br />     case "Limit" :<br />     case "LimitB" :<br />     case "SafeString" :<br />     case "Filter" :<br />      if(!eval(this[_dataType])) {<br />       this.AddError(i, getAttribute("msg"));<br />      }<br />      break;<br />     default :<br />      if(!this[_dataType].test(value)){<br />       this.AddError(i, getAttribute("msg"));<br />      }<br />      break;<br />    }<br />   }<br />  }<br />  if(this.ErrorMessage.length &gt; 1){<br />   mode = mode || 1;<br />   var errCount = this.ErrorItem.length;<br />   switch(mode){<br />   case 2 :<br />    for(var i=1;i&lt;errCount;i++)<br />     this.ErrorItem[i].style.color = "red";<br />   case 1 :<br />    alert(this.ErrorMessage.join("\n"));<br />    this.ErrorItem[1].focus();<br />    break;<br />   case 3 :<br />    for(var i=1;i&lt;errCount;i++){<br />    try{<br />     var span = document.createElement("SPAN");<br />     span.id = "__ErrorMessagePanel";<br />     span.style.color = "red";<br />     this.ErrorItem[i].parentNode.appendChild(span);<br />     span.innerHTML = this.ErrorMessage[i].replace(/\d+:/,"*");<br />     }<br />     catch(e){alert(e.description);}<br />    }<br />    this.ErrorItem[1].focus();<br />    break;<br />   default :<br />    alert(this.ErrorMessage.join("\n"));<br />    break;<br />   }<br />   return false;<br />  }<br />  return true;<br /> },<br /> limit : function(len,min, max){<br />  min = min || 0;<br />  max = max || Number.MAX_VALUE;<br />  return min &lt;= len &amp;&amp; len &lt;= max;<br /> },<br /> LenB : function(str){<br />  return str.replace(/[^\x00-\xff]/g,"**").length;<br /> },<br /> ClearState : function(elem){<br />  with(elem){<br />   if(style.color == "red")<br />    style.color = "";<br />   var lastNode = parentNode.childNodes[parentNode.childNodes.length-1];<br />   if(lastNode.id == "__ErrorMessagePanel")<br />    parentNode.removeChild(lastNode);<br />  }<br /> },<br /> AddError : function(index, str){<br />  this.ErrorItem[this.ErrorItem.length] = this.ErrorItem[0].elements[index];<br />  this.ErrorMessage[this.ErrorMessage.length] = this.ErrorMessage.length + ":" + str;<br /> },<br /> Exec : function(op, reg){<br />  return new RegExp(reg,"g").test(op);<br /> },<br /> compare : function(op1,operator,op2){<br />  switch (operator) {<br />   case "NotEqual":<br />    return (op1 != op2);<br />   case "GreaterThan":<br />    return (op1 &gt; op2);<br />   case "GreaterThanEqual":<br />    return (op1 &gt;= op2);<br />   case "LessThan":<br />    return (op1 &lt; op2);<br />   case "LessThanEqual":<br />    return (op1 &lt;= op2);<br />   default:<br />    return (op1 == op2);            <br />  }<br /> },<br /> MustChecked : function(name, min, max){<br />  var groups = document.getElementsByName(name);<br />  var hasChecked = 0;<br />  min = min || 1;<br />  max = max || groups.length;<br />  for(var i=groups.length-1;i&gt;=0;i--)<br />   if(groups[i].checked) hasChecked++;<br />  return min &lt;= hasChecked &amp;&amp; hasChecked &lt;= max;<br /> },<br /> DoFilter : function(input, filter){<br />return new RegExp("^.+\.(?=EXT)(EXT)$".replace(/EXT/g, filter.split(/\s*,\s*/).join("|")), "gi").test(input);<br /> },<br /> IsIdCard : function(number){<br />  var date, Ai;<br />  var verify = "10x98765432";<br />  var Wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];<br />  var area = ['','','','','','','','','','','','北京','天津','河北','山西','内蒙古','','','','','','辽宁','吉林','黑龙江','','','','','','','','上海','江苏','浙江','安微','福建','江西','山东','','','','河南','湖北','湖南','广东','广西','海南','','','','重庆','四川','贵州','云南','西藏','','','','','','','陕西','甘肃','青海','宁夏','新疆','','','','','','台湾','','','','','','','','','','香港','澳门','','','','','','','','','国外'];<br />  var re = number.match(/^(\d{2})\d{4}(((\d{2})(\d{2})(\d{2})(\d{3}))|((\d{4})(\d{2})(\d{2})(\d{3}[x\d])))$/i);<br />  if(re == null) return false;<br />  if(re[1] &gt;= area.length || area[re[1]] == "") return false;<br />  if(re[2].length == 12){<br />   Ai = number.substr(0, 17);<br />   date = [re[9], re[10], re[11]].join("-");<br />  }<br />  else{<br />   Ai = number.substr(0, 6) + "19" + number.substr(6);<br />   date = ["19" + re[4], re[5], re[6]].join("-");<br />  }<br />  if(!this.IsDate(date, "ymd")) return false;<br />  var sum = 0;<br />  for(var i = 0;i&lt;=16;i++){<br />   sum += Ai.charAt(i) * Wi[i];<br />  }<br />  Ai +=  verify.charAt(sum%11);<br />  return (number.length ==15 || number.length == 18 &amp;&amp; number == Ai);<br /> },<br /> IsDate : function(op, formatString){<br />  formatString = formatString || "ymd";<br />  var m, year, month, day;<br />  switch(formatString){<br />   case "ymd" :<br />    m = op.match(new RegExp("^((<a href="file://\\d{4})|(\\d{2}))([-./])(\\d{1,2})\\4(\\d{1,2})$">\\d{4})|(\\d{2}))([-./])(\\d{1,2})\\4(\\d{1,2})$</a>"));<br />    if(m == null ) return false;<br />    day = m[6];<br />    month = m[5]*1;<br />    year =  (m[2].length == 4) ? m[2] : GetFullYear(parseInt(m[3], 10));<br />    break;<br />   case "dmy" :<br />    m = op.match(new RegExp("^(<a href="file://\\d{1,2})([-./])(\\d{1,2})\\2((\\d{4})|(\\d{2}))$">\\d{1,2})([-./])(\\d{1,2})\\2((\\d{4})|(\\d{2}))$</a>"));<br />    if(m == null ) return false;<br />    day = m[1];<br />    month = m[3]*1;<br />    year = (m[5].length == 4) ? m[5] : GetFullYear(parseInt(m[6], 10));<br />    break;<br />   default :<br />    break;<br />  }<br />  if(!parseInt(month)) return false;<br />  month = month==0 ?12:month;<br />  var date = new Date(year, month-1, day);<br />        return (typeof(date) == "object" &amp;&amp; year == date.getFullYear() &amp;&amp; month == (date.getMonth()+1) &amp;&amp; day == date.getDate());<br />  function GetFullYear(y){return ((y&lt;30 ? "20" : "19") + y)|0;}<br /> }<br /> }<br />&lt;/script&gt;</p>
		<p> </p>
<img src ="http://www.blogjava.net/aojilee/aggbug/45054.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-05-08 17:49 <a href="http://www.blogjava.net/aojilee/articles/45054.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>操作oracle数据库的CLOB字段</title><link>http://www.blogjava.net/aojilee/articles/43193.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Wed, 26 Apr 2006 01:51:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/43193.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/43193.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/43193.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/43193.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/43193.html</trackback:ping><description><![CDATA[/** <br />* <br />*操作oracle数据库的CLOB字段，包括读和写 <br />*作者：令少爷 <br />* */ <br /><br />package com.nes.common.sql.lob; <br /><br />import java.sql.*; <br />import java.io.*; <br />import oracle.jdbc.OracleResultSet; <br />import oracle.sql.*; <br /><br />public class JClob { <br /><br />String tableName = null; //表名 <br />String primaryKey = null; //表的主键名 <br />String primaryValue = null; //表的主键值 <br />String fieldName = null; //表的CLOB字段名 <br />String clobValue = null; //表的CLOB字段值 <br /><br />Connection conn = null; //与oracle的连接 <br /><br />/** <br />* <br />*用于测试用 <br />* <br />* */ <br />public static void main(String[] args) { <br />try { <br />JClob jc = new JClob(getConnection(),"aa","a","aaaa","c","ccc"); <br />jc.write(); <br />jc.read(); <br />} <br />catch (Exception e) { <br />System.out.println(e); <br />e.printStackTrace(); <br />} <br />} <br /><br /><br />/** <br />* <br />*构造方法 <br />* <br />* */ <br />public JClob(Connection connection,String tableName,String primaryKey,String primaryValue,String fieldName,String clobValue) { <br />this.conn = connection; <br />this.tableName = tableName; <br />this.primaryKey = primaryKey; <br />this.primaryValue = primaryValue; <br />this.fieldName = fieldName; <br />this.clobValue = clobValue; <br />} <br /><br />/** <br />* <br />*构造方法，但不必传clobValue值 <br />*一般构造出的实例用来读Clob字段 <br />* <br />* */ <br />public JClob(Connection connection,String tableName,String primaryKey,String primaryValue,String fieldName) { <br />this.conn = connection; <br />this.tableName = tableName; <br />this.primaryKey = primaryKey; <br />this.primaryValue = primaryValue; <br />this.fieldName = fieldName; <br />} <br /><br />/** <br />* <br />*用于测试 <br />* <br />* */ <br />public static Connection getConnection() throws SQLException,ClassNotFoundException { <br />Class.forName("oracle.jdbc.OracleDriver"); <br />Connection conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.18:1521:portal","portal","portal"); <br />return conn; <br />} <br /><br />/** <br />* <br />*读数据库中clob字段的内容 <br />*@return clob字段值 <br />* <br />* */ <br />public String read() throws SQLException,IOException { <br />String rtn = null; <br />try { <br />String sql = "select " + fieldName + " from " + tableName + " where " + primaryKey + "=" + primaryValue; <br />//Connection conn = getConnection(); <br />PreparedStatement pstmt = conn.prepareStatement(sql); <br />//int v = Integer.parseInt(primaryValue); <br />//pstmt.setInt(1,v); <br />ResultSet rs = pstmt.executeQuery(); <br /><br />java.sql.Clob clob = null; <br />if (rs.next()) { <br />clob = rs.getCLOB(fieldName); <br />//clob = ((OracleResultSet)rs).getCLOB(fieldName); <br />//clob = ((org.apache.commons.dbcp.DelegatingResultSet)rs).getClob(fieldName); <br />//Reader in = clob.getCharacterStream(); <br />InputStream input = clob.getAsciiStream(); <br />int len = (int)clob.length(); <br />byte[] by = new byte[len]; <br />int i ;//= input.read(by,0,len); <br />while(-1 != (i = input.read(by, 0, by.length))) { <br />input.read(by, 0, i); <br />} <br />rtn = new String(by); <br />} <br />} <br />catch (SQLException e){ <br />throw e; <br />} <br />catch (Exception ee) { <br />ee.printStackTrace(); <br />} <br /><br />return rtn; <br />} <br /><br />/** <br />* <br />*葱数据库中clob字段的内容 <br />* <br />* */ <br />public void write() throws SQLException,IOException { <br />String sql = "update " + tableName + " set " + fieldName + "=empty_clob() where " + primaryKey + "=" + primaryValue; <br />//Connection conn = getConnection(); <br />conn.setAutoCommit(false); <br /><br />PreparedStatement pstmt = conn.prepareStatement(sql); <br />pstmt.executeUpdate(); <br /><br />sql = "select " + fieldName + " from " + tableName + " where " + primaryKey + "=" + primaryValue; <br />Statement st = conn.createStatement(); <br />ResultSet rs = st.executeQuery(sql); <br /><br />java.sql.Clob clob ; <br />if (rs.next()) { <br />clob = ((oracle.jdbc.OracleResultSet)rs).getClob(fieldName); <br />//clob = ((org.apache.commons.dbcp.DelegatingResultSet)rs).getClob(fieldName); <br />oracle.sql.CLOB my_clob = (oracle.sql.CLOB)clob; <br />OutputStream writer = my_clob.getAsciiOutputStream(); <br />byte[] contentStr = this.getContent().getBytes(); <br />writer.write(contentStr); <br />writer.flush(); <br />writer.close(); <br />} <br /><br />conn.commit(); <br />rs.close(); <br />st.close(); <br />pstmt.close(); <br />conn.setAutoCommit(true); <br />} <br /><br />/** <br />* <br />* <br />* */ <br />private String getContent() { <br />return this.clobValue; <br />} <br /><br />/** <br />* <br />* <br />* */ <br />public void setClobValue(String clobValue) { <br />this.clobValue = clobValue; <br />} <br />} <img src ="http://www.blogjava.net/aojilee/aggbug/43193.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-04-26 09:51 <a href="http://www.blogjava.net/aojilee/articles/43193.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Web开发工具</title><link>http://www.blogjava.net/aojilee/articles/39885.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Fri, 07 Apr 2006 09:35:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/39885.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/39885.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/39885.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/39885.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/39885.html</trackback:ping><description><![CDATA[
		<h3>1、PowerBand </h3>
		<p>PowerBand 是一个IE的插件(同时也支持MyIE2/Maxthon)。提供了对HTML动态分析，跟踪，编辑的功能。能够方便快捷的分析HTML页面的结构，有助于网页设计人员/网站开发人员调试，分析晦涩的HTML代码。这是我最早使用的HTML开发辅助工具，现在是2.1版了，支持ASP.NET ViewState的解码，支持DebugView，类似于VC中的Watch，通过这个功能能够显示页面中元素对象的详细内容。同时， PowerBand也支持即时脚本交互功能。</p>
		<p>
				<a href="http://www.XingWorld.Net/download/soft/program/powerband21.zip">下载PowerBand2.1</a>
		</p>
		<h3>2、HttpWatch</h3>
&lt; p&gt;HttpWatch又是一个IE下的强劲插件，HttpWatch最主要的功能就是对通过浏览器进行网络通讯的数据进行监控和分析，当你在浏览器的地址栏上请求一个URL或者提交一份表单时，HttpWatch帮你分析http请求的head信息，访问页面的cookie信息，Get和Post 的详细数据包分析，Catch内容分析，QueryString分析。想知道GMail或者是任何一个AJAX网页时如何和服务器进行数据交互的吗？用这个插件就可以一览无余了<img alt="" src="/FCKeditor/editor/images/smiley/qq/face5.gif" align="absMiddle" /><p><a href="http://www.superdown.com/soft/8447.htm">下载HttpWatch3.2.0.63&lt; /A&gt; <a href="http://www.superdown.com/soft/16715.htm">下载注册机</a>&lt; /p&gt;
<h3>3、IE Developer Toolbar </h3><p>第三个插件还是基于IE下的，不过这个可是<a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=e59c3964-672d -4511-bb3e-2d5e1db91038&amp;displaylang=en">微软发布的Web开发IE工具栏&lt; /A&gt;，实现的功能如下：</a></p><ul><li>浏览和修改Web页的文档对象模型（DOM）。
</li><li>通过多种技术方式定位、选定Web页上的特定元素。
</li><li>禁止或激活IE设置。
</li><li>查看HTML对象的类名、ID，以及类似链接路径、tab顺序、快捷键等细节。
</li><li>描绘表格、单元格、图片或选定标签的轮廓。
</li><li>显示图片象素、大小、路径、替代文字等。
</li><li>即时重定义浏览器窗口大小到800x600或自定义大小。
</li><li>清空浏览器缓存和cookie，被清除项可从所有对象或给定域中选择。
</li><li>直接访问关联W3C规范参考、IE开发组blog或其他来源。
</li><li>显示设计时标尺，帮助对齐对象。 </li></ul><p><a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=e59c3964-672d -4511-bb3e-2d5e1db91038&amp;displaylang=en">官方下载</a></p><h3>4、ViewPage</h3>
&lt; p&gt;ViewPage是MyIE2/Maxthon浏览器插件，这个插件不算是纯粹的开发辅助，它看起来更像是资源提取，可以方便的提取网页中的源代码，包括所有框架的源代码，提取图像、Flash、媒体文件、链接，我最欣赏的就是能方便的查看页面中包含的所有js文件源代码和所有css源代码。 </a></p><p><a href="http://forum.maxthon.com/forum/index.php?showtopic=280">下载ViewPage1.2 for Maxthon</a></p><h3>5、Web Development with Mozilla Firefox</h3><p>Firefox下的开发辅助工具和插件非常多，先说Firefox自带的吧。</p><ul><li>查看源代码（查看-〉页面源代码），Firefox自带的查看源代码支持语法高亮显示，这个功能很独特
</li><li>查看页面信息（工具-〉页面信息），这是基本功能了，不过有点简陋
</li><li>Javascript调试台，这是Firefox的一大特色，是IE所没有的功能
</li><li>DOM查看器，内置的DOM分析和查看工具，怀疑PowerBand和IE Developer Toolbar 都是模仿Firefox的这个做的 </li></ul><p>Firefox自带的开发辅助功能虽然已很丰富，但让我更加垂涎的是丰富的开发类插件</p><ul><li>最著名的就是<a href="http://chrispederick.com/work/firefox/webdeveloper/">Web Developer</a>了，这个插件几乎囊括了所有能实现的web开发辅助功能，微软的那个就像是模仿于它
</li><li><a href="http://www.karmatics.com/aardvark/">Aardvark</a>是一个CSS debug插件
</li><li><a href="http://addneditcookies.mozdev.org/">Add &amp; Edit Cookies</a>，看名字就知道了，一个专门增加和修改Cookies的插件，相关的还有一个<a href="http://www.bitstorm.org/extensions/view-cookies/">View Cookies</a>是用来查看Cookies的插件
</li><li><a href="http://www.iosart.com/firefox/colorzilla/index.html">Colorzilla&lt; /A&gt;可以捕获当前鼠标所指的页面坐标位置和颜色，以及DOM路径
<li><a href="http://ieview.mozdev.org/">IE View</a> and <a href="http://operaview.mozdev.org/">Opera View</a>是两个专门在Firefox中用IE和Opera中打开一个页面的工具，方便你调试支持多浏览器的web
</li><li><a href="http://www.kevinfreitas.net/extensions/measureit/">MeasureIt&lt; /A&gt;是一个Firefox中画坐标和度量尺的小插件，用它测量HTML元素的宽高很是方便
<li><a href="http://livehttpheaders.mozdev.org/">Live HTTP Headers</a>是一个类似于HttpWatch功能一样的Firefox插件，用来查看HTTP Header、Cookie、MIME等信息，不过HttpWatch是收费的，这个确是免费的，推荐下载 </li></a></li></a></li></ul>
&lt; p&gt;Firefox下的开发类插件太多了，如果你感兴趣可以<a href="https://addons.mozilla.org/extensions/showlist.php?application=firefox&amp; numpg=10&amp;category=Developer%20Tools">打开这个链接</a>，这里面全是 Developer Tools。还有<a href="http://lesliefranke.com/sandbox/presentations/firefoxwdev/firefoxwdev.htm"> 这篇文章</a>，非常详细的一步一步介绍了Mozilla Firefox的web开发工具，推荐阅读。<img src ="http://www.blogjava.net/aojilee/aggbug/39885.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-04-07 17:35 <a href="http://www.blogjava.net/aojilee/articles/39885.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>POJO 与 PO的 概念</title><link>http://www.blogjava.net/aojilee/articles/35646.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Thu, 16 Mar 2006 06:20:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/35646.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/35646.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/35646.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/35646.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/35646.html</trackback:ping><description><![CDATA[<TABLE class=partsmb cellSpacing=0 border=0>
<TBODY>
<TR>
<TD></TD>
<TD class=bvBody>
<DIV id=entrycns!FA59E5D58CD1EF4B!206>
<TABLE class="fixedTable blogpost" cellSpacing=0 width="100%" border=0>
<TBODY>
<TR>
<TD class=bvh8></TD></TR>
<TR>
<TD vAlign=top><SPAN class=bold id=LastMDatecns!FA59E5D58CD1EF4B!206><STRONG></STRONG></SPAN></TD></TR>
<TR>
<TD height=4><STRONG></STRONG></TD></TR>
<TR>
<TD class=dateline><STRONG></STRONG></TD></TR>
<TR>
<TD height=4><STRONG></STRONG></TD></TR>
<TR>
<TD class=ellipse><SPAN class=bvTitle id=subjcns!FA59E5D58CD1EF4B!206><STRONG>POJO 与 PO的 概念</STRONG></SPAN></TD></TR>
<TR>
<TD class=bvh8><STRONG></STRONG></TD></TR>
<TR>
<TD id=msgcns!FA59E5D58CD1EF4B!206>
<DIV>
<H3>作者：robbin (MSN:robbin_fan AT hotmail DOT com)</H3>
<P>整理人：smallduzi</P>
<P><EM><STRONG>版权声明：本文严禁转载，如有转载请求，请和作者联系</STRONG></EM></P>
<P>POJO = pure old java object or plain ordinary java object or what ever.</P>
<P>PO = persisent object 持久对象</P>
<P>就是说在一些Object/Relation Mapping工具中，能够做到维护数据库表记录的persisent object完全是一个符合Java Bean规范的纯Java对象，没有增加别的属性和方法。全都是这样子的：</P><PRE>public class User { 
  private long id; 
  private String name;
  public void setId(long id) {
 this.id = id;
}  
public void setName(String name) {
this.name=name;
} 
 public long getId() {
 return id;
}  
public String getName() { 
return name;
}
}  
</PRE>
<P>首先要区别持久对象和POJO。</P>
<P>持久对象实际上必须对应数据库中的entity，所以和POJO有所区别。比如说POJO是由new创建，由GC回收。但是持久对象是insert数据库创建，由数据库delete删除的。基本上持久对象生命周期和数据库密切相关。另外持久对象往往只能存在一个数据库Connection之中，Connnection关闭以后，持久对象就不存在了，而POJO只要不被GC回收，总是存在的。</P>
<P>由于存在诸多差别，因此持久对象PO(Persistent Object)在代码上肯定和POJO不同，起码PO相对于POJO会增加一些用来管理数据库entity状态的属性和方法。而ORM追求的目标就是要PO在使用上尽量和POJO一致，对于程序员来说，他们可以把PO当做POJO来用，而感觉不到PO的存在。</P>
<P>JDO的实现方法是这样的：</P>
<P>1、编写POJO</P>
<P>2、编译POJO</P>
<P>3、使用JDO的一个专门工具，叫做Enhancer，一般是一个命令行程序，手工运行，或者在ant脚本里面运行，对POJO的class文件处理一下，把POJO替换成同名的PO。</P>
<P>4、在运行期运行的实际上是PO，而不是POJO。</P>
<P>该方法有点类似于JSP，JSP也是在编译期被转换成Servlet来运行的，在运行期实际上运行的是Servlet，而不是JSP。</P>
<P>Hibernate的实现方法比较先进：</P>
<P>1、编写POJO</P>
<P>2、编译POJO</P>
<P>3、直接运行，在运行期，由Hibernate的CGLIB动态把POJO转换为PO。</P>
<P>由此可以看出Hibernate是在运行期把POJO的字节码转换为PO的，而JDO是在编译期转换的。一般认为JDO的方式效率会稍高，毕竟是编译期转换嘛。但是Hibernate的作者Gavin King说CGLIB的效率非常之高，运行期的PO的字节码生成速度非常之快，效率损失几乎可以忽略不计。</P>
<P>实际上运行期生成PO的好处非常大，这样对于程序员来说，是无法接触到PO的，PO对他们来说完全透明。可以更加自由的以POJO的概念操纵PO。另外由于是运行期生成PO，所以可以支持增量编译，增量调试。而JDO则无法做到这一点。实际上已经有很多人在抱怨JDO的编译期Enhancer问题了，而据说JBossDO将采用运行期生成PO字节码，而不采用编译期生成PO字节码。</P>
<P>另外一个相关的问题是，不同的JDO产品的Enhancer生成的PO字节码可能会有所不同，可能会影响在JDO产品之间的可移植性，这一点有点类似EJB的可移植性难题。</P>
<P>由这个问题另外引出一个JDO的缺陷。</P>
<P>由于JDO的PO状态管理方式，所以当你在程序里面get/set的时候，实际上不是从PO的实例中取values，而是从JDO <A href="http://www.hibernate.org.cn/index.php?cmd=newdoc&amp;newdocname=State+Manager&amp;node=48&amp;refnode=84"><FONT color=#006699><U>State Manager</U></FONT></A><STRONG><FONT color=#880000>?</FONT></STRONG>中取出来，所以一旦PM关闭，PO就不能进行存取了。</P>
<P>在JDO中，也可以通过一些办法使得PO可以在PM外面使用，比如说定义PO是transient的，但是该PO在PM关闭后就没有PO identity了。无法进行跨PM的状态管理。</P>
<P>而Hibernate是从PO实例中取values的，所以即使Session关闭，也一样可以get/set，可以进行跨Session的状态管理。</P>
<P>在分多层的应用中，由于持久层和业务层和web层都是分开的，此时Hibernate的PO完全可以当做一个POJO来用，也就是当做一个VO，在各层间自由传递，而不用去管Session是开还是关。如果你把这个POJO序列化的话，甚至可以用在分布式环境中。（不适合lazy loading的情况）</P>
<P>但是JDO的PO在PM关闭后就不能再用了，所以必须在PM关闭前把PO拷贝一份VO，把VO传递给业务层和web层使用。在非分布式环境中，也可以使用ThreadLocal模式确保PM始终是打开状态，来避免每次必须进行PO到VO的拷贝操作。但是不管怎么说，这总是权宜之计，不如Hibernate的功能强。</P></DIV><A>
<DIV>
<H3>评论</H3>
<DIV>
<H2></A><FONT color=#1f57a0>&nbsp;</FONT><A></A><FONT color=#1f57a0>re: POJO 与 PO的 概念</FONT><A></A><FONT color=#1f57a0> </FONT><A></A><FONT color=#1f57a0>&nbsp;&nbsp;</FONT><A></A><FONT color=#1f57a0> </FONT></H2>potian 写道: <BR>辨别一些名词： <BR>1。VO：实际上很模糊，通常指ValueObject和ViewObject <BR>2. ViewObject，界面展现需要的对象，如Struts的FormBean <BR>3。Value Object，早期被作为ValueObject和Transfer Object的总称。实际上Value Object的真正意义在于它的内容，而不是身份 <BR>4。Transfer Object：数据传输对象，在应用程序不同层次之间传书对象，在一个分布式应用程序中，通常可以提高整体的性能 <BR>5。PO：也许就是Persistent Object，基本上就是Entity了 <BR>在不同的体系结构和实现方式里面，这些对象有可能重复，也有可能不重叠。如果你要做一个对所有的体系都能够方便移植的框架，那么每一种对象都需要严格区分。例如JDO的PO不能作为TO，应为它不能脱离PM，譬如你可以选择用ViewObject（如Struts的FOrmBean)直接作为TO，但在tapestry和Webwork里面就不合适了。但在很多时候，能够方便实用是最重要的，不要过度设计就是了。 <BR><BR>robbin写道： <BR><BR>POJO是这样一个对象，它是一个普通的Java对象，它不同于EJB这样的带有繁重的容器控制功能的对象，它也不是那种被Enhanced过的对象，例如JDO的静态Enhance，也不是类似Hibernate那样被动态的byte code generation过。 <BR><BR>也就是说POJO的概念是相对于其他那种被人动过手脚的class而言的，它是没有被动过手脚的。 <BR><BR>曹晓钢 <BR><BR>其实，为什么要做DAO?无非是： <BR>1， 管理connection/transaction (hibernate的话就是session/transaction) <BR>2, 便于进行统计/log操作； <BR>3， 便于进行权限控制； <BR><BR>DAO模式中，有两类对象，一种是DAO，一种是valueObject。 在我们讨论的这个情况中，value object是hibernate对应的POJO. <BR><BR>那么，按照我的理解，DAO就是一个Transaction包装器，其逻辑结构就是商业的具体事务。此处，数据库的transaction和商业的事务是统一的。 <BR><BR>这里有一篇不错的关于DAO的文章。 <BR><A href="http://www-106.ibm.com/developerworks/java/library/j-dao/"><U><FONT color=#0000ff>http://www-106.ibm.com/developerworks/java/library/j-dao/</FONT></U></A> <BR><BR>BO 包含business logic <BR>留在这备忘。 </DIV></DIV></TD></TR></TBODY></TABLE></DIV></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/aojilee/aggbug/35646.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-03-16 14:20 <a href="http://www.blogjava.net/aojilee/articles/35646.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java/J2EE中文问题终极解决之道</title><link>http://www.blogjava.net/aojilee/articles/26723.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Thu, 05 Jan 2006 07:09:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/26723.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/26723.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/26723.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/26723.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/26723.html</trackback:ping><description><![CDATA[<TABLE style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" cellSpacing=0 cellPadding=0 width="96%" border=0>
<TBODY>
<TR>
<TD bgColor=#e8f0f1>&nbsp; Java/J2EE中文问题终极解决之道</TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<DIV align=right></DIV></TD></TR></TBODY></TABLE><SPAN class=oblog_text>&nbsp; 
<P align=center><A href="http://www.jdon.com/aboutme.htm">板桥里人</A> http://www.jdon.com 2005/06/29</P>
<P>　　Java中文问题一直困扰着很多初学者，如果了解了Java系统的中文问题原理，我们就可以对中文问题能够采取根本的解决之道。</P>
<P>　　最古老的解决方案是使用String的字节码转换，这种方案问题是不方便，我们需要破坏对象封装性，进行字节码转换。</P>
<P>　　还有一种方式是对J2EE容器进行编码设置，如果J2EE应用系统脱离该容器，则会发生乱码，而且指定容器配置不符合J2EE应用和容器分离的原则。</P>
<P>　　在Java内部运算中，涉及到的所有字符串都会被转化为UTF-8编码来进行运算。那么，在被Java转化之前，字符串是什么样的字符集？ Java总是根据操作系统的默认编码字符集来决定字符串的初始编码，而且Java系统的输入和输出的都是采取操作系统的默认编码。</P>
<P>　　因此，如果能统一Java系统的输入、输出和操作系统3者的编码字符集合，将能够使Java系统正确处理和显示汉字。这是处理Java系统汉字的一个原则，但是在实际项目中，能够正确抓住和控制住Java系统的输入和输出部分是比较难的。J2EE中，由于涉及到外部浏览器和数据库等，所以中文问题乱码显得非常突出。</P>
<P>　　J2EE应用程序是运行在J2EE容器中。在这个系统中，输入途径有很多种：一种是通过页面表单打包成请求（request）发往服务器的；第二种是通过数据库读入；还有第3种输入比较复杂，JSP在第一次运行时总是被编译成Servlet，JSP中常常包含中文字符，那么编译使用javac时，Java将根据默认的操作系统编码作为初始编码。除非特别指定，如在Jbuilder/eclipse中可以指定默认的字符集。</P>
<P>　　输出途径也有几种：第一种是JSP页面的输出。由于JSP页面已经被编译成Servlet，那么在输出时，也将根据操作系统的默认编码来选择输出编码，除非指定输出编码方式；还有输出途径是数据库，将字符串输出到数据库。</P>
<P>　　由此看来，一个J2EE系统的输入输出是非常复杂，而且是动态变化的，而Java是跨平台运行的，在实际编译和运行中，都可能涉及到不同的操作系统，如果任由Java自由根据操作系统来决定输入输出的编码字符集，这将不可控制地出现乱码。</P>
<P>　　正是由于Java的跨平台特性，使得字符集问题必须由具体系统来统一解决，所以在一个Java应用系统中，<STRONG>解决中文乱码的根本办法是明确指定整个应用系统统一字符集。</STRONG></P>
<P>　　指定统一字符集时，到底是指定ISO8859_1 、GBK还是UTF-8呢？ </P>
<P>　　（1）如统一指定为ISO8859_1，因为目前大多数软件都是西方人编制的，他们默认的字符集就是ISO8859_1，包括操作系统Linux和数据库MySQL等。这样，如果指定Jive统一编码为ISO8859_1，那么就有下面3个环节必须把握：</P>
<P>　　开发和编译代码时指定字符集为ISO8859_1。</P>
<P>　　运行操作系统的默认编码必须是ISO8859_1，如Linux。</P>
<P>　　在JSP头部声明：&lt;%@ page contentType="text/html;charset=ISO8859_1" %&gt;。</P>
<P>　　（2）如果统一指定为GBK中文字符集，上述3个环节同样需要做到，不同的是只能运行在默认编码为GBK的操作系统，如中文Windows。</P>
<P>　　统一编码为ISO8859_1和GBK虽然带来编制代码的方便，但是各自只能在相应的操作系统上运行。但是也破坏了Java跨平台运行的优越性，只在一定范围内行得通。例如，为了使得GBK编码在linux上运行，设置Linux编码为GBK。</P>
<P>　　那么有没有一种除了应用系统以外不需要进行任何附加设置的中文编码根本解决方案呢？</P>
<P>　　将Java/J2EE系统的统一编码定义为UTF-8。UTF-8编码是一种兼容所有语言的编码方式，惟一比较麻烦的就是要找到应用系统的所有出入口，然后使用UTF-8去“结扎”它。</P>
<P>　　一个J2EE应用系统需要做下列几步工作：</P>
<OL>
<LI>开发和编译代码时指定字符集为UTF-8。JBuilder和Eclipse都可以在项目属性中设置。 
<LI>使用过滤器，如果所有请求都经过一个Servlet控制分配器，那么使用Servlet的filter执行语句，将所有来自浏览器的请求（request）转换为UTF-8，因为浏览器发过来的请求包根据浏览器所在的操作系统编码，可能是各种形式编码。关键一句：<BR>request.setCharacterEncoding("UTF-8")。<BR>网上有此filter的源码，<A href="http://sourceforge.net/projects/jdon/" target=_blank>Jdon框架源码</A>中com.jdon.util.SetCharacterEncodingFilter<BR>需要配置web.xml 激活该Filter。 
<LI>在JSP头部声明：&lt;%@ page contentType="text/html;charset= UTF-8" %&gt;。 
<LI>在Jsp的html代码中，声明UTF-8:<BR>&lt;meta http-equiv="Content-Type" content="text/html; charset=utf-8"&gt; 
<LI>设定数据库连接方式是UTF-8。例如连接MYSQL时配置URL如下：<BR>jdbc:mysql://localhost:3306/test?useUnicode=true&amp;amp;characterEncoding=UTF-8<BR>一般数据库都可以通过管理设置设定UTF-8 
<LI>其他和外界交互时能够设定编码时就设定UTF-8，例如读取文件，操作XML等。 </LI></OL>　　　　 笔者以前在Jsp/Servlet时就采取这个原则，后来使用Struts、Tapestry、EJB、Hibernate、Jdon等框架时，从未被乱码困扰过，可以说适合各种架构。希望本方案供更多初学者分享，减少Java/J2EE的第一个拦路虎，也避免因为采取一些临时解决方案，导致中文问题一直出现在新的技术架构中。</SPAN></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/aojilee/aggbug/26723.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-01-05 15:09 <a href="http://www.blogjava.net/aojilee/articles/26723.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>深入Java中文问题及最优解决方法</title><link>http://www.blogjava.net/aojilee/articles/26720.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Thu, 05 Jan 2006 06:47:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/26720.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/26720.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/26720.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/26720.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/26720.html</trackback:ping><description><![CDATA[<TABLE style="TABLE-LAYOUT: fixed; WORD-BREAK: break-all" cellSpacing=0 cellPadding=0 width="96%" border=0>
<TBODY>
<TR>
<TD bgColor=#e8f0f1>深入Java中文问题及最优解决方法--上</TD></TR>
<TR>
<TD>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<DIV align=right></DIV></TD></TR></TBODY></TABLE><SPAN class=oblog_text>&nbsp; 说明：本文为转载，作者联系地址为：<A href="mailto:josserchai@yahoo.com">josserchai@yahoo.com</A>。<BR>由于Java编程中的中文问题是一个老生常谈的问题，在阅读了许多关于Java中文问题解决方法之后，结合作者的编程实践，我发现过去谈的许多方法都不能清晰地说明问题及解决问题，尤其是跨平台时的中文问题。于是我给出此篇文章，内容包括对控制台运行的class、Servelets、JSP及EJB类中的中文问题我剖析和建议解决办法。希望大家指教。 
<P>　　Abstract：本文深入分析了Java程序设计中Java编译器对java源文件和JVM对class类文件的编码/解码过程，通过此过程的解析透视出了Java编程中中文问题产生的根本原因，最后给出了建议的最优化的解决Java中文问题的方法。 </P>
<P>　　1、中文问题的来源 </P>
<P>计算机最初的操作系统支持的编码是单字节的字符编码，于是，在计算机中一切处理程序最初都是以单字节编码的英文为准进行处理。随着计算机的发展，为了适应世界其它民族的语言（当然包括我们的汉字），人们提出了UNICODE编码，它采用双字节编码，兼容英文字符和其它民族的双字节字符编码，所以，目前，大多数国际性的软件内部均采用UNICODE编码，在软件运行时，它获得本地支持系统（多数时间是操作系统）默认支持的编码格式，然后再将软件内部的UNICODE转化为本地系统默认支持的格式显示出来。Java的JDK和JVM即是如此，我这里说的JDK是指国际版的JDK，我们大多数程序员使用的是国际化的JDK版本，以下所有的JDK均指国际化的JDK版本。我们的汉字是双字节编码语言，为了能让计算机处理中文，我们自己制定的gb2312、GBK、GBK2K等标准以适应计算机处理的需求。所以，大部分的操作系统为了适应我们处理中文的需求，均定制有中文操作系统，它们采用的是GBK,GB2312编码格式以正确显示我们的汉字。如：中文Win2K默认采用的是GBK编码显示，在中文WIN2k中保存文件时默认采用的保存文件的编码格式也是GBK的，即，所有在中文WIN2K中保存的文件它的内部编码默认均采用GBK编码，注意：GBK是在GB2312基础上扩充来的。</P>
<P>由于Java语言内部采用UNICODE编码，所以在JAVA程序运行时，就存在着一个从UNICODE编码和对应的操作系统及浏览器支持的编码格式转换输入、输出的问题，这个转换过程有着一系列的步骤，如果其中任何一步出错，则显示出来的汉字就会出是乱码，这就是我们常见的JAVA中文问题。</P>
<P>同时，Java是一个跨平台的编程语言，也即我们编写的程序不仅能在中文windows上运行，也能在中文Linux等系统上运行，同时也要求能在英文等系统上运行（我们经常看到有人把在中文win2k上编写的JAVA程序，移植到英文Linux上运行）。这种移植操作也会带来中文问题。</P>
<P>还有，有人使用英文的操作系统和英文的IE等浏览器，来运行带中文字符的程序和浏览中文网页，它们本身就不支持中文，也会带来中文问题。</P>
<P>几乎所有的浏览器默认在传递参数时都是以UTF-8编码格式来传递，而不是按中文编码传递，所以，传递中文参数时也会有问题，从而带来乱码现象。</P>
<P>总之，以上几个方面是JAVA中的中文问题的主要来源，我们把以上原因造成的程序不能正确运行而产生的问题称作：JAVA中文问题。</P>
<P>　　2、JAVA编码转换的详细过程 </P>
<P>我们常见的JAVA程序包括以下类别：<BR>*直接在console上运行的类(包括可视化界面的类)<BR>*JSP代码类（注：JSP是Servlets类的变型）<BR>*Servelets类<BR>*EJB类<BR>*其它不可以直接运行的支持类</P>
<P>这些类文件中，都有可能含有中文字符串，并且我们常用前三类JAVA程序和用户直接交互，用于输出和输入字符，如：我们在JSP和Servlet中得到客户端送来的字符，这些字符也包括中文字符。无论这些JAVA类的作用如何，这些JAVA程序的生命周期都是这样的：</P>
<P>*编程人员在一定的操作系统上选择一个合适的编辑软件来实现源程序代码并以.java扩展名保存在操作系统中，例如我们在中文win2k中用记事本编辑一个java源程序；<BR>*编程人员用JDK中的javac.exe来编译这些源代码，形成.class类(JSP文件是由容器调用JDK来编译的)；<BR>*直接运行这些类或将这些类布署到WEB容器中去运行，并输出结果。<BR>那么，在这些过程中，JDK和JVM是如何将这些文件如何编码和解码并运行的呢？<BR>这里，我们以中文win2k操作系统为例说明JAVA类是如何来编码和被解码的。</P>
<P>第一步，我们在中文win2k中用编辑软件如记事本编写一个Java源程序文件(包括以上五类JAVA程序)，程序文件在保存时默认采用了操作系统默认支持GBK编码格式(操作系统默认支持的格式为file.encoding格式)形成了一个.java文件，也即，java程序在被编译前，我们的JAVA源程序文件是采用操作系统默认支持的file.encoding编码格式保存的，java源程序中含有中文信息字符和英文程序代码；要查看系统的file.encoding参数，可以用以下代码：<BR>　　 public class ShowSystemDefaultEncoding {<BR>　　 public static void main(String[] args) {<BR>　　 String encoding = System.getProperty("file.encoding");<BR>　　 System.out.println(encoding);<BR>　　 }}</P>
<P>第二步，我们用JDK的javac.exe文件编译我们的Java源程序，由于JDK是国际版的，在编译的时候，如果我们没有用-encoding参数指定我们的JAVA源程序的编码格式，则javac.exe首先获得我们操作系统默认采用的编码格式，也即在编译java程序时，若我们不指定源程序文件的编码格式，JDK首先获得操作系统的file.encoding参数(它保存的就是操作系统默认的编码格式，如WIN2k，它的值为GBK)，然后JDK就把我们的java源程序从file.encoding编码格式转化为JAVA内部默认的UNICODE格式放入内存中。然后，javac把转换后的unicode格式的文件进行编译成.class类文件，此时.class文件是UNICODE编码的，它暂放在内存中，紧接着，JDK将此以UNICODE编码的编译后的class文件保存到我们的操作系统中形成我们见到的.class文件。对我们来说，我们最终获得的.class文件是内容以UNICODE编码格式保存的类文件，它内部包含我们源程序中的中文字符串，只不过此时它己经由file.encoding格式转化为UNICODE格式了。</P>
<P>这一步中，对于JSP源程序文件是不同的，对于JSP，这个过程是这样的：即WEB容器调用JSP编译器，JSP编译器先查看JSP文件中是否设置有文件编码格式，如果JSP文件中没有设置JSP文件的编码格式，则JSP编译器调用JDK先把JSP文件用JVM默认的字符编码格式(也即WEB容器所在的操作系统的默认的file.encoding)转化为临时的Servlet类，然后再把它编译成UNICODE格式的class类，并保存在临时文件夹中。如：在中文win2k上，WEB容器就把JSP文件从GBK编码格式转化为UNICODE格式，然后编译成临时保存的Servlet类，以响应用户的请求。</P>
<P>第三步，运行第二步编译出来的类，分为三种情况：</P>
<P>A、 直接在console上运行的类<BR>B、 EJB类和不可以直接运行的支持类(如JavaBean类)<BR>C、 JSP代码和Servlet类<BR>D、 JAVA程序和数据库之间<BR>下面我们分这四种情况来看。<BR>A、直接在console上运行的类</P>
<P>这种情况，运行该类首先需要JVM支持，即操作系统中必须安装有JRE。运行过程是这样的：首先java启动JVM，此时JVM读出操作系统中保存的class文件并把内容读入内存中，此时内存中为UNICODE格式的class类，然后JVM运行它，如果此时此类需要接收用户输入，则类会默认用file.encoding编码格式对用户输入的串进行编码并转化为unicode保存入内存（用户可以设置输入流的编码格式）。程序运行后，产生的字符串（UNICODE编码的）再回交给JVM，最后JRE把此字符串再转化为file.encoding格式(用户可以设置输出流的编码格式)传递给操作系统显示接口并输出到界面上。</P>
<P>对于这种直接在console上运行的类，它的转化过程可用图1更加明确的表示出来：</P>
<P>图1<BR>&nbsp;<IMG height=387 alt=6616_1.gif src="http://www.blogjava.net/images/blogjava_net/aojilee/article_image/6616_1.gif" width=462 border=0></P>
<P>以上每一步的转化都需要正确的编码格式转化，才能最终不出现乱码现象。</P>
<P>B、EJB类和不可以直接运行的支持类(如JavaBean类)</P>
<P>由于EJB类和不可以直接运行的支持类，它们一般不与用户直接交互输入和输出，它们常常与其它的类进行交互输入和输出，所以它们在第二步被编译后，就形成了内容是UNICODE编码的类保存在操作系统中了，以后只要它与其它的类之间的交互在参数传递过程中没有丢失，则它就会正确的运行。<BR>这种EJB类和不可以直接运行的支持类, 它的转化过程可用图2更加明确的表示出来：</P>
<P>图2<BR>&nbsp;<IMG height=246 alt=6616_2.gif src="http://www.blogjava.net/images/blogjava_net/aojilee/article_image/6616_2.gif" width=443 border=0></P>
<P>C、JSP代码和Servlet类</P>
<P>经过第二步后，JSP文件也被转化为Servlets类文件，只不过它不像标准的Servlets一校存在于classes目录中，它存在于WEB容器的临时目录中，故这一步中我们也把它做为Servlets来看。</P>
<P>对于Servlets，客户端请求它时，WEB容器调用它的JVM来运行Servlet，首先，JVM把Servlet的class类从系统中读出并装入内存中，内存中是以UNICODE编码的Servlet类的代码，然后JVM在内存中运行该Servlet类，如果Servlet在运行的过程中，需要接受从客户端传来的字符如：表单输入的值和URL中传入的值，此时如果程序中没有设定接受参数时采用的编码格式，则WEB容器会默认采用ISO-8859-1编码格式来接受传入的值并在JVM中转化为UNICODE格式的保存在WEB容器的内存中。Servlet运行后生成输出，输出的字符串是UNICODE格式的，紧接着，容器将Servlet运行产生的UNICODE格式的串（如html语法，用户输出的串等）直接发送到客户端浏览器上并输出给用户，如果此时指定了发送时输出的编码格式，则按指定的编码格式输出到浏览器上，如果没有指定，则默认按ISO-8859-1编码发送到客户的浏览器上。这种JSP代码和Servlet类，它的转化过程可用图3更加明确地表示出来：</P>
<P>图3<BR>&nbsp;<IMG height=431 alt=6616_3.gif src="http://www.blogjava.net/images/blogjava_net/aojilee/article_image/6616_3.gif" width=422 border=0></P>
<P>D、Java程序和数据库之间</P>
<P>对于几乎所有数据库的JDBC驱动程序，默认的在JAVA程序和数据库之间传递数据都是以ISO-8859-1为默认编码格式的，所以，我们的程序在向数据库内存储包含中文的数据时，JDBC首先是把程序内部的UNICODE编码格式的数据转化为ISO-8859-1的格式，然后传递到数据库中，在数据库保存数据时，它默认即以ISO-8859-1保存，所以，这是为什么我们常常在数据库中读出的中文数据是乱码。<BR>对于JAVA程序和数据库之间的数据传递，我们可以用图4清晰地表示出来</P>
<P>图4<BR>&nbsp;<IMG height=167 alt=6616_4.gif src="http://www.blogjava.net/images/blogjava_net/aojilee/article_image/6616_4.gif" width=467 border=0></P>
<P>3、分析常见的JAVA中文问题几个必须清楚的原则</P>
<P>首先，经过上面的详细分析，我们可以清晰地看到，任何JAVA程序的生命期中，其编码转换的关键过程是在于：最初编译成class文件的转码和最终向用户输出的转码过程。<BR>其次，我们必须了解JAVA在编译时支持的、常用的编码格式有以下几种：<BR>*ISO-8859-1，8-bit, 同8859_1,ISO-8859-1,ISO_8859_1等编码<BR>*Cp1252，美国英语编码，同ANSI标准编码<BR>*UTF-8，同unicode编码<BR>*GB2312，同gb2312-80,gb2312-1980等编码<BR>*GBK , 同MS936，它是gb2312的扩充<BR>及其它的编码，如韩文、日文、繁体中文等。同时，我们要注意这些编码间的兼容关体系如下：<BR>unicode和UTF-8编码是一一对应的关系。GB2312可以认为是GBK的子集，即GBK编码是在gb2312上扩展来的。同时，GBK编码包含了20902个汉字，编码范围为：0x8140-0xfefe，所有的字符可以一一对应到UNICODE2.0中来。</P>
<P>再次，对于放在操作系统中的.java源程序文件，在编译时，我们可以指定它内容的编码格式，具体来说用-encoding来指定。注意：如果源程序中含有中文字符，而你用-encoding指定为其它的编码字符，显然是要出错的。用-encoding指定源文件的编码方式为GBK或gb2312，无论我们在什么系统上编译含有中文字符的JAVA源程序都不会有问题，它都会正确地将中文转化为UNICODE存储在class文件中。<BR><BR>然后，我们必须清楚，几乎所有的WEB容器在其内部默认的字符编码格式都是以ISO-8859-1为默认值的，同时，几乎所有的浏览器在传递参数时都是默认以UTF-8的方式来传递参数的。所以，虽然我们的Java源文件在出入口的地方指定了正确的编码方式，但其在容器内部运行时还是以ISO-8859-1来处理的。<BR><BR>4、中文问题的分类及其建议最优解决办法 </P>
<P>了解以上JAVA处理文件的原理之后，我们就可以提出了一套建议最优的解决汉字问题的办法。<BR>我们的目标是：我们在中文系统中编辑的含有中文字符串或进行中文处理的JAVA源程序经编译后可以移值到任何其它的操作系统中正确运行，或拿到其它操作系统中编译后能正确运行，能正确地传递中文和英文参数，能正确地和数据库交流中英文字符串。<BR>我们的具体思路是：在JAVA程序转码的入口和出口及JAVA程序同用户有输入输出转换的地方限制编码方法使之正确即可。</P>
<P>具体解决办法如下：</P>
<P>1、 针对直接在console上运行的类<BR>对于这种情况，我们建议在程序编写时，如果需要从用户端接收用户的可能含有中文的输入或含有中文的输出，程序中应该采用字符流来处理输入和输出，具体来说，应用以下面向字符型节点流类型：<BR>对文件：FileReader，FileWrieter <BR>其字节型节点流类型为：FileInputStream，FileOutputStream<BR>对内存（数组）：CharArrayReader，CharArrayWriter<BR>其字节型节点流类型为：ByteArrayInputStream，ByteArrayOutputStream<BR>对内存（字符串）：StringReader，StringWriter<BR>对管道：PipedReader，PipedWriter<BR>其字节型节点流类型为：PipedInputStream，PipedOutputStream<BR>同时，应该用以下面向字符型处理流来处理输入和输出：<BR>BufferedWriter，BufferedReader<BR>其字节型的处理流为：BufferedInputeStream，BufferedOutputStream<BR>InputStreamReader，OutputStreamWriter<BR>其字节型的处理流为：DataInputStream，DataOutputStream<BR>其中InputStreamReader和InputStreamWriter用于将字节流按照指定的字符编码集转换到字符流，如：<BR>InputStreamReader in = new InputStreamReader(System.in，"GB2312")；<BR>OutputStreamWriter out = new OutputStreamWriter (System.out，"GB2312")；<BR>例如：采用如下的示例JAVA编码就达到了要求：</P>
<P>//Read.java<BR>import java.io.*;<BR>public class Read {<BR>public static void main(String[] args) throws IOException {<BR>String str = "n中文测试，这是内部硬编码的串"+"ntest english character";<BR>String strin= "";<BR>BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in,"gb2312")); //设置输入接口按中文编码<BR>BufferedWriter stdout = new BufferedWriter(new OutputStreamWriter(System.out,"gb2312")); //设置输出接口按中文编码<BR>stdout.write("请输入:");<BR>stdout.flush();<BR>strin = stdin.readLine();<BR>stdout.write("这是从用户输入的串："+strin);<BR>stdout.write(str);<BR>stdout.flush();<BR>}}<BR>同时，在编译程序时，我们用以下方式来进行：<BR>javac -encoding gb2312 Read.java<BR>其运行结果如图5所示：<BR>&nbsp;<IMG height=185 alt=6617_1.gif src="http://www.blogjava.net/images/blogjava_net/aojilee/article_image/6617_1.gif" width=424 border=0></P>
<P>图5</P>
<P></P>
<P>2、 针对EJB类和不可以直接运行的支持类(如JavaBean类) </P>
<P>由于这种类它们本身被其它的类调用，不直接与用户交互，故对这种类来说，我们的建议的处理方式是内部程序中应该采用字符流来处理程序内部的中文字符串（具体如上面一节中一样），同时，在编译类时用-encoding gb2312参数指示源文件是中文格式编码的即可。<BR></P>
<P>3、 针对Servlet类</P>
<P>针对Servlet，我们建议用以下方法：</P>
<P>在编译Servlet类的源程序时，用-encoding指定编码为GBK或GB2312，且在向用户输出时的编码部分用response对象的setContentType("text/html;charset=GBK");或gb2312来设置输出编码格式，同样在接收用户输入时，我们用request.setCharacterEncoding("GB2312")；这样无论我们的servlet类移植到什么操作系统中，只有客户端的浏览器支持中文显示，就可以正确显示。如下是一个正确的示例：</P>
<P>//HelloWorld.java<BR>package hello;<BR>import java.io.*;<BR>import javax.servlet.*;<BR>import javax.servlet.http.*;<BR>public class HelloWorld extends HttpServlet<BR>{<BR>public void init() throws ServletException { }<BR>public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException<BR>{<BR>request.setCharacterEncoding("GB2312"); //设置输入编码格式<BR>response.setContentType("text/html;charset=GB2312"); //设置输出编码格式<BR>PrintWriter out = response.getWriter(); //建议使用PrintWriter输出<BR>out.println("&lt;hr&gt;");<BR>out.println("Hello World! This is created by Servlet!测试中文!");<BR>out.println("&lt;hr&gt;");<BR>}<BR>public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException<BR>{<BR>request.setCharacterEncoding("GB2312"); //设置输入编码格式<BR>response.setContentType("text/html;charset=GB2312"); //设置输出编码格式<BR>String name = request.getParameter("name");<BR>String id = request.getParameter("id");<BR>if(name==null) name="";<BR>if(id==null) id="";<BR>PrintWriter out = response.getWriter(); //建议使用PrintWriter输出<BR>out.println("&lt;hr&gt;");<BR>out.println("你传入的中文字串是：" + name);<BR>out.println("&lt;hr&gt;你输入的id是：" + id);<BR>out.println("&lt;hr&gt;");<BR>}<BR>public void destroy() { }<BR>}<BR>请用javac -encoding gb2312 HelloWorld.java来编译此程序。<BR>测试此Servlet的程序如下所示：<BR>&lt;%@page contentType="text/html; charset=gb2312"%&gt;<BR>&lt;%request.setCharacterEncoding("GB2312");%&gt;<BR>&lt;html&gt;&lt;head&gt;&lt;title&gt;&lt;/title&gt;<BR>&lt;Script language="JavaScript"&gt;<BR>function Submit() {<BR>//通过URL传递中文字符串值给Servlet<BR>document.base.action = "./HelloWorld?name=中文";<BR>document.base.method = "POST";<BR>document.base.submit();<BR>}<BR>&lt;/Script&gt;<BR>&lt;/head&gt;</P>
<P></P>
<P>&lt;body bgcolor="#FFFFFF" text="#000000" topmargin="5"&gt;<BR>&lt;form name="base" method = "POST" target="_self"&gt;<BR>&lt;input name="id" type="text" value="" size="30"&gt;<BR>&lt;a href = "JavaScript:Submit()"&gt;传给Servlet&lt;/a&gt;<BR>&lt;/form&gt;&lt;/body&gt;&lt;/html&gt;<BR>其运行结果如图6所示：<BR><IMG height=328 alt=6617_2.gif src="http://www.blogjava.net/images/blogjava_net/aojilee/article_image/6617_2.gif" width=407 border=0><BR></P>
<P>图6<BR>4、 JAVA程序和数据库之间</P>
<P>为避免JAVA程序和数据库之间数据传递出现乱码现象，我们建议采用以下最优方法来处理：<BR>1、 对于JAVA程序的处理方法按我们指定的方法处理。<BR>2、 把数据库默认支持的编码格式改为GBK或GB2312的。</P>
<P>如：在mysql中，我们可以在配置文件my.ini中加入以下语句实现：<BR>在[mysqld]区增加：<BR>default-character-set=gbk<BR>并增加：<BR>[client]<BR>default-character-set=gbk<BR>在SQL Server2K中，我们可以将数据库默认的语言设置为Simplified Chinese来达到目的。</P>
<P>5、 针对JSP代码</P>
<P>由于JSP是在运行时，由WEB容器进行动态编译的，如果我们没有指定JSP源文件的编码格式，则JSP编译器会获得服务器操作系统的file.encoding值来对JSP文件编译的，它在移植时最容易出问题，如在中文win2k中可以很好运行的jsp文件拿到英文linux中就不行，尽管客户端都是一样的，那是因为容器在编译JSP文件时获取的操作系统的编码不同造成的（在中文wink中的file.encoding和在英文Linux中file.encoding是不同的，且英文Linux的file.encoding对中文不支持，所以编译出来的JSP类就会有问题）。网络上讨论的大多数是此类问题，多是因为JSP文件移植平台时不能正确显示的问题，对于这类问题，我们了解了JAVA中程序编码转换的原理，解决起来就容易多了。我们建议的解决办法如下：</P>
<P>1、我们要保证JSP向客户端输出时是采用中文编码方式输出的，即无论如何我们首先在我们的JSP源代编中加入以下一行：</P>
<P>&lt;%@page contentType="text/html; charset=gb2312"%&gt;<BR>2、为了让JSP能正确获得传入的参数，我们在JSP源文件头加入下面一句：<BR>&lt;%request.setCharacterEncoding("GB2312");%&gt;<BR>3、为了让JSP编译器能正确地解码我们的含有中文字符的JSP文件，我们需要在JSP源文件中指定我们的JSP源文件的编码格式，具体来说，我们在JSP源文件头上加入下面的一句即可：<BR>&lt;%@page pageEncoding="GB2312"%&gt;或&lt;%@page pageEncoding="GBK"%&gt;<BR>这是JSP规范2.0新增加的指令。<BR>我们建议使用此方法来解JSP文件中的中文问题，下面的代码是一个正确做法的JSP文件的测试程序：</P>
<P></P>
<P>//testchinese.jsp<BR>&lt;%@page pageEncoding="GB2312"%&gt;<BR>&lt;%@page contentType="text/html; charset=gb2312"%&gt;<BR>&lt;%request.setCharacterEncoding("GB2312");%&gt;<BR>&lt;%<BR>String action = request.getParameter("ACTION");<BR>String name = "";<BR>String str = "";<BR>if(action!=null &amp;&amp; action.equals("SENT"))<BR>{<BR>name = request.getParameter("name");<BR>str = request.getParameter("str");<BR>}<BR>%&gt;<BR>&lt;html&gt;<BR>&lt;head&gt;<BR>&lt;title&gt;&lt;/title&gt;<BR>&lt;Script language="JavaScript"&gt;<BR>function Submit()<BR>{<BR>document.base.action = "?ACTION=SENT&amp;str=传入的中文";<BR>document.base.method = "POST";<BR>document.base.submit();<BR>}<BR>&lt;/Script&gt;<BR>&lt;/head&gt;<BR>&lt;body bgcolor="#FFFFFF" text="#000000" topmargin="5"&gt;<BR>&lt;form name="base" method = "POST" target="_self"&gt;<BR>&lt;input type="text" name="name" value="" size="30"&gt;<BR>&lt;a href = "JavaScript:Submit()"&gt;提交&lt;/a&gt;<BR>&lt;/form&gt;<BR>&lt;%<BR>if(action!=null &amp;&amp; action.equals("SENT"))<BR>{<BR>out.println("&lt;br&gt;你输入的字符为："+name);<BR>out.println("&lt;br&gt;你通过URL传入的字符为："+str);<BR>}<BR>%&gt;<BR>&lt;/body&gt;<BR>&lt;/html&gt;<BR>如图7是此程序运行的结果示意图：<BR></P>
<P><IMG height=351 alt=6617_3.gif src="http://www.blogjava.net/images/blogjava_net/aojilee/article_image/6617_3.gif" width=506 border=0></P>
<P>图7</P>
<P>5、总结</P>
<P>在上面的详细分析中，我们清晰地给出了JAVA在处理源程序过程中的详细转换过程，为我们正确解决JAVA编程中的中文问题提供了基础。同时，我们给出了认为是最优的解决JAVA中文问题的办法。</P>
<P>6、参考资料<BR>1、段明辉.Java 编程技术中汉字问题的分析及解决.<BR>http://www-900.ibm.com/developerWorks/cn/java/java_chinese/index.shtml<BR>2、 周竞涛.关于Java中文问题的几条分析原则<BR>http://www-900.ibm.com/developerWorks/cn/java/l-javachinese/index.shtml<BR></P>
<P>作者介绍：abnerchai，高级程序员，联系方法：<A href="mailto:josserchai@yahoo.com">josserchai@yahoo.com</A></P></SPAN></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/aojilee/aggbug/26720.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-01-05 14:47 <a href="http://www.blogjava.net/aojilee/articles/26720.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>servlet 和jsp 过滤器</title><link>http://www.blogjava.net/aojilee/articles/26715.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Thu, 05 Jan 2006 06:32:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/26715.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/26715.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/26715.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/26715.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/26715.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 或许，servlet&nbsp;API的2.3版本中最重要的一个新功能就是能够为servlet和JSP页面定义过滤器。过滤器提供了某些早期服务器所支持的非标准“servlet链接”的一种功能强大且标准的替代品。过滤器是一个程序，它先于与之相关的servlet或JSP页面运行在服务器上。过滤器可附加到一个或多个servlet或JSP页面上，并且可以检查进入这些资源的请求信息。在这之后，过滤器可以作如...&nbsp;&nbsp;<a href='http://www.blogjava.net/aojilee/articles/26715.html'>阅读全文</a><img src ="http://www.blogjava.net/aojilee/aggbug/26715.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-01-05 14:32 <a href="http://www.blogjava.net/aojilee/articles/26715.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Servlet过滤器(filter)</title><link>http://www.blogjava.net/aojilee/articles/26713.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Thu, 05 Jan 2006 06:13:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/26713.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/26713.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/26713.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/26713.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/26713.html</trackback:ping><description><![CDATA[<STRONG>什么是Servlet过滤器(filter)？</STRONG> <BR>Servlet 过滤器是小型的 Web 组件，可以链在Servlet容器的处理过程中，拦截请求和响应，检查和修在客户机和Web应用程序之间交换的数据。这意味着过滤器会在Servlet处理之前访问一个进入的请求，并在外发的响应回到客户前访问这些信息。 <BR>过滤器可以被添加到请求/响应链中，或者在无需影响应用程序中其他 Web 组件的情况下删除它们。过滤器仅只是改动请求和响应的运行时处理，因而不应该将它们直接嵌入 Web 应用程序框架。 <BR>Servlet可以与一个或者多个过滤器相关联，后者将形成一个过滤器链。 <BR><BR><B>编写一个 Servlet 过滤器</B> <BR>实现一个 Servlet 过滤器需要三个步骤：首先要编写过滤器实现类的程序，然后要把该过滤器添加到 Web 应用程序中（通过在 Web 部署描述符 web.xml 中声明它），最后要把过滤器与应用程序一起打包部署。 <BR><BR><B>1. 编写实现类的程序 <BR></B>过滤器 API 一共包含 3 个简单的接口：Filter、FilterChain 和 FilterConfig。过滤器类必须需要实现 Filter 接口： <BR><BR>init()：这个方法在容器实例化过滤器时被调用，它主要设计用于使过滤器为处理做准备。容器为这个方法传递一个FilterConfig对象，其中包含着配置信息。 <BR>doFilter()：过滤器拥有单个用于处理请求和响应的方法——doFilter()。这个方法接受三个输入参数：一个 ServletRequest、response 和一个 FilterChain 对象。FilterChain对于正确的过滤操作至关重要。doFilter()方法必须调用FilterChain的doFilter()方法，除非该方法用来拦截以后的下游处理。注意：过滤器的一个实例可以同时服务于多个请求，意味着任何共享的变量都必须通过同步块(synchronized block)来访问。 <BR>destroy()：该方法由容器在销毁过滤器实例之前调用。 <BR>例1 演示了一个简单的过滤器，用来计算一个客户机的 Web 请求所花的大致时间。 <BR>
<TABLE class=java cellSpacing=1 cellPadding=3 bgColor=#999999 border=0>
<TBODY>
<TR>
<TD vAlign=top align=left width=1 bgColor=#dddddd><PRE><FONT color=#555555>1<BR>2<BR>3<BR>4<BR>5<BR>6<BR>7<BR>8<BR>9<BR>10<BR>11<BR>12<BR>13<BR>14<BR>15<BR>16<BR>17<BR>18<BR>19<BR>20<BR>21<BR>22<BR>23<BR>24<BR>25<BR>26<BR>27<BR>28<BR>29<BR>30<BR>31<BR>32<BR>33<BR>34<BR>35<BR>36<BR>37<BR>38<BR>39<BR>40<BR>41<BR>42<BR></FONT></PRE></TD>
<TD vAlign=top align=left bgColor=#ffffff><PRE><FONT class=java-reserved_word><B>package</B></FONT> cc.ejb.examples;
<FONT class=java-reserved_word><B>import</B></FONT> java.io.IOException;
<FONT class=java-reserved_word><B>import</B></FONT> java.io.StringWriter;
<FONT class=java-reserved_word><B>import</B></FONT> java.io.PrintWriter;
<FONT class=java-reserved_word><B>import</B></FONT> java.util.Date;
<FONT class=java-reserved_word><B>import</B></FONT> javax.servlet.Filter;
<FONT class=java-reserved_word><B>import</B></FONT> javax.servlet.FilterChain;
<FONT class=java-reserved_word><B>import</B></FONT> javax.servlet.FilterConfig;
<FONT class=java-reserved_word><B>import</B></FONT> javax.servlet.ServletException;
<FONT class=java-reserved_word><B>import</B></FONT> javax.servlet.ServletRequest;
<FONT class=java-reserved_word><B>import</B></FONT> javax.servlet.ServletResponse;
<FONT class=java-reserved_word><B>public</B></FONT> <FONT class=java-reserved_word><B>class</B></FONT> PageTimerFilter <FONT class=java-reserved_word><B>implements</B></FONT> Filter 
<FONT class=java-bracket>{</FONT>
 <FONT class=java-reserved_word><B>private</B></FONT> FilterConfig config = <FONT class=java-reserved_word><B>null</B></FONT>;
 <FONT class=java-reserved_word><B>public</B></FONT> <FONT class=java-reserved_word><B>void</B></FONT> init(FilterConfig config) <FONT class=java-reserved_word><B>throws</B></FONT> ServletException 
 <FONT class=java-bracket>{</FONT>  
  this.config = config;
 <FONT class=java-bracket>}</FONT>
 <FONT class=java-reserved_word><B>public</B></FONT> <FONT class=java-reserved_word><B>void</B></FONT> doFilter(ServletRequest request, ServletResponse response,FilterChain chain) <FONT class=java-reserved_word><B>throws</B></FONT> IOException, ServletException
 <FONT class=java-bracket>{</FONT>
  Date startTime, endTime;
  <FONT class=java-reserved_word><B>double</B></FONT> duration;
  startTime = <FONT class=java-reserved_word><B>new</B></FONT> Date();
  <FONT class=java-comment>// Forward the request to the next resource in the chain</FONT>
  chain.doFilter(request, response);
  <FONT class=java-comment>// Calculate the duration between the start time and end time</FONT>
  endTime = <FONT class=java-reserved_word><B>new</B></FONT> Date();
  duration = (endTime.getTime() - startTime.getTime())/1000;<FONT class=java-comment>//Convert from milliseconds to seconds</FONT>
  StringWriter sw = <FONT class=java-reserved_word><B>new</B></FONT> StringWriter();
  PrintWriter writer = <FONT class=java-reserved_word><B>new</B></FONT> PrintWriter(sw);
  writer.println();
  writer.println(<FONT class=java-string>"==============="</FONT>);
  writer.println(<FONT class=java-string>"Total elapsed time is: "</FONT> + duration + <FONT class=java-string>" seconds."</FONT>);
  writer.println(<FONT class=java-string>"==============="</FONT>);
  <FONT class=java-comment>// Log the resulting string</FONT>
  writer.flush();
  config.getServletContext().log(sw.getBuffer().toString());
 <FONT class=java-bracket>}</FONT>
 <FONT class=java-reserved_word><B>public</B></FONT> <FONT class=java-reserved_word><B>void</B></FONT> destroy() <FONT class=java-bracket>{</FONT>
  this.config=<FONT class=java-reserved_word><B>null</B></FONT>;
 <FONT class=java-bracket>}</FONT>
<FONT class=java-bracket>}</FONT>
</PRE></TD></TR></TBODY></TABLE><BR>在doFilter()方法实现中，出现在FilterChain的doFilter()方法调用之前的代码都被看成是预处理，Web资源（包括其他过滤器、Servlet等等）所做的处理还没有发生。而在该方法之后的代码则是后期处理，这时外发的响应信息已经包含了Web资源的完整响应。也就是说，<B>FilterChain的doFilter()将调用接下来的过滤器（在有链式关系的时候）或者其他Web资源。</B> <BR><BR><B>2. 配置 Servlet 过滤器</B>和配置Servlet类似，过滤器通过 web.xml 文件中的两个 XML 标签&lt;filter&gt;和&lt;filter-name&gt;来声明。&lt;filter&gt;标签负责把一个过滤器名和一个特定的类关联起来，这种关联是通过&lt;filter-name&gt;和&lt;filter-class&gt;元素指定。其 DTD定义如下： <BR>
<TABLE class=java cellSpacing=1 cellPadding=3 bgColor=#999999 border=0>
<TBODY>
<TR>
<TD vAlign=top align=left width=1 bgColor=#dddddd><PRE><FONT color=#555555>1<BR></FONT></PRE></TD>
<TD vAlign=top align=left bgColor=#ffffff><PRE>(((description*,display-name*,icon*)),filter-name,filter-class,init-param*)
</PRE></TD></TR></TBODY></TABLE><BR>可以为过滤器指定初始化参数，和Servlet类似，参数是使用&lt;inti-param&gt;和成对的&lt;param-name&gt;和&lt;param-value&gt;来指定的，如下所示： <BR>
<TABLE class=java cellSpacing=1 cellPadding=3 bgColor=#999999 border=0>
<TBODY>
<TR>
<TD vAlign=top align=left width=1 bgColor=#dddddd><PRE><FONT color=#555555>1<BR>2<BR>3<BR>4<BR></FONT></PRE></TD>
<TD vAlign=top align=left bgColor=#ffffff><PRE>&lt;init-param&gt;
    &lt;param-name&gt;counter&lt;/param-name&gt;
       &lt;param-value&gt;100&lt;/param-value&gt;
&lt;/init-param&gt;
</PRE></TD></TR></TBODY></TABLE><BR>例 2 显示了 web.xml 文件，它展示了如何声明过滤器的包含关系： <BR>
<TABLE class=java cellSpacing=1 cellPadding=3 bgColor=#999999 border=0>
<TBODY>
<TR>
<TD vAlign=top align=left width=1 bgColor=#dddddd><PRE><FONT color=#555555>1<BR>2<BR>3<BR>4<BR>5<BR>6<BR>7<BR>8<BR></FONT></PRE></TD>
<TD vAlign=top align=left bgColor=#ffffff><PRE> &lt;filter&gt;
  &lt;filter-name&gt;Page Timers&lt;/filter-name&gt;
  &lt;filter-class&gt;cc.ejb.examples.PageTimerFilter&lt;/filter-class&gt;
 &lt;/filter&gt;
 &lt;filter-mapping&gt;
  &lt;filter-name&gt;Page Timers&lt;/filter-name&gt;
  &lt;url-pattern&gt;<FONT class=java-comment>/*&lt;/url-pattern&gt;
 &lt;/filter-mapping&gt;
</FONT></PRE></TD></TR></TBODY></TABLE><BR>&lt;filter&gt;必须有一个&lt;ulr-pattern&gt;或者&lt;servlet-name&gt;元素。我们可以通过&lt;ulr-pattern&gt;来指定通配符，将过滤器应用到Web资源范围，在上面的例子中，Page Timer过滤器被应用到每个Web资源。也可以通过&lt;servlet-name&gt;将过滤器指定到某一个特定的Servlet上。应该注意这些声明的顺序，所引用的过滤器名必须在前面的过滤器定义给出。 <BR><BR><B>3. 部署 Servlet 过滤器</B>事实上， 部署过滤器是非常简单的事情。只需把过滤器类和其他 Web 组件类包括在一起，并像通常所做的那样把 web.xml 文件（连同过滤器定义和过滤器映射声明）放进 Web 应用程序结构中，servlet 容器将处理之后的其他所有事情。 <BR><BR><B>Servlet 2.4中的新特性</B> <BR>我们通过下面的例子来研究Servlet 2.4的新特性。例3和例4是两个很简单的程序片断，JSP程序将来自客户的请求forward到Thank.html。 <BR>
<TABLE class=java cellSpacing=1 cellPadding=3 bgColor=#999999 border=0>
<TBODY>
<TR>
<TD vAlign=top align=left width=1 bgColor=#dddddd><PRE><FONT color=#555555>1<BR>2<BR>3<BR>4<BR>5<BR>6<BR></FONT></PRE></TD>
<TD vAlign=top align=left bgColor=#ffffff><PRE>&lt;%@ page language=<FONT class=java-string>"java"</FONT> %&gt;
&lt;html&gt;  
&lt;body&gt;
&lt;jsp:forward page=<FONT class=java-string>"/Thank.html"</FONT>/&gt;
&lt;/body&gt;
&lt;/html&gt;
</PRE></TD></TR></TBODY></TABLE><BR>
<TABLE class=java cellSpacing=1 cellPadding=3 bgColor=#999999 border=0>
<TBODY>
<TR>
<TD vAlign=top align=left width=1 bgColor=#dddddd><PRE><FONT color=#555555>1<BR>2<BR>3<BR>4<BR>5<BR></FONT></PRE></TD>
<TD vAlign=top align=left bgColor=#ffffff><PRE>&lt;html&gt;
  &lt;body&gt;
    Thank you <FONT class=java-reserved_word><B>for</B></FONT> coming Filter worlds&lt;br&gt;
  &lt;/body&gt;
&lt;/html&gt;
</PRE></TD></TR></TBODY></TABLE><BR>将这两个程序和上面的过滤器一起打包部署并运行Test.jsp，我们发现控制台只有一行而不是两行输出： <BR>
<TABLE class=java cellSpacing=1 cellPadding=3 bgColor=#999999 border=0>
<TBODY>
<TR>
<TD vAlign=top align=left width=1 bgColor=#dddddd><PRE><FONT color=#555555>1<BR>2<BR>3<BR>4<BR></FONT></PRE></TD>
<TD vAlign=top align=left bgColor=#ffffff><PRE>11:06:57,045 INFO  [Engine] StandardContext[/Test]
===============
Total elapsed time is: 0.0 seconds.
===============
</PRE></TD></TR></TBODY></TABLE><BR>因为，在Servlet 2.3 规范中的过滤器只能过滤 Web 客户机和其所访问的指定 Web 资源之间的内容。如果该资源然将请求调度给其他 Web 资源（这里是Thank.html），就不能向幕后委托的任何请求应用过滤器。 <BR><BR>2.4 规范消除了这个限制，通过增强filter和request dispatcher的配合，过滤器可以根据请求分发器(request dispatcher)所使用的方法有条件地对Web请求进行过滤。该功能是通过中的元素来实现的： <BR><BR>只有当request直接来自客户，过滤器才生效，对应为REQUEST条件。 <BR>只有当request被一个请求分发器使用forward()方法转到一个Web构件时(采用或定义)，对应称为FORWARD条件。 <BR>类似地，只有当request被一个请求分发器使用include()方法转到一个Web构件时(采用或定义)，对应称为INCLUDE条件。 <BR>只有当request被一个请求分发器使用“错误信息页”机制方法转到一个Web构件时，对应称为ERROR条件。 <BR>以上四种条件的组合使用。 <BR>修改之后web.xml的如下所示： <BR>
<TABLE class=java cellSpacing=1 cellPadding=3 bgColor=#999999 border=0>
<TBODY>
<TR>
<TD vAlign=top align=left width=1 bgColor=#dddddd><PRE><FONT color=#555555>1<BR>2<BR>3<BR>4<BR>5<BR>6<BR>7<BR>8<BR>9<BR>10<BR></FONT></PRE></TD>
<TD vAlign=top align=left bgColor=#ffffff><PRE> &lt;filter&gt;
  &lt;filter-name&gt;Page Timers&lt;/filter-name&gt;
  &lt;filter-class&gt;cc.ejb.examples.PageTimerFilter&lt;/filter-class&gt;
 &lt;/filter&gt;
 &lt;filter-mapping&gt;
  &lt;filter-name&gt;Page Timers&lt;/filter-name&gt;
  &lt;url-pattern&gt;<FONT class=java-comment>/*&lt;/url-pattern&gt;
  &lt;dispatcher&gt;REQUEST&lt;/dispatcher&gt;
  &lt;dispatcher&gt;FORWARD&lt;/dispatcher&gt;
 &lt;/filter-mapping&gt;
</FONT></PRE></TD></TR></TBODY></TABLE><BR>再次部署运行这个Web应用，结果如下： <BR>
<TABLE class=java cellSpacing=1 cellPadding=3 bgColor=#999999 border=0>
<TBODY>
<TR>
<TD vAlign=top align=left width=1 bgColor=#dddddd><PRE><FONT color=#555555>1<BR>2<BR>3<BR>4<BR>5<BR>6<BR>7<BR>8<BR></FONT></PRE></TD>
<TD vAlign=top align=left bgColor=#ffffff><PRE>11:17:51,165 INFO  [Engine] StandardContext[/Test]
===============
Total elapsed time is: 0.0 seconds.
===============
11:17:51,165 INFO  [Engine] StandardContext[/Test]
===============
Total elapsed time is: 10.0 seconds.
===============
</PRE></TD></TR></TBODY></TABLE><BR><B>过滤器的运用 <BR></B>在适合使用装饰过滤器模式或者拦截器模式的任何地方，您都可以使用过滤器。过滤器的一些最普遍的应用如下： <BR><BR><B>加载</B>：对于到达系统的所有请求，过滤器收集诸如浏览器类型、一天中的时间、转发 URL 等相关信息，并对它们进行日志记录。 <BR><B>性能</B>：过滤器在内容通过线路传来并在到达 servlet 和 JSP 页面之前解压缩该内容，然后再取得响应内容，并在将响应内容发送到客户机机器之前将它转换为压缩格式。 <BR><B>安全</B>：过滤器处理身份验证令牌的管理，并适当地限制安全资源的访问，提示用户进行身份验证和/或将他们指引到第三方进行身份验证。过滤器甚至能够管理访问控制列表（Access Control List，ACL），以便除了身份验证之外还提供授权机制。将安全逻辑放在过滤器中，而不是放在 servlet 或者 JSP 页面中，这样提供了巨大的灵活性。在开发期间，过滤器可以关闭（在 web.xml 文件中注释掉）。在生产应用中，过滤器又可以再次启用。此外还可以添加多个过滤器，以便根据需要提高安全、加密和不可拒绝的服务的等级。 <BR><B>会话处理</B>：将 servlet 和 JSP 页面与会话处理代码混杂在一起可能会带来相当大的麻烦。使用过滤器来管理会话可以让 Web 页面集中精力考虑内容显示和委托处理，而不必担心会话管理的细节。 <BR><B>XSLT 转换</B>：不管是使用移动客户端还是使用基于 XML 的 Web 服务，无需把逻辑嵌入应用程序就在 XML 语法之间执行转换的能力都绝对是无价的。 <img src ="http://www.blogjava.net/aojilee/aggbug/26713.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2006-01-05 14:13 <a href="http://www.blogjava.net/aojilee/articles/26713.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>struts简单实例(中) </title><link>http://www.blogjava.net/aojilee/articles/20974.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Tue, 22 Nov 2005 07:47:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/20974.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/20974.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/20974.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/20974.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/20974.html</trackback:ping><description><![CDATA[通过前面的步骤,只能说具备开始开发和测试struts的条件,我还基本上什么都没有做.不过可以通过分析前面部署的一些文件,了解struts开发一些关键的部分.分析一下这个例子,可以看到文件结构是很简单的.<BR><IMG height=131 alt=3.JPG src="http://www.blogjava.net/images/blogjava_net/aojilee/struttest/3.JPG" width=160 border=0>&nbsp;<BR>需要注意的是web-inf里面的配置文件web.xml和struts-config.xml。<BR>至于详细配置先不细究。不如自动动手，看看能不能自己东西写个action出来。能想到的只有登陆系统了。为了不陷入数据库配置的泥潭。就先饶过数据库拉，假设有一个用户，用户名：aoji ，密码：apple。<BR><img src ="http://www.blogjava.net/aojilee/aggbug/20974.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2005-11-22 15:47 <a href="http://www.blogjava.net/aojilee/articles/20974.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>struts简单实例(上)</title><link>http://www.blogjava.net/aojilee/articles/20965.html</link><dc:creator>奥吉</dc:creator><author>奥吉</author><pubDate>Tue, 22 Nov 2005 06:47:00 GMT</pubDate><guid>http://www.blogjava.net/aojilee/articles/20965.html</guid><wfw:comment>http://www.blogjava.net/aojilee/comments/20965.html</wfw:comment><comments>http://www.blogjava.net/aojilee/articles/20965.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/aojilee/comments/commentRss/20965.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/aojilee/services/trackbacks/20965.html</trackback:ping><description><![CDATA[目的，通过简单的安装部署测试struts的功能。这也是struts学习的实例。<BR>前提条件：已经正确的安装jdk1.4 系列版本，正确的设置了jdk环境变量。正确的安装了jboss3，4系列版本。jboss能够正常的启动。<BR>所需工具： eclipse3或者3.1。<BR><BR>1. 下载struts包。下载地址和项目主页：<A href="http://struts.apache.org/">http://struts.apache.org/</A><BR><BR>2.&nbsp; 解压缩struts包到任意文件夹。<BR><BR>3.&nbsp; 到解压缩的struts目录下找到webapps\struts-blank.war<BR><BR>4.&nbsp; 可以直接将struts-blank.war目录移动到jboss的部署目录下面，如：E:\jboss-403SP1\server\default\deploy\<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;或者通过winrar等解压缩文件，打开并解压缩到你指定的目录，如teststruts.war，然后将此目录拷贝到<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;E:\jboss-403SP1\server\default\deploy\<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;目录结构如下：<BR><BR><IMG height=400 alt=1.jpg src="http://www.blogjava.net/images/blogjava_net/aojilee/struttest/1.jpg" width=250 border=0><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;5. 启动jboss<BR>　&nbsp;&nbsp;6. 在网页上进行测试.<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<IMG height=200 alt=2.JPG src="http://www.blogjava.net/images/blogjava_net/aojilee/struttest/2.JPG" width=600 border=0><BR>&nbsp;&nbsp;&nbsp;&nbsp; 7. 到此基本上成功了大半.<BR><img src ="http://www.blogjava.net/aojilee/aggbug/20965.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/aojilee/" target="_blank">奥吉</a> 2005-11-22 14:47 <a href="http://www.blogjava.net/aojilee/articles/20965.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>