﻿<?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-kapok-随笔分类-J2SE</title><link>http://www.blogjava.net/kapok/category/721.html</link><description>垃圾桶,嘿嘿，我藏的这么深你们还能找到啊，真牛！</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 09:03:57 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 09:03:57 GMT</pubDate><ttl>60</ttl><item><title>主动对象</title><link>http://www.blogjava.net/kapok/archive/2005/09/20/13572.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 20 Sep 2005 13:08:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/20/13572.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/13572.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/20/13572.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/13572.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/13572.html</trackback:ping><description><![CDATA[<STRONG><FONT size=5>&nbsp;</FONT></STRONG>
<P align=justify><A name=_Toc532649025></A><A name=_Toc532649683></A><A name=_Toc532650467></A><A name=_Toc535899856></A><A name=_Toc88053>5.1 </A><FONT lang=ZH-CN face=楷体_GB2312 size=5><A name=_Toc88053>主动对象</A></P></FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>
<P align=justify>　　那么到底什么是主动对象呢？传统上，所有的对象都是被动的代码段，对象中的代码是在对它发出方法调用的线程中执行的。也就是，调用线程（</FONT><FONT size=3>calling threads</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>）被“借出”，以执行被动对象的方法。</P>
<P align=justify>　　而主动对象却不一样。这些对象持有它们自己的线程（甚或多个线程），并将这个线程用于执行对它们的任何方法的调用。因而，如果你想象一个传统对象，在里面封装了一个线程（或多个线程），你就得到了一个主动对象。</P>
<P align=justify>　　例如，设想对象</FONT><FONT size=3>“A”</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>已在你的程序的</FONT><FONT size=3>main()</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>函数中被实例化。当你的程序启动时，</FONT><FONT size=3>OS</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>创建一个线程，以从</FONT><FONT size=3>main()</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>函数开始执行。如果你调用对象</FONT><FONT size=3>A</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>的任何方法，该线程将“流过”那个方法，并执行其中的代码。一旦执行完成，该线程返回调用该方法的点并继续它的执行。但是，如果</FONT><FONT size=3>”A”</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>是主动对象，事情就不是这样了。在这种情况下，主线程不会被主动对象借用。相反，当</FONT><FONT size=3>”A”</FONT><FONT lang=ZH-CN face=楷体_GB2312 size=3>的方法被调用时，方法的执行发生在主动对象持有的线程中。另一种思考方法：如果调用的是被动对象的方法（常规对象），调用会阻塞（同步的）；而另一方面，如果调用的是主动对象的方法，调用不会阻塞（异步的）。</P></FONT><FONT size=3>
<P align=justify>　</P></FONT><img src ="http://www.blogjava.net/kapok/aggbug/13572.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-20 21:08 <a href="http://www.blogjava.net/kapok/archive/2005/09/20/13572.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>When Runtime.exec() won't </title><link>http://www.blogjava.net/kapok/archive/2005/09/08/12455.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Thu, 08 Sep 2005 10:35:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/09/08/12455.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/12455.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/09/08/12455.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/12455.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/12455.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.htmlWhen Runtime.exec() won't Navigate yourself around pitfalls related to the Runtime.exec() methodSummary-->SummaryIn this install...&nbsp;&nbsp;<a href='http://www.blogjava.net/kapok/archive/2005/09/08/12455.html'>阅读全文</a><img src ="http://www.blogjava.net/kapok/aggbug/12455.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-09-08 18:35 <a href="http://www.blogjava.net/kapok/archive/2005/09/08/12455.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>用DynaBean减除不必要的VO和FormBean     </title><link>http://www.blogjava.net/kapok/archive/2005/05/10/4138.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 10 May 2005 05:01:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/10/4138.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4138.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/10/4138.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4138.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4138.html</trackback:ping><description><![CDATA[<A href="http://dev.csdn.net/article/62/62098.shtm">http://dev.csdn.net/article/62/62098.shtm</A><BR><BR><A href="http://www.chinaunix.net/jh/26/138463.html">http://www.chinaunix.net/jh/26/138463.html</A><BR><BR>&nbsp;&nbsp; DynaBean是BeanUtils里的宝贝之一。有了动态Bean，就不用写那么多VO出来了，特别那些只是为了和View交互而存在的bean，i hate Form Bean。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;DynaBean的用法很简单，特别是有了LazyBynaBean之后.<BR><PRE>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DynaBean car = new LazyDynaBean();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; car.set("carNo",1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; car.set("owner","张三");
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out.print(car.get("carNo"));</P></PRE>
<P></P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 看着像一个map,只不过多了反射的功能，所以支持那些使用反射来获取属性的场合。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 但是因为他没有car.getCarNo()这样的函数，只能用car.get("carNo")，所以也就不支持JSTL里面&lt;c: out value="{car.carNo}"&gt;这样的语法，因为JSTL是默认转回car.getCarNo()的。<BR>而且几经讨论,JSTL不支持 car.userFunction(),理由是不希望代码里有java代码:(<BR><BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因此，还是写&lt;%=car.get("carNo")&gt;好了，如果是陷在JSTL的一个循环结构里，就要用从pageContext里面把bean拿出来，<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 又或者自己写的tag，幸亏也很简单，用BeanUtils.getProperty(bean,property)函数就可以了，<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 又或者，用Velocity和Freemarker。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 最后，如果car.set("carNo",null)，会得到一个莫名其妙的Object对象，如果要使它为Null,需要先定义他的类型。<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 因此，对于可能为Null的列，需要用LazyClass定义类型，幸亏也很Lazy, 不可能为Null的值可以不管。</P>
<P>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LazyDynaClass lc = new LazyDynaClass();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; lc.add("owner",String.class); // 指定null converter String<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; DynaBean&nbsp;car = new LazyDynaBean(lc);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; car.set("carNo",1);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; car.set("owner",null);</P><img src ="http://www.blogjava.net/kapok/aggbug/4138.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-10 13:01 <a href="http://www.blogjava.net/kapok/archive/2005/05/10/4138.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Development with JSP and XML-- Part III: Developing JSP Custom Tags</title><link>http://www.blogjava.net/kapok/archive/2005/05/10/4133.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 10 May 2005 02:55:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/10/4133.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4133.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/10/4133.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4133.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4133.html</trackback:ping><description><![CDATA[<SPAN class=byline><B><A href="http://java.sun.com./developer/technicalArticles/xml/WebAppDev3/">http://java.sun.com./developer/technicalArticles/xml/WebAppDev3/</A><BR><BR>Part III: Developing JSP Custom Tags</B><BR>By Qusay H. Mahmoud<BR>August 2001 </SPAN>
<P>JavaServer Pages (JSP) technology makes it easy to embed bits of Java code (or scriptlets) in HTML documents. This solution, however, may not be suitable for all HTML content developers, perhaps because they do not know Java and they do not care to learn its syntax. While JavaBeans can be used to encapsulate much of the Java code, using them in JSP pages still requires content developers to have some knowledge of Java syntax. </P>
<P>JSP technology allows you to introduce new custom tags through the tag library facility. As a Java developer, you can extend JSP pages by introducing custom tags that can be deployed and used in an HTML-like syntax. Custom tags also allow you to provide better packaging by improving the separation between business logic and presentation logic. </P>
<P>This article presents a brief overview of custom tags, then it shows: </P>
<UL>
<LI>How to develop and deploy simple tags 
<LI>How to develop and deploy advanced tags: parameterized tags and tags with a body 
<LI>How to describe tags with the Tag Library Descriptor (TLD) </LI></UL>
<P>Finally, some programming guidelines are also provided. </P>
<H3>Overview of Tags</H3>
<P>If you have experience with HTML, you already know about the types of tags that can be used. Basically there are two types of tags, and both can have attributes (information about how the tag should do its job): </P>
<UL>
<LI><I>Bodyless Tags</I>: A bodyless tag is a tag that has a start tag but does not have a matching end tag. It has the syntax: 
<P><PRE>&lt;tagName attributeName="value" 
anotherAttributeName="anotherValue"/&gt;
</PRE>
<P></P>
<P>Bodyless tags are used to represent certain functions, such as presenting an input field, or displaying an image. Here is an example of a bodyless tag in HTML: </P>
<P><CODE>&lt;IMG SRC="/developer/technicalArticles/xml/WebAppDev3/fig10.gif"&gt;</CODE> </P>
<P></P>
<LI><I>Tags with a Body</I>: A tag with a body has a start tag and a matching end tag. It has the syntax: 
<P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>  
&lt;tagName attributeName="value" 
anotherAttributeName="anotherValue"&gt;
...tag body...
&lt;/tagName&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  --></P>
<P>Tags with a body are used to perform operations on the body content, such as formatting. Here is an example of a tag with a body in HTML: </P>
<P><CODE>&lt;H2&gt;Custom Tags&lt;/H2&gt;</CODE> </P></LI></UL>
<H3>JSP Custom Tags</H3>
<P>JSP custom tags are merely Java classes that implement special interfaces. Once they are developed and deployed, their actions can be called from your HTML using XML syntax. They have a start tag and an end tag. They may or may not have a body. A bodyless tag can be expressed as: </P>
<P><CODE>&lt;tagLibrary:tagName /&gt;</CODE></P>
<P>And, a tag with a body can be expressed as: </P><PRE>&lt;tagLibrary:tagName&gt;
   body
&lt;/tagLibrary:tagName&gt;
</PRE>
<P>Again, both types may have attributes that serve to customize the behavior of a tag. The following tag has an attribute called <CODE>name</CODE>, which accepts a String value obtained by evaluating the variable <CODE>yourName</CODE>: </P>
<P><CODE>&lt;mylib:hello name="&lt;%= yourName %&gt;" /&gt; </CODE></P>
<P>Or, it can be written as a tag with a body as: </P><PRE>&lt;mylib:hello&gt;
  &lt;%= yourName %&gt;
&lt;/mylib:hello&gt;</PRE>
<BLOCKQUOTE><!-- BEGIN FRAGMENT | HR TAG -->
<DIV class=contentdivider>
<TABLE class=grey4 cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=4 alt=" " src="http://java.sun.com./im/a.gif" width=1 border=0></TD></TR></TBODY></TABLE></DIV><!-- END FRAGMENT | HR TAG --><B>Note</B>: Any data that is a simple string, or can be generated by evaluating a simple expression, should be passed as an attribute and not as body content. <!-- BEGIN FRAGMENT | HR TAG -->
<DIV class=contentdivider>
<TABLE class=grey4 cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD><IMG height=4 alt=" " src="http://java.sun.com./im/a.gif" width=1 border=0></TD></TR></TBODY></TABLE></DIV><!-- END FRAGMENT | HR TAG --></BLOCKQUOTE>
<H4>Benefits of Custom Tags</H4>
<P>A very important thing to note about JSP custom tags is that they do not offer more functionality than scriptlets, they simply provide better packaging, by helping you improve the separation of business logic and presentation logic. Some of the benefits of custom tags are: </P>
<UL>
<LI>They can reduce or eliminate scriptlets in your JSP applications. Any necessary parameters to the tag can be passed as attributes or body content, and therefore no Java code is needed to initialize or set component properties. 
<LI>They have simpler syntax. Scriptlets are written in Java, but custom tags can be used in an HTML-like syntax. 
<LI>They can improve the productivity of nonprogrammer content developers, by allowing them to perform tasks that cannot be done with HTML. 
<LI>They are reusable. They save development and testing time. Scritplets are not reusable, unless you call cut-and-paste reuse. </LI></UL>
<P>In short, you can use custom tags to accomplish complex tasks the same way you use HTML to create a presentation. </P>
<H3>Defining a Tag</H3>
<P>A tag is a Java class that implements a specialized interface. It is used to encapsulate the functionality from a JSP page. As we mentioned earlier, a tag can be bodyless or with a body. To define a simple bodyless tag, your class must implement the <CODE>Tag</CODE> interface. Developing tags with a body is discussed later. Sample 1 shows the source code for the <CODE>Tag</CODE> interface that you must implement: </P>
<P><B>Sample 1:</B> Tag.java </P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>public interface Tag {
   public final static int SKIP_BODY = 0;
   public final static int EVAL_BODY_INCLUDE = 1;
   public final static int SKIP_PAGE = 5;
   public final static int EVAL_PAGE = 6;

   void setPageContext(PageContext pageContext);
   void setParent(Tag parent);
   Tag getParent();
   int doStartTag() throws JspException;
   int doEndTag() throws JspException;
   void release();
}
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>All tags must implement the <CODE>Tag</CODE> interface (or one of its subinterfaces) as it defines all the methods the JSP runtime engine calls to execute a tag. Table 1 provides a description of the methods in the <CODE>Tag</CODE> interface. </P>
<CENTER>
<TABLE width="75%" border=2>
<CAPTION align=top><I>Table 1: Description of methods in the Tag interface</I> </CAPTION>
<TBODY>
<TR>
<TH>Method</TH>
<TH>Description</TH></TR>
<TR>
<TD><CODE>setPageContext(PageContext pc)</CODE></TD>
<TD>This method is invoked by the JSP runtime, prior to <CODE>doStartTag</CODE>, to set the page context.</TD></TR>
<TR>
<TD><CODE>setParent(Tag parent)</CODE></TD>
<TD>Invoked by the JSP runtime, prior to <CODE>doStartTag</CODE>, to pass a tag handler a reference to its parent tag.</TD></TR>
<TR>
<TD><CODE>getParent</CODE></TD>
<TD>Returns a <CODE>Tag</CODE> instance that is the parent of this tag.</TD></TR>
<TR>
<TD><CODE>doStartTag</CODE></TD>
<TD>Invoked by the JSP runtime to prompt the tag handler to process the start tag for this instance.</TD></TR>
<TR>
<TD><CODE>doEndTag</CODE></TD>
<TD>Invoked by the JSP runtime after returning from <CODE>doStartTag</CODE>. The body of the action may or may not have been evaluated, depending on the return value of <CODE>doStartTag</CODE>.</TD></TR>
<TR>
<TD><CODE>release</CODE></TD>
<TD>Invoked by the JSP runtime to indicate to the tag handler to perform any cleanup necessary.</TD></TR></TBODY></TABLE></CENTER>
<H3>My First Tag</H3>
<P>Now, let's look at a sample tag that when invoked prints a message to the client. </P>
<P>There are a few steps involved in developing a custom tag. These steps can be summarized as follows: </P>
<OL>
<LI>Develop the tag handler 
<LI>Create a tag library descriptor 
<LI>Test the tag </LI></OL>
<H4>1. Develop the Tag Handler</H4>
<P>A <I>tag handler</I> is an object invoked by the JSP runtime to evaluate a custom tag during the execution of a JSP page that references the tag. The methods of the tag handler are called by the implementation class at various points during the evaluation of the tag. Every tag handler must implement a specialized interface. In this example, the simple tag implements the <CODE>Tag</CODE> interface as shown in Sample 2.</P>
<P><B>Sample 2</B>: HelloTag.java</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>package tags;

import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class HelloTag implements Tag {
   private PageContext pageContext;
   private Tag parent;

   public HelloTag() {
      super();
   }

   public int doStartTag() throws JspException {
      try {
         pageContext.getOut().print(
         "This is my first tag!");
      } catch (IOException ioe) {
         throw new JspException("Error: 
         IOException while writing to client" 
         + ioe.getMessage());
      }
      return SKIP_BODY;
   }

   public int doEndTag() throws JspException {
      return SKIP_PAGE;
   }

   public void release() {
   }

   public void setPageContext(PageContext 
   pageContext) {
      this.pageContext = pageContext;
   }

   public void setParent(Tag parent) {
      this.parent = parent;
   }

   public Tag getParent() {
      return parent;
   }
}
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>The two important methods to note in <CODE>HelloTag</CODE> are <CODE>doStartTag</CODE> and <CODE>doEndTag</CODE>. The <CODE>doStartTag</CODE> method is invoked when the start tag is encountered. In this example, this method returns <CODE>SKIP_BODY</CODE> because a simple tag has no body. The <CODE>doEndTag</CODE> method is invoked when the end tag is encountered. In this example, this method returns <CODE>SKIP_PAGE</CODE> because we do not want to evaluate the rest of the page; otherwise it should return <CODE>EVAL_PAGE</CODE>. </P>
<P>To compile the <CODE>HelloTag</CODE> class, assuming that Tomcat is installed at: <I>c:\tomcat</I>: </P>
<UL>
<LI>Create a new subdirectory called <I>tags</I>, which is the name of the package containing the <CODE>HelloTag</CODE> class. This should be created at: <I>c:\tomcat\webapps\examples\web-inf\classes</I>. 
<LI>Save <I>HelloTag.java</I> in the <I>tags</I> subdirectory. 
<LI>Compile with the command: 
<P><PRE>c:\tomcat\webapps\examples\web-inf\classes\tags&gt; 
javac -classpath c:\tomcat\lib\servlet.jar 
HelloTag.java
</PRE>
<P></P></LI></UL>
<H4>2. Create a Tag Library Descriptor</H4>
<P>The next step is to specify how the tag will be used by the JSP runtime that executes it. This can be done by creating a Tag Library Descriptor (TLD), which is an XML document. Sample 3 shows a sample TLD: </P>
<P><B>Sample 3</B>: mytaglib.tld</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;?xml version="1.0" encoding="ISO-8859-1" ?&gt;
&lt;!DOCTYPE taglib
        PUBLIC "-//Sun Microsystems, Inc.//
        DTD JSP Tag Library 1.1//EN"
        "http://java.sun.com/j2ee/dtds/
        web-jsptaglibrary_1_1.dtd"&gt;

&lt;!-- a tag library descriptor --&gt;

&lt;taglib&gt;
   &lt;tlibversion&gt;1.0&lt;/tlibversion&gt;
   &lt;jspversion&gt;1.1&lt;/jspversion&gt;
   &lt;shortname&gt;first&lt;/shortname&gt;
   &lt;uri&gt;&lt;/uri&gt;
   &lt;info&gt;A simple tab library for the 
   examples&lt;/info&gt;

  &lt;tag&gt;
    &lt;name&gt;hello&lt;/name&gt;
    &lt;tagclass&gt;tags.HelloTag&lt;/tagclass&gt;
    &lt;bodycontent&gt;empty&lt;/bodycontent&gt;
    &lt;info&gt;Say Hi&lt;/info&gt;
  &lt;/tag&gt;         
&lt;/taglib&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>First we specify the tag library version and JSP version. The <CODE>&lt;shortname&gt;</CODE> tag specifies how we are going to reference the tag library from the JSP page. The <CODE>&lt;uri&gt;</CODE> tag can be used as a unique identifier for your tag library. </P>
<P>In this TLD, we only have one tag named <CODE>hello</CODE> whose class is specified using the <CODE>&lt;tagclass&gt;</CODE> tag. However, a tag library can have as many tags as you like. The <CODE>&lt;bodycontent&gt;</CODE> tells us that this tag will not have a body; otherwise an error will be produced. On the other hand, if you like to evaluate the body of the tag, that value would be: </P>
<UL>
<LI><CODE>tagdependent</CODE>: meaning that any body of the tag would be handled by the tag itself, and it can be empty. 
<LI><CODE>JSP</CODE>: meaning that the JSP container should evaluate any body of the tag, but it can also be empty. </LI></UL>
<P>Save <I>mytaglib.tld</I> in the directory: <I>c:\tomcat\webapps\examples\web-inf\jsp</I>. </P>
<H4>3. Test the Tag</H4>
<P>The final step is to test the tag we have developed. In order to use the tag, we have to reference it, and this can be done in three ways: </P>
<OL>
<LI>Reference the tag library descriptor of an unpacked tag library. For example: 
<P><PRE>&lt;@ taglib uri="/WEB-INF/jsp/mytaglib.tld" 
prefix="first" %&gt;
</PRE>
<P></P>
<LI>Reference a JAR file containing a tag library. For example: 
<P><PRE>&lt;@ taglib uri="/WEB-INF/myJARfile.jar" 
prefix='first" %&gt;
</PRE>
<P></P>
<LI>Define a reference to the tag library descriptor from the web-application descriptor (web.xml) and define a short name to reference the tag library from the JSP. To do this, open the file: <I>c:\tomcat\webapps\examples\web-inf\web.xml</I> and add the following lines before the end line, which is <CODE>&lt;web-app&gt;</CODE>: 
<P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>    &lt;taglib&gt;
       &lt;taglib-uri&gt;mytags&lt;/taglib-uri&gt;
       &lt;taglib-location&gt;/WEB-INF/jsp/
       mytaglib.tld&lt;/taglib-location&gt;
    &lt;/taglib&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  --></P></LI></OL>
<P>Now, write a JSP and use the first syntax. Sample 4 shows an example: </P>
<P><B>Sample 4</B>: Hello.jsp</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;%@ taglib uri="/WEB-INF/jsp/mytaglib.tld"
 prefix="first" %&gt;
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;Hello Tag&lt;/TITLE&gt;
&lt;/HEAD&gt;

&lt;BODY bgcolor="#ffffcc"&gt;

&lt;B&gt;My first tag prints&lt;/B&gt;:

&lt;first:hello/&gt;

&lt;/BODY&gt;
&lt;/HTML&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>The <CODE>taglib</CODE> is used to tell the JSP runtime where to find the descriptor for our tag library, and the <CODE>prefix</CODE> specifies how we will refer to tags in this library. With this in place, the JSP runtime will recognize any usage of our tag throughout the JSP, as long as we precede our tag name with the prefix <CODE>first</CODE> as in <CODE>&lt;first:hello/&gt;</CODE>. </P>
<P>Alternatively, you can use the second reference option by creating a JAR file. Or, you can use the third reference option simply by replacing the first line in Sample 4 with the following line: </P><PRE>&lt;%@ taglib uri="mytags" prefix="first" %&gt;
</PRE>
<P>Basically, we have used the <CODE>mytags</CODE> name, which was added to web.xml, to reference the tag library. For the rest of the examples in this article, this reference will be used. </P>
<P>Now, if you request Hello.jsp from a browser, you would see something similar to Figure 1. </P>
<P>
<CENTER><IMG src="http://java.sun.com./developer/technicalArticles/xml/WebAppDev3/fig1.gif"><BR><I>Figure 1: First Custom Tag</I> </CENTER>
<P></P>
<P>The custom tag developed in Sample 4 is a simple tag, the goal of it was just to give you a flavor of the effort involved in developing custom tags. You may have noticed that even this simple tag required us to implement a number of methods, some of which have very simple implementations. To minimize the effort involved, the JSP designers provided a template to be used for implementing simple tags. The template is the <CODE>TagSupport</CODE> abstract class. It is a convenience class that provides default implementations for all the methods in the <CODE>Tag</CODE> interface. </P>
<P>Therefore, an easier way to write simple tags is to <I>extend</I> the <CODE>TagSupport</CODE> class rather than <I>implementing</I> the <CODE>Tag</CODE> interface. You can think of the <CODE>TagSupport</CODE> abstract class as an adapter. Having said that, the <CODE>HelloTag</CODE> class in Sample 4 can be easily written as shown in Sample 5. </P>
<P><B>Sample 5</B>: Extending the TagSupport class</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>package tags;

import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class HelloTag extends TagSupport {

   public int doStartTag() throws JspException {
      try {
         pageContext.getOut().print("This is my 
         first tag!");
      } catch (IOException ioe) {
         throw new JspException("Error: 
         IOException while writing 
        to client" + ioe.getMessage());
      }
      return SKIP_BODY;
   }

   public int doEndTag() throws JspException {
      return SKIP_PAGE;
   }
}
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<H3>Parameterized Tags</H3>
<P>We have seen how to develop simple tags. Now, let's see how to develop parameterized tags--tags that have attributes. There are two new things that that need to be added to the previous example to handle attributes: </P>
<OL>
<LI>Add a set method 
<LI>Add a new tag to <I>mytagslib.tld</I> </LI></OL>
<P>Adding a set method and changing the output message results in Sample 5. </P>
<P><B>Sample 5</B>: A tag with an attribute</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>package tags;

import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class HelloTagParam extends TagSupport {
   private String name;

   <B>
   public void setName(String name) {
      this.name = name;
   }
   </B>

   public int doStartTag() throws JspException {
      try {
         pageContext.getOut().print("Welcome to 
         JSP Tag Programming, "<B> +name</B>);
      } catch (IOException ioe) {
         throw new JspException("Error: 
         IOException 
         while writing to client");
      }
      return SKIP_BODY;
   }

   public int doEndTag() throws JspException {
      return SKIP_PAGE;
   }
}
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>The next thing we need to do is add a new tag to <I>mytaglib.tld</I>. The new tag is shown in Sample 6. This snippet of code should be added to <I>mytaglib.tld</I> right before the last line, <CODE>&lt;/taglib&gt;</CODE>: </P>
<P><B>Sample 6</B>: revising mytaglib.tld</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;tag&gt;
  &lt;name&gt;helloparam&lt;/name&gt;
  &lt;tagclass&gt;tags.HelloTagParam&lt;/tagclass&gt;
  &lt;bodycontent&gt;empty&lt;/bodycontent&gt;
  &lt;info&gt;Tag with Parameter&lt;/info&gt;
  &lt;attribute&gt;
     &lt;name&gt;name&lt;/name&gt;
     &lt;required&gt;false&lt;/required&gt;
     &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
         &lt;/attribute&gt;
&lt;/tag&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>We have added a new tag named <CODE>helloparam</CODE>. Notice the new <CODE>&lt;attribute&gt;</CODE> tag, which specifies that the <CODE>helloparam</CODE> tag accepts an attribute whose name is <CODE>name</CODE>. The <CODE>&lt;required&gt;</CODE> tag is set to <CODE>false</CODE>, meaning that the attribute is optional; the <CODE>&lt;rtexprvalue&gt;</CODE> tag is set to <CODE>false</CODE> specifying that no run time evaluation will be done. </P>
<P>Nothing needs to be added to the web.xml web-application descriptor file since we are using the same tag library: <I>mytaglib.tld</I>. </P>
<P>Now, we can test the new tag. The source code in Sample 7 shows how to test it using a name attribute "JavaDuke". </P>
<P><B>Sample 7</B>: HelloTagParam.jsp</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;%@ taglib uri="mytags" prefix="first" %&gt;
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;Hello Tag with Parameter&lt;/TITLE&gt;
&lt;/HEAD&gt;

&lt;BODY bgcolor="#ffffcc"&gt;
&lt;B&gt;My parameterized tag prints&lt;/B&gt;:

&lt;P&gt;

&lt;first:helloparam name="JavaDuke"/&gt;

&lt;/BODY&gt;
&lt;/HTML&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>If you request <I>HelloTagParam.jsp</I> from a web browser, you would see an output similar to that in Figure 2. </P>
<P>
<CENTER><IMG src="http://java.sun.com./developer/technicalArticles/xml/WebAppDev3/fig2.gif"><BR><I>Figure 2: Testing a parameterized tag</I> </CENTER>
<P></P>
<H3>Tag Libraries</H3>
<P>A tag library is a collection of JSP custom tags. The <A href="http://jakarta.apache.org/taglibs">Jakarta Taglibs Project</A> provides several useful tag libraries for XML parsing, transformations, email, databases, and other uses. They can be easily downloaded and used. </P>
<P>Here we develop our tag library. As an example, we develop a simple math library that provides two tags, one for adding two numbers, and the other for subtracting one number from another number. Each tag is represented by one class. The source code for the two classes, <CODE>Add.java</CODE> and <CODE>Subtract.java</CODE>, is shown in Sample 8. </P>
<P><B>Sample 8</B>: Add.java and Subtract.java</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>package tags;

import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class Add extends TagSupport {
   private int num1, num2;

   public void setNum1(int num1) {
      this.num1 = num1;
   }
   
   public void setNum2(int num2) {
      this.num2 = num2;
   }


   public int doStartTag() throws JspException {
      try {
         pageContext.getOut().print("Welcome
         to First
          Grade Math! ");
         pageContext.getOut().print("The sum of: " +
         num1 + " and " + num2 + " is: " + (
         num1+num2));
      } catch (IOException ioe) {
         throw new JspException("Error:
         IOException
          while writing to client");
      }
      return SKIP_BODY;
   }
}
      
// Subtract.java

package tags;

import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class Subtract extends TagSupport {
   private int num1, num2;

   public void setNum1(int num1) {
      this.num1 = num1;
   }
   
   public void setNum2(int num2) {
      this.num2 = num2;
   }


   public int doStartTag() throws JspException {
      try {
         pageContext.getOut().print("Welcome to First
          Grade Math! ");
         pageContext.getOut().print("If you 
         subtract:
          " + num2 + " from " + num1 +
          ", you get: "+ (num1 - num2));
      } catch (IOException ioe) {
         throw new JspException("Error:
          IOException 
         while writing to client");
      }
      return SKIP_BODY;
   }
}
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>The source code is easy to understand. Notice one thing we have repeated in <CODE>Add.java</CODE> and <CODE>Subract.java</CODE> is the call to <CODE>pageContext.getOut.print</CODE>. A better way to do this would be to get a <CODE>JspWriter</CODE> object then then use it to print to the client: </P><PRE>JspWriter out = pageContext.getOut();
out.print("first line");
out.print("second line");
</PRE>
<P>The next step is to revise the tag library descriptor file, <I>mytaglib.tld</I>, and add descriptions to the two new tags. Sample 9 shows the description for the new tags. Add the following snippet of XML to <CODE>mytaglib.tld</CODE>, right before the last line. </P>
<P><B>Sample 9</B>: revising mytaglib.tld</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>  &lt;tag&gt;
    &lt;name&gt;add&lt;/name&gt;
    &lt;tagclass&gt;tags.Add&lt;/tagclass&gt;
    &lt;bodycontent&gt;empty&lt;/bodycontent&gt;
    &lt;info&gt;Tag with Parameter&lt;/info&gt;
    &lt;attribute&gt;
       &lt;name&gt;num1&lt;/name&gt;
       &lt;required&gt;true&lt;/required&gt;
       &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
    &lt;/attribute&gt;
    &lt;attribute&gt;
      &lt;name&gt;num2&lt;/name&gt;
      &lt;required&gt;true&lt;/required&gt;
      &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
    &lt;/attribute&gt;
  &lt;/tag&gt;

  &lt;tag&gt;
    &lt;name&gt;sub&lt;/name&gt;
    &lt;tagclass&gt;tags.Subtract&lt;/tagclass&gt;
    &lt;bodycontent&gt;empty&lt;/bodycontent&gt;
    &lt;info&gt;Tag with Parameter&lt;/info&gt;
    &lt;attribute&gt;
       &lt;name&gt;num1&lt;/name&gt;
       &lt;required&gt;true&lt;/required&gt;
       &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
    &lt;/attribute&gt;
    &lt;attribute&gt;
      &lt;name&gt;num2&lt;/name&gt;
      &lt;required&gt;true&lt;/required&gt;
      &lt;rtexprvalue&gt;false&lt;/rtexprvalue&gt;
    &lt;/attribute&gt;
  &lt;/tag&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>As you can see, each tag requires two attributes that must be named <CODE>num1</CODE> and <CODE>num2</CODE>. </P>
<P>Now, we can test our math tag library using the test driver shown in Sample 10. </P>
<P><B>Sample 10</B>: math.jsp</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;%@ taglib uri="mytags" prefix="math" %&gt;
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;Hello Tag with Parameter&lt;/TITLE&gt;
&lt;/HEAD&gt;

&lt;BODY bgcolor="#ffffcc"&gt;
&lt;B&gt;Calling first tag&lt;/B&gt;
&lt;P&gt;
&lt;math:add num1="1212" num2="121"/&gt;
&lt;P&gt;
&lt;B&gt;Calling second tag&lt;/B&gt;
&lt;P&gt;
&lt;math:sub num1="2123" num2="3"/&gt;

&lt;/BODY&gt;
&lt;/HTML&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>If you request <I>math.jsp</I> from a web browser, you would see an output that is similar to Figure 3. </P>
<P>
<CENTER><A href="http://java.sun.com./developer/technicalArticles/xml/WebAppDev3/fig3new.gif"><IMG height=181 alt="fig 3" src="http://java.sun.com./developer/technicalArticles/xml/WebAppDev3/fig3bnew.gif" width=450> </A><BR><I>Figure 3: Testing the math tag library </I></CENTER>
<P></P>
<H3>Tags with a Body</H3>
<P>A tag handler for a tag with a body is implemented differently depending on whether the body needs to be evaluated once or multiple times. </P>
<UL>
<LI><I>Single Evaluation</I>: if the body needs to be evaluated once, the tag handler should implement the <CODE>Tag</CODE> interface, or extend the <CODE>TagSupport</CODE> abstract class; the <CODE>doStartTag</CODE> method needs to return <CODE>EVAL_BODY_INCLUDE</CODE>, and if it does not need to be evaluated at all then it should return <CODE>BODY_SKIP</CODE>. 
<LI><I>Multiple Evaluation</I>: if the body needs to be evaluated multiple times, the <CODE>BodyTag</CODE> interface should be implemented. The <CODE>BodyTag</CODE> interface extends the <CODE>Tag</CODE> interface and defines additional methods (<CODE>setBodyContent</CODE>, <CODE>doInitBody</CODE>, and <CODE>doAfterBody</CODE>) that enable a tag handler to inspect and possibly change its body. Alternatively, and similarly to the <CODE>TagSupport</CODE> class, you can extend the <CODE>BodyTagSupport</CODE> class, which provides default implementations for the methods in the <CODE>BodyTag</CODE> interface. Typically, you need to implement <CODE>doInitBody</CODE> and <CODE>doAfterBody</CODE> methods. The <CODE>doInitBody</CODE> is called after the body content is set but before it is evaluated, and the <CODE>doAfterBody</CODE> is called after the body content is evaluated. </LI></UL>
<H4>Single Evaluation</H4>
<P>Here is an example of single evaluation where we extend the <CODE>BodyTagSupport</CODE> class. This example reads the body content, converts it to lowercase, then writes the output back to the client. Sample 11 shows the source code. The body content is retrieved as a string, converted to lowercase, then written back to the client. </P>
<P><B>Sample 11</B>: ToLowerCaseTag.java</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>package tags;

import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class ToLowerCaseTag extends BodyTagSupport {

   public int doAfterBody() throws JspException {
      try {
         BodyContent bc = getBodyContent();
         // get the bodycontent as string
         String body = bc.getString();
         // getJspWriter to output content
         JspWriter out = bc.getEnclosingWriter();
         if(body != null) {
            out.print(body.toLowerCase());
         }
      } catch(IOException ioe) {
         throw new JspException("Error:
          "+ioe.getMessage());   
      }
      return SKIP_BODY;
   }
}

</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>The next step is to add a tag to the tag library descriptor file, <I>mytaglib.tld</I>. The new tag descriptor is: </P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;tag&gt;
  &lt;name&gt;tolowercase&lt;/name&gt;
  &lt;tagclass&gt;tags.ToLowerCaseTag&lt;/tagclass&gt;
  &lt;bodycontent&gt;JSP&lt;/bodycontent&gt;
  &lt;info&gt;To lower case tag&lt;/info&gt;
&lt;/tag&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>Note that when you write a tag with a body, the <CODE>&lt;bodycontent&gt;</CODE> tag's value must be either <CODE>JSP</CODE> or <CODE>jspcontent</CODE>, as discussed earlier. </P>
<P>A test driver for this example is shown in Sample 12. </P>
<P><B>Sample 12</B>: lowercase.jsp</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;%@ taglib uri="mytags" prefix="first" %&gt;
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;Body Tag&lt;/TITLE&gt;
&lt;/HEAD&gt;

&lt;BODY bgcolor="#ffffcc"&gt;

&lt;first:tolowercase&gt;
Welcome to JSP Custom Tags Programming.
&lt;/first:tolowercase&gt;

&lt;/BODY&gt;
&lt;/HTML&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>If you request <CODE>lowercase.jsp</CODE> from a web browser, you would see something similar to Figure 4. </P>
<P>
<CENTER><IMG src="http://java.sun.com./developer/technicalArticles/xml/WebAppDev3/fig4.gif"><BR><I>Figure 4: Testing the lowercase tag</I> </CENTER>
<P></P>
<H4>Multiple Evaluations</H4>
<P>Let's now see an example of a body tag evaluated multiple times. The example accepts a string and prints the string as many times as indicated in the JSP. The source code is shown in Sample 13: </P>
<P><B>Sample 13</B>: LoopTag.java</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>package tags;

import java.io.*;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;

public class LoopTag extends BodyTagSupport {

   int times = 0;

   BodyContent bodyContent;

   public void setTimes(int times) {

      this.times = times;
   }

   public int doStartTag() throws JspException {
      if (times&gt;0) {
        return EVAL_BODY_TAG;
      } else {
         return SKIP_BODY;
      }
   }

   public void setBodyContent(BodyContent 
   bodyContent) {
      this.bodyContent = bodyContent;
   }

   public int doAfterBody() throws JspException {
      if (times &gt;1) {
        times--;
        return EVAL_BODY_TAG;
      } else {
         return SKIP_BODY;
      }
   }

   public int doEndTag() throws JspException {
      try {
         if(bodyContent != null) {
           bodyContent.writeOut(
           bodyContent.getEnclosingWriter());
         }
      } catch(IOException e) {
        throw new JspException(
        "Error: "+e.getMessage());
      }
      return EVAL_PAGE;
   }
}
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>In this example, the methods implemented play the following roles: </P>
<UL>
<LI>The <CODE>doStartTag</CODE> method gets called at the start of the tag. It checks if the loop needs to be performed. 
<LI>The <CODE>setBodyContent</CODE> is called by the JSP container to check for more than one loop. 
<LI>The <CODE>doAfterBody</CODE> method is called after each evaluation; the number of times the loop needs to be performed is decreased by one, then it returns <CODE>SKIP_BODY</CODE> when the number of times is not greater than one. 
<LI>The <CODE>doEndTag</CODE> method is called at the end of the tag, and the content (if any) is written to the enclosing writer. </LI></UL>
<P>Similarly to previous examples, the next step is to add a new tag descriptor to <I>mytaglib.tld</I>. The following lines show what needs to be added: </P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;tag&gt;
  &lt;name&gt;loop&lt;/name&gt;
  &lt;tagclass&gt;tags.LoopTag&lt;/tagclass&gt;
  &lt;bodycontent&gt;JSP&lt;/bodycontent&gt;
  &lt;info&gt;Tag with body and parameter&lt;/info&gt;
  &lt;attribute&gt;
     &lt;name&gt;times&lt;/name&gt;
     &lt;required&gt;true&lt;/required&gt;
     &lt;rtexprvalue&gt;true&lt;/rtexprvalue&gt;
  &lt;/attribute&gt;
&lt;/tag&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>Note that the <CODE>&lt;rtexprvalue&gt;</CODE> tag specifies that evaluations will be performed at runtime. </P>
<P>A test driver is shown in Sample 14. </P>
<P><B>Sample 14</B>: loops.jsp</P><!-- BEGIN VCD7 CODE SAMPLE COMPONENT  -->
<TABLE class=grey4 cellSpacing=0 cellPadding=10 width="100%" border=0>
<TBODY>
<TR>
<TD><PRE>&lt;%@ taglib uri="mytags" prefix="first" %&gt;
&lt;HTML&gt;
&lt;HEAD&gt;
&lt;TITLE&gt;Body Tag&lt;/TITLE&gt;
&lt;/HEAD&gt;

&lt;BODY bgcolor="#ffffcc"&gt;

&lt;first:loop times="4"&gt;
Welcome to Custom Tags Programming.&lt;BR&gt;
&lt;/first:loop&gt;

&lt;/BODY&gt;
&lt;/HTML&gt;
</PRE></TD></TR></TBODY></TABLE><SPAN class=sp20></SPAN><BR><!-- END VCD7 CODE SAMPLE COMPONENT  -->
<P>Finally, if you request <I>loops.jsp</I> from a browser, you would see output similar to that in Figure 5. </P>
<P>
<CENTER><IMG src="http://java.sun.com./developer/technicalArticles/xml/WebAppDev3/fig5.gif"><BR><I>Figure 5: Testing loops.jsp</I> </CENTER>
<P></P>
<H4>Programming Guidelines</H4>
<P>Here are a few guidelines to keep in mind when developing JSP tag libraries: </P>
<UL>
<LI><I>Keep it simple</I>: if a tag requires several attributes, try to break it up into several tags. 
<LI><I>Make it usable</I>: consult the users of the tags (HTML developers) to achieve a high degree of usability. 
<LI><I>Do not invent a programming language in JSP</I>: do not develop custom tags that allow users to write explicit programs. 
<LI><I>Try not to reinvent the wheel</I>: there are several JSP tag libraries available, such as the <A href="http://jakarta.apache.org/taglibs">Jakarta Taglibs Project</A>. Check to see if what you want is already available so you do not have to reinvent the wheel. </LI></UL>
<H4>Conclusion</H4>
<P>In <A href="http://java.sun.com./developer/technicalArticles/xml/WebAppDev2/">Web Application Development with JSP and XML Part II: JSP with XML in mind</A>, we have seen how to parse XML documents. But even the untrained eye would have noticed that we have embedded lots of the parsing (or logic) code in JSP. Even though we have used JavaBeans to encapsulate much of the Java code, we still ended up with JavaServer Pages mixing program logic with presentation. </P>
<P>Custom tags help you improve the separation of program logic (parsing and iteration in Part II), and presentation. The various examples in this article show how to develop and deploy simple and advanced custom tags. As an exercise, you may want to rewrite the SAX and DOM examples in Part II as tag libraries. Also, you may wish to look at what the <A href="http://jakarta.apache.org/taglibs">Jakarta Taglibs Project</A> has to offer for XML parsing and XSL transformations. It provides tag libraries for other things as well. </P>
<H4>For more information</H4>
<P><BR><A href="http://java.sun.com./products/jsp/index.jsp">Javaserver Pages, Dynamically Generated Web Content</A> <BR><A href="http://java.sun.com./products/jsp/taglibraries/index.jsp">Javaserver Pages Tag Libraries</A> <BR><A href="http://jakarta.apache.org/taglibs/">Jakarta Taglibs Project</A> <BR><A href="http://java.sun.com./developer/technicalArticles/javaserverpages/JakartaTaglibs">The Jakarta Taglibs Project--Part I</A> <BR><A href="http://jcp.org/jsr/detail/052.jsp">A Standard Tag Library for JavaServer Pages</A> </P>
<H4>About the author</H4>
<P><A href='mailto:"qmahmoud@javacourses.com"'>Qusay H. Mahmoud</A> provides Java consulting and training services. He has published dozens of articles on Java, and is the author of <I>Distributed Programming with Java</I> (Manning Publications, 1999). </P><img src ="http://www.blogjava.net/kapok/aggbug/4133.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-10 10:55 <a href="http://www.blogjava.net/kapok/archive/2005/05/10/4133.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>instanceof, isAssignable, isInstance</title><link>http://www.blogjava.net/kapok/archive/2005/05/09/4107.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 09 May 2005 02:12:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/09/4107.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4107.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/09/4107.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4107.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4107.html</trackback:ping><description><![CDATA[<P>public class TestClass {</P>
<P>&nbsp;public static void main(String[] args) {<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;Parent parent = new Parent();<BR>&nbsp;&nbsp;Child child = new Child();<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;if (child instanceof Child){<BR>&nbsp;&nbsp;&nbsp;System.out.println("child instanceof Child is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("child instanceof Child is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;if (child instanceof Parent){<BR>&nbsp;&nbsp;&nbsp;System.out.println("child instanceof Parent is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("child instanceof Parent is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;if (child instanceof Person){<BR>&nbsp;&nbsp;&nbsp;System.out.println("child instanceof Person is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("child instanceof Person is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;if (Child.class.isInstance(child)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isInstance(child) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isInstance(child) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;if (Parent.class.isInstance(child)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Parent.class.isInstance(child) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Parent.class.isInstance(child) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;if (Person.class.isInstance(child)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Person.class.isInstance(child) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Person.class.isInstance(child) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;if (Child.class.isAssignableFrom(Child.class)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isAssignableFrom(Child.class) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isAssignableFrom(Child.class) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;if (Child.class.isAssignableFrom(Parent.class)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isAssignableFrom(Parent.class) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isAssignableFrom(Parent.class) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;if (Child.class.isAssignableFrom(Person.class)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isAssignableFrom(Person.class) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isAssignableFrom(Person.class) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;//Parent class implemented the Interface 'Person'<BR>&nbsp;&nbsp;if (Parent.class.isAssignableFrom(Person.class)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Parent.class.isAssignableFrom(Person.class) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Parent.class.isAssignableFrom(Person.class) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;if (Child.class.isAssignableFrom(Child.class)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isAssignableFrom(Child.class) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Child.class.isAssignableFrom(Child.class) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;if (Parent.class.isAssignableFrom(Child.class)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Parent.class.isAssignableFrom(Child.class) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Parent.class.isAssignableFrom(Child.class) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;if (Person.class.isAssignableFrom(Child.class)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Person.class.isAssignableFrom(Child.class) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Person.class.isAssignableFrom(Child.class) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;//Parent class implemented the Interface 'Person'<BR>&nbsp;&nbsp;if (Person.class.isAssignableFrom(Parent.class)){<BR>&nbsp;&nbsp;&nbsp;System.out.println("Person.class.isAssignableFrom(Parent.class) is true!");<BR>&nbsp;&nbsp;} else {<BR>&nbsp;&nbsp;&nbsp;System.out.println("Person.class.isAssignableFrom(Parent.class) is false!");<BR>&nbsp;&nbsp;}<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;<BR>&nbsp;&nbsp;<BR>&nbsp;}<BR>}<BR><BR><BR><BR><BR><BR>public class Child extends Parent {</P>
<P>&nbsp;public static void main(String[] args) {<BR>&nbsp;}<BR>}<BR><BR><BR><BR>public class Parent implements Person {</P>
<P>&nbsp;public static void main(String[] args) {<BR>&nbsp;}<BR>}<BR><BR><BR><BR>public interface Person {</P>
<P>}<BR><BR><BR></P><img src ="http://www.blogjava.net/kapok/aggbug/4107.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-09 10:12 <a href="http://www.blogjava.net/kapok/archive/2005/05/09/4107.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>构造函数</title><link>http://www.blogjava.net/kapok/archive/2005/05/09/4097.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 08 May 2005 16:15:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/09/4097.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4097.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/09/4097.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4097.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4097.html</trackback:ping><description><![CDATA[<A href="http://www.pconline.com.cn/pcedu/empolder/life/0405/385800.html">http://www.pconline.com.cn/pcedu/empolder/life/0405/385800.html</A><img src ="http://www.blogjava.net/kapok/aggbug/4097.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-09 00:15 <a href="http://www.blogjava.net/kapok/archive/2005/05/09/4097.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>正确使用String类的几点注意</title><link>http://www.blogjava.net/kapok/archive/2005/05/09/4096.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 08 May 2005 16:06:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/05/09/4096.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/4096.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/05/09/4096.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/4096.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/4096.html</trackback:ping><description><![CDATA[<P style="FONT-WEIGHT: bolder; COLOR: #525252" align=center><A href="http://www.softhouse.com.cn/html/200410/2004102714063700001427.html">http://www.softhouse.com.cn/html/200410/2004102714063700001427.html</A><BR><BR>正确使用String类的几点注意</P>
<P>&nbsp;&nbsp;&nbsp; java.lang.String类对大家来说最熟悉不过了，我们写java程序很少能不用String的。本文讲述如何正确的使用String，内容主要涉及初始化、串联和比较等操作。&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; 首先我们必须清楚的一点是String类是final类型的，因此你不可以继承这个类、不能修改这个类。我们使用String的时候非常简单，通常都是String s = "hello"，但是Java API中同时提供了一个构造函数为String(String s)，因此你也可以这样使用String s = new String("hello")，对于后面这样初始化一个String的方式是不推荐的，因为new操作符意味着将会在heap上生成一个新的对象，如果这样的操作发生在一个循环中，那么代价是惨重的。比如<BR>for(int i = 0;i&lt;1000;i++)<BR>{<BR>&nbsp;&nbsp;&nbsp; String s = new String("hello");<BR>}<BR>&nbsp;&nbsp;&nbsp; 这将会创建1000个String类型的对象，由于String类是final的，因此这样的操作事实上是每次都生成了一个新的String对象的。如果你使用String s = "hello";那么就可以实现复用了，为什么可以复用呢，下面会有解释。</P>
<P>&nbsp;&nbsp;&nbsp; 当我们使用"+"实现串联操作的时候，比如String s = "hello"+"world";其实是通过StringBuffer类的append()方法实现的，最后返回String给s。如果有兴趣的话，你可以写一个简单的例子，然后用javap看看虚拟机是如何工作的。在使用串联的时候我们同样应该注意String是final类，如果你需要多次串联比如：<BR>String sql = "xxx";<BR>sql = "xxxx";<BR>sql = "ssssss";<BR>那么为了提高效率节省空间，我们应该自己用StringBuffer来替代"+";</P>
<P>&nbsp;&nbsp;&nbsp; 通常对String的比较有两种情况，一个是使用==，另一个是使用equals()方法，注意==是对对象的地址进行比较的，而String中的equals()方法是覆盖了Object类的方法，并且实现为对String对象的内容的比较。所以String s1 = new String("hello");String s2 = new String("hello")，我们对s1和s2进行上述比较的时候，前者应该返回false,因为使用new生成的是两个不同的对象。后者应该返回true因为他们的内容是一样的，都是"hello"。那么如果我们还有一个String s3 = "hello";他和s1的比较应该是什么样子的呢，答案是s1==s3为false，equals的比较位true。<STRONG><FONT color=#ff1493 size=5>事实上String类是维持着一个String池的，这个池初始化为空的，当我们String x = "hello"的时候，hello就会被放入这个池中，当我们再次String y = "hello"的时候，他首先去检查池中是否存在一个和hello内容一样的对象，如果存在的话就会把这个引用返回给y,如果不存在的话，就会创建一个并放入到池中。</FONT></STRONG>这样就实现了复用。在String有一个方法intern()他可以把String的对象放入到池冲并返回池中的对象。如果我们对s1(String s1 = new String("hello"))调用intern,s1 = s1.intern()这时候，我们再把s1和s3进行“==”的判断，你会发现结果返回true!<BR>&nbsp;&nbsp;&nbsp; 看下面的例子</P>
<P>public class StringTest<BR>{</P>
<P>&nbsp;&nbsp;&nbsp; public static void main(String[] args)<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String s1 = "hello";<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String s2 = new String("hello");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; String s3 = new String("hello");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; <BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testString(s1,s2,s3);<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; s2 = s2.intern();<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("after s2.intern");<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; testString(s1,s2,s3);&nbsp;&nbsp;&nbsp;</P>
<P>&nbsp;&nbsp;&nbsp; &nbsp;}<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static void testString(String s1,String s2,String s3)<BR>&nbsp;&nbsp;&nbsp; {<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("s1 = s2 is "+(s1==s2));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("s2 = s3 is "+(s2==s3));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("s1.equals(s2) is "+s1.equals(s2));<BR>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.out.println("s2.equals(s3) is "+s2.equals(s3));<BR>&nbsp;&nbsp;&nbsp; }<BR>}<BR>输出结果为<BR>s1 = s2 is false<BR>s2 = s3 is false<BR>s1.equals(s2) is true<BR>s2.equals(s3) is true<BR>after s2.intern<BR>s1 = s2 is true<BR>s2 = s3 is false<BR>s1.equals(s2) is true<BR>s2.equals(s3) is true</P><img src ="http://www.blogjava.net/kapok/aggbug/4096.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-05-09 00:06 <a href="http://www.blogjava.net/kapok/archive/2005/05/09/4096.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>在JSP中处理虚拟路径 </title><link>http://www.blogjava.net/kapok/archive/2005/04/27/3853.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Wed, 27 Apr 2005 08:14:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/04/27/3853.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/3853.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/04/27/3853.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/3853.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/3853.html</trackback:ping><description><![CDATA[<SPAN id=ContentLabel style="PADDING-RIGHT: 10px; DISPLAY: block; PADDING-LEFT: 10px; PADDING-BOTTOM: 0px; PADDING-TOP: 0px"><A href="http://www.ziyuan8.com/down/Article/Catalog4/12.html">http://www.ziyuan8.com/down/Article/Catalog4/12.html</A><BR><BR>摘要<BR>　　在为服务器端组件编程时，你很可能要从相对于web根的路径来取得某个文件的真实路径，但此文件实际上在站点的一个虚拟路径上。<BR>什么是虚拟路径？<BR>　　在一个web服务器上，虚拟路径将物理上分离的各文件组合在一起，放在同一个站点路径上，在应用服务器上，每个应用定位于其自己的虚拟路径上，实际上相互之间有着完美地分离。<BR>getRealPath()方法<BR>　　JSP servlet API提供了getRealPath(path)方法，返回给定虚拟路径的真实路径，如果转换错误，则返回null。<BR>getRealPath语法定义：<BR>　　public java.lang.String getRealPath(java.lang.String path)<BR>　　返回一个字符串，包含一个给定虚拟路径的真实路径。例如，虚拟路径 "/index.html"<BR>不管在服务器文件系统上具有怎样的真实路径，使用"/index.html"总可以找到它。返回的真实路径使用了相近于servlet容器(srvlet container)所在计算机或操作系统的格式，包含了适当的路径分隔符。如果servlet容器无法转换则这个方法将返回null。<BR>　　参数：<BR>　 　　 path -一个描述了虚拟路径的字符串<BR>　　返回值：<BR>　 　　 描述真实路径的字符串或者null<BR>遗憾的是，getRealPath常常返回不同的东西，这取决于服务器或jsp文件调用此方法的路径位置。<BR>一个example站点<BR>假设我们的站点组织如下：<BR>根路径包含了我们的站点的根： http://address/<BR>a_virtual目录包含了我们站点提供的虚拟路径的文件，例如：<BR>http://addess/virtual_dir/<BR>我们查找file1.txt和file2.txt的真实路径，它们一个在站点根路径下，一个在虚拟路径下。<BR>getRealPath("/file1.txt") 应该返回“C:\site\site_root\file1.txt"，<BR>getRealPath("/virtual_dir/file2.txt")应该返回"C:\site\a_virtual\file2.txt"<BR>getRealPath("/file3.txt")应该返回null，因为这个文件不存在。<BR>但getRealPath()并不总是返回同样的结果，这还取决与你使用的js引擎。<BR>JSP引擎<BR>Tomcat 3.1<BR>Tomcat返回的结果具有应用的独立性（application dependant）：<BR>　　它取决与调用getRealPath方法的那个jsp文件所在的位置。<BR>　　实际上，当page1.jsp (位于站点根处)对file1.txt和file2.txt调用txtgetRealPath(), 它返回正确的结果。(这是在tomcat 3.1, 3.0版则对file2.txt返回错误的路径)<BR>但是当page2.jsp(位于另一个应用,在一个虚拟路径中)调用getRealPath，它返回了错误的路径：它连接了该jsp文件所在的路径和请求的虚拟路径。<BR>　　例如，从page2.jsp中调用getRealPath(/file1.txt)将返回 C:\site\a_virtual\file1.txt。<BR>　　这一行为其实是使不同的应用相互独立的典型的处理方法。<BR>JRun 2.3.3和INPRISE APPLICATION SERVER 4.0 (IAS)<BR>JRun和IAS对file1.txt和file2.txt都返回正确的结果。<BR>　　然而所有这些引擎有一个共同的行为： 当getRealPath处理不存在的文件时，它们都不返回null！<BR>解决之道<BR>　　既然getRealPath总是返回一个路径，我们怎么知道它是否正确呢？最简单的方法是检查这个返回的路径是否存在。<BR>　　这就是isVirtual方法要做的：在对一个给定的文件调用getRealPath以后，它使用了java.io来<BR>存取这个文件，于是就可以知道它是否存在。<BR>/**<BR>* isVirtual<BR>*<BR>* Check if the path name is a virtual or not.<BR>*<BR>* @param pathName The name of the path to check.<BR>*/<BR>private boolean isVirtual(String pathName) {<BR>　 // Check if it is a virtual path<BR>　 if (m_application.getRealPath(pathName)!=null) {<BR>　 　 java.io.File virtualFile = new java.io.File(m_application.getRealPath(pathName));<BR>　 　 if (virtualFile.exists()) {return true;}<BR>　 　 else {return false;}<BR>　 }<BR>　 else {return false;}<BR>}</SPAN><img src ="http://www.blogjava.net/kapok/aggbug/3853.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-04-27 16:14 <a href="http://www.blogjava.net/kapok/archive/2005/04/27/3853.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>webwork 阅读备忘录</title><link>http://www.blogjava.net/kapok/archive/2005/04/19/3442.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Tue, 19 Apr 2005 02:19:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/04/19/3442.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/3442.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/04/19/3442.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/3442.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/3442.html</trackback:ping><description><![CDATA[<A href="http://wiki.opensymphony.com/display/WW/Templates">http://wiki.opensymphony.com/display/WW/Templates</A><img src ="http://www.blogjava.net/kapok/aggbug/3442.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-04-19 10:19 <a href="http://www.blogjava.net/kapok/archive/2005/04/19/3442.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java模式设计之单例模式（四）</title><link>http://www.blogjava.net/kapok/archive/2005/04/18/3414.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 18 Apr 2005 06:41:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/04/18/3414.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/3414.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/04/18/3414.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/3414.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/3414.html</trackback:ping><description><![CDATA[<TABLE height=105 cellSpacing=0 cellPadding=0 width=760 align=center border=0>
<TBODY>
<TR>
<TD class=title vAlign=center align=middle height=55><FONT class=f22><B><A href="http://www.uml.org.cn/j2ee/j2ee122404.htm">http://www.uml.org.cn/j2ee/j2ee122404.htm</A><BR><BR>Java模式设计之单例模式（四）</B></FONT><FONT color=#ff0000 size=3><B><!-- #EndEditable --></B></FONT></TD></TR>
<TR>
<TD class=formtitle align=middle height=50><!-- #BeginEditable "2" --><FONT color=#000080><SPAN style="FONT-FAMILY: 新細明體; mso-bidi-font-family: SimSun; mso-font-kerning: 0pt; mso-ascii-font-family: Comic Sans MS; mso-hansi-font-family: Comic Sans MS">阎宏 作者授权</SPAN></FONT><SPAN lang=EN-US style="COLOR: black; FONT-FAMILY: 'Comic Sans MS'; mso-bidi-font-family: SimSun; mso-font-kerning: 0pt"><?XML:NAMESPACE PREFIX = O /><O:P> </O:P></SPAN><!-- #EndEditable --></TD></TR></TBODY></TABLE>
<TABLE height=65 cellSpacing=0 cellPadding=0 width=760 align=center border=0>
<TBODY>
<TR>
<TD class=content height=65><!-- #BeginEditable "3" -->
<TABLE width="85%" align=center border=0>
<TBODY>
<TR>
<TD class=content>　　不完全的单例类&nbsp;<BR><BR>　　什么是不完全的单例类<BR><BR>　　估计有些读者见过下面这样的“不完全”的单例类。<BR><BR>　　代码清单10：“不完全”单例类<BR><BR>package com.javapatterns.singleton.demos;&nbsp;<BR>public class LazySingleton&nbsp;<BR>{&nbsp;<BR>private static LazySingleton&nbsp;<BR>m_instance = null;&nbsp;<BR>/**&nbsp;<BR>* 公开的构造子，外界可以直接实例化&nbsp;<BR>*/&nbsp;<BR>public LazySingleton() { }&nbsp;<BR>/**&nbsp;<BR>* 静态工厂方法&nbsp;<BR>* @return 返还LazySingleton 类的惟一实例&nbsp;<BR>*/&nbsp;<BR>synchronized public static&nbsp;<BR>LazySingleton getInstance()&nbsp;<BR>{&nbsp;<BR>if (m_instance == null)&nbsp;<BR>{&nbsp;<BR>m_instance = new LazySingleton();&nbsp;<BR>}&nbsp;<BR>return m_instance;&nbsp;<BR>}<BR>}&nbsp;<BR><BR>　　上面的代码乍看起来是一个“懒汉”式单例类，仔细一看，发现有一个公开的构造子。由于外界可以使用构造子创建出任意多个此类的实例，这违背了单例类只能有一个（或有限个）实例的特性，因此这个类不是完全的单例类。这种情况有时会出现，比如javax.swing.TimerQueue 便是一例，关于这个类，请参见《Java与模式》一书中的“观察者模式与Swing 定时器” 一章。<BR><BR>　　造成这种情况出现的原因有以下几种可能：<BR><BR>　　（1） 初学者的错误。许多初学者没有认识到单例类的构造子不能是公开的，因此犯下这个错误。有些初学Java 语言的学员甚至不知道一个Java 类的构造子可以不是公开的。在 这种情况下，设计师可能会通过自我约束，也就是说不去调用构造子的办法，将这个不完全的单例类在使用中作为一个单例类使用。<BR><BR>　　在这种情况下，一个简单的矫正办法，就是将公开的构造子改为私有的构造子。<BR><BR>　　（2） 当初出于考虑不周，将一个类设计成为单例类，后来发现此类应当有多于一个的实例。为了弥补错误， 干脆将构造子改为公开的，以便在需要多于一个的实例时， 可以随时调用构造子创建新的实例。要纠正这种情况较为困难，必须根据具体情况做出改进的决定。如果一个类在最初被设计成为单例类，但后来发现实际上此类应当有有限多个实例，这时候应当考虑是否将单例类改为多例类（Multiton）。<BR><BR>　　（3）设计师的Java 知识很好，而且也知道单例模式的正确使用方法，但是还是有意使用这种不完全的单例模式，因为他意在使用一种“改良”的单例模式。这时候， 除去共有的构造子不符合单例模式的要求之外，这个类必须是很好的单例模式。<BR><BR>　　默认实例模式<BR><BR>　　有些设计师将这种不完全的单例模式叫做“默认实例模式”（Default Instance Pattern）。在所谓的“ 默认实例模式”里面， 一个类提供静态的方法，如同单例模式一样， 同时又提供一个公开的构造子，如同普通的类一样。<BR><BR>　　这样做的惟一好处是，这种模式允许客户端选择如何将类实例化：创建新的自己独有的实例，或者使用共享的实例。这样一来，由于没有任何的强制性措施，客户端的选择不一定是合理的选择。其结果是设计师往往不会花费时间在如何提供最好的选择上，而是不恰当地将这种选择交给客户端的程序员，这样必然会导致不理想的设计和欠考虑的实现。<BR><BR>　　本文建议读者不要这样做。 
<P><SPAN class=f14>　　<B>相关模式</B><BR><BR>　　有一些模式可以使用单例模式，如抽象工厂模式可以使用单例模式，将具体工厂类设计成单例类；建造模式可以使用单例模式，将具体建造类设计成单例类。<BR><BR>　　<B>多例（Multiton）模式</B><BR><BR>　　正如同本章所说的，单例模式的精神可以推广到多于一个实例的情况。这时候这种类叫做多例类，这种模式叫做多例模式。单例类（左）和多例类（右）的类图如下所示。<BR><BR><IMG onerror="this.src='http://www.yesky.com/image20010518/61675.jpg';" hspace=3 src="http://www.yesky.com/image20010518/61675.jpg" align=center vspace=1 border=1><BR><BR>　　关于多例模式，请见《Java与模式》一书中的“专题：多例（Multiton）模式与多语言支持”一章。<BR><BR>　　<STRONG>简单工厂（Simple Factory）模式</STRONG><BR><BR>　　单例模式使用了简单工厂模式（又称为静态工厂方法模式）来提供自己的实例。在上面ConfigManager 例子的代码中， 静态工厂方法getInstance() 就是静态工厂方法。在java.awt.Toolkit 类中，getDefaultToolkit() 方法就是静态工厂方法。简单工厂模式的简略类图如下所示。<BR><BR><IMG onerror="this.src='http://www.yesky.com/image20010518/61674.jpg';" hspace=3 src="http://www.yesky.com/image20010518/61674.jpg" align=center vspace=1 border=1><BR><BR>　　本章讨论了单例模式的结构和实现方法。<BR><BR>　　单例模式是一个看上去很简单的模式，很多设计师最先学会的往往是单例模式。然而，随着Java 系统日益变得复杂化和分散化，单例模式的使用变得比过去困难。本书提醒读者在分散式的Java 系统中使用单例模式时，尽量不要使用有状态的。<BR><BR>　　<B>问答题</B><BR><BR>　　1. 为什么不使用一个静态的“全程”原始变量，而要建一个类？一个静态的原始变量当然只能有一个值，自然而然不就是“单例”的吗？<BR><BR>　　2. 举例说明如何调用EagerSingleton 类。<BR><BR>　　3. 举例说明如何调用RegSingleton 类和RegSingletonChild 类。<BR><BR>　　4. 请问java.lang.Math 类和java.lang.StrictMath 类是否是单例模式？<BR><BR>　　5. 我们公司只购买了一个JDBC 驱动软件的单用户使用许可，可否使用单例模式管理通过JDBC 驱动软件连接的数据库？<BR><BR></SPAN><BR></P><SPAN class=f14>　　<B>问答题答案</B><BR><BR>　　1. 单例模式可以提供很复杂的逻辑，而一个原始变量不能自已初始化，不可能有继承的关系，没有内部结构。因此单例模式有很多优越之处。<BR><BR>　　在Java 语言里并没有真正的“全程”变量，一个变量必须属于某一个类或者某一个实例。而在复杂的程序当中，一个静态变量的初始化发生在哪里常常是一个不易确定的问题。当然，使用“全程”原始变量并没有什么错误，就好像选择使用Fortran 语言而非Java语言编程并不是一种对错的问题一样。<BR><BR>　　2. 几种单例类的使用方法如下。<BR><BR>　　代码清单11：几种单例类的使用方法<BR><BR>public class RegSingletonTest<BR>{<BR>public static void main(String[] args)<BR>{<BR>//(1) Test eager<BR>System.out.println( EagerSingleton.getInstance());<BR>//(2) Test reg<BR>System.out.println(<BR>RegSingleton.getInstance(<BR>"com.javapatterns.singleton.demos.RegSingleton").about());<BR>System.out.println( RegSingleton.getInstance(null).about() );<BR>System.out.println(<BR>RegSingleton.getInstance(<BR>"com.javapatterns.singleton.demos.RegSingletonChild").about());<BR>System.out.println( RegSingletonChild.getInstance().about());<BR>}<BR>}<BR>　　3. 见上题答案。<BR><BR>　　4. 它们都不是单例类。原因如下：<BR><BR>　　这两个类均有一个私有的构造子。但是这仅仅是单例模式的必要条件，而不是充分条件。回顾在本章开始提出的单例模式的三个特性可以看出，无论是Math 还是StrictMath 都没有为外界提供任何自身的实例。实际上，这两个类都是被设计来提供静态工厂方法和常量的，因此从来就不需要它们的实例，这才是它们的构造子是私有的原因。Math和StrictMath 类的类图如下所示。<BR><BR><IMG onerror="this.src='http://www.yesky.com/image20010518/61673.jpg';" hspace=3 src="http://www.yesky.com/image20010518/61673.jpg" align=center vspace=1 border=1><BR><BR>　　5. 这样做是可行的，只是必须注意当使用在分散式系统中的时候，不一定能保证单例类实例的惟一性。</SPAN> 
<P><SPAN class=f14>　　<B>附录：双重检查成例的研究</B><BR><BR>　　成例是一种代码层次上的模式，是在比设计模式的层次更具体的层次上的代码技巧。成例往往与编程语言密切相关。双重检查成例（Double Check Idiom ）是从C 语言移植过来的一种代码模式。在C 语言里，双重检查成例常常用在多线程环境中类的晚实例化（Late Instantiation）里。<BR><BR>　　本节之所以要介绍这个成例（严格来讲，是介绍为什么这个成例不成立）， 是因为有很多人认为双重检查成例可以使用在“懒汉”单例模式里面。<BR><BR>　　<B>什么是双重检查成例</B><BR><BR>　　为了解释什么是双重检查成例，请首先看看下面没有使用任何线程安全考虑的错误例子。<BR><BR>　　从单线程的程序谈起<BR><BR>　　首先考虑一个单线程的版本。<BR><BR>　　代码清单13：没有使用任何线程安全措施的一个例子<BR><BR>// Single threaded version<BR>class Foo<BR>{<BR>　private Helper helper = null;<BR>　public Helper getHelper()<BR>　{<BR>　　if (helper == null)<BR>　　{<BR>　　　helper = new Helper();<BR>　　}<BR>　　return helper;<BR>　}<BR>　// other functions and members...<BR>}<BR>　　这是一个错误的例子，详情请见下面的说明。<BR><BR>　　写出这样的代码，本意显然是要保持在整个JVM 中只有一个Helper 的实例；因此，才会有if (helper == null) 的检查。非常明显的是，如果在多线程的环境中运行，上面的代码会有两个甚至两个以上的Helper 对象被创建出来，从而造成错误。<BR><BR>　　但是，想像一下在多线程环境中的情形就会发现，如果有两个线程A 和B 几乎同时到达if (helper == null)语句的外面的话，假设线程A 比线程B 早一点点，那么：<BR><BR>　　（1）A 会首先进入if (helper == null) 块的内部，并开始执行new Helper() 语句。此时，helper 变量仍然是null，直到线程A 的new Helper() 语句返回并给helper 变量赋值为止。<BR><BR>　　（2） 但是，线程B 并不会在if (helper == null)语句的外面等待，因为此时helper == null 是成立的，它会马上进入if (helper == null)语句块的内部。这样，线程B 会不可避免地执行helper = new Helper();语句，从而创建出第二个实例来。<BR><BR>　　（3）线程A 的helper = new Helper();语句执行完毕后，helper 变量得到了真实的对象引用，(helper == null)不再为真。第三个线程不会再进入if (helper == null) 语句块的内部了。<BR><BR>　　（4）线程B 的helper = new Helper(); 语句也执行完毕后，helper 变量的值被覆盖。但是第一个Helper 对象被线程A 引用的事实不会改变。<BR><BR>　　这时，线程A 和B 各自拥有一个独立的Helper 对象，而这是错误的。<BR><BR>　　<B>线程安全的版本</B><BR><BR>　　为了克服没有线程安全的缺点，下面给出一个线程安全的例子。<BR><BR>　　代码清单14：这是一个正确的答案<BR><BR>// Correct multithreaded version<BR>class Foo<BR>{<BR>　private Helper helper = null;<BR>　public synchronized Helper getHelper()<BR>　{<BR>　　if (helper == null)<BR>　　{<BR>　　　helper = new Helper();<BR>　　　return helper;<BR>　　}<BR>　}<BR>　// other functions and members...<BR>}<BR>　　显然，由于整个静态工厂方法都是同步化的，因此，不会有两个线程同时进入这个方法。因此，当线程A 和B 作为第一批调用者同时或几乎同时调用此方法时：<BR><BR>　　（1）早到一点的线程A 会率先进入此方法，同时线程B 会在方法外部等待。<BR><BR>　　（2） 对线程A 来说，helper 变量的值是null ，因此helper = new Helper(); 语句会被执行。<BR><BR>　　（3）线程A 结束对方法的执行，helper 变量的值不再是null。<BR><BR>　　（4）线程B 进入此方法，helper 变量的值不再是null ，因此helper = new Helper(); 语句不会被执行。线程B 取到的是helper 变量所含有的引用，也就是对线程A 所创立的Helper 实例的引用。<BR><BR>　　显然，线程A 和B 持有同一个Helper 实例，这是正确的。</SPAN> 
<P><SPAN class=f14>　　<B>画蛇添足的“双重检查”</B><BR><BR>　　但是，仔细审察上面的正确答案会发现，同步化实际上只在helper 变量第一次被赋值之前才有用。在helper 变量有了值以后，同步化实际上变成了一个不必要的瓶颈。如果能有一个方法去掉这个小小的额外开销，不是更加完美了吗？因此，就有了下面这个设计“巧妙”的双重检查成例。在读者向下继续读之前，有必要提醒一句：正如本小节的标题所标明的那样，这是一个反面教材，因为双重检查成例在Java 编译器里无法实现。<BR><BR>　　代码清单15：使用双重检查成例的懒汉式单例模式<BR><BR>
<TABLE width="100%" bgColor=#ffffff>
<TBODY>
<TR>
<TD>// Broken multithreaded version<BR>// "Double-Checked Locking" idiom<BR>class Foo<BR>{<BR>　private Helper helper = null;<BR>　public Helper getHelper()<BR>　{<BR>　　if (helper == null) //第一次检查(位置1)<BR>　　{<BR>　　　//这里会有多于一个的线程同时到达 (位置2)<BR>　　　synchronized(this)<BR>　　　{<BR>　　　　//这里在每个时刻只能有一个线程 (位置3)<BR>　　　　if (helper == null) //第二次检查 (位置4)<BR>　　　　{<BR>　　　　　helper = new Helper();<BR>　　　　}<BR>　　　}<BR>　　}<BR>　　return helper;<BR>　}<BR>　// other functions and members...<BR>}</TD></TR></TBODY></TABLE><BR>　　这是一个错误的例子，详情请见下面的解释。<BR><BR>　　对于初次接触双重检查成例的读者来说，这个技巧的思路并不明显易懂。因此，本节在这里给出一个详尽的解释。同样，这里假设线程A 和B 作为第一批调用者同时或几乎同时调用静态工厂方法。<BR><BR>　　（1） 因为线程A 和B 是第一批调用者，因此，当它们进入此静态工厂方法时，helper 变量是null。因此，线程A 和B 会同时或几乎同时到达位置1。<BR><BR>　　（2）假设线程A 会首先到达位置2，并进入synchronized(this) 到达位置3。这时，由于synchronized(this) 的同步化限制，线程B 无法到达位置3，而只能在位置2 等候。<BR><BR>　　（3）线程A 执行helper = new Helper() 语句，使得helper 变量得到一个值，即对一个Helper 对象的引用。此时，线程B 只能继续在位置2 等候。<BR><BR>　　（4）线程A 退出synchronized(this) ，返回Helper 对象，退出静态工厂方法。<BR><BR>　　（5）线程B 进入synchronized(this) 块，达到位置3，进而达到位置4。由于helper 变量已经不是null 了，因此线程B 退出synchronized(this)，返回helper 所引用的Helper 对象(也就是线程A 所创建的Helper 对象)，退出静态工厂方法。<BR><BR>　　到此为止，线程A 和线程B 得到了同一个Helper 对象。可以看到，在上面的方法<BR><BR>　　getInstance() 中，同步化仅用来避免多个线程同时初始化这个类，而不是同时调用这个静态工厂方法。如果这是正确的，那么使用这一个成例之后，“ 懒汉式”单例类就可以摆脱掉同步化瓶颈，达到一个很妙的境界。<BR><BR>　　代码清单16：使用了双重检查成例的懒汉式单例类<BR><BR><SPAN class=f14>public class LazySingleton<BR>{<BR>　private static LazySingleton m_instance = null;<BR>　private LazySingleton() { }<BR>　/**<BR>　* 静态工厂方法<BR>　*/<BR>　public static LazySingleton getInstance()<BR>　{<BR>　　if (m_instance == null)<BR>　　{<BR>　　//More than one threads might be here!!!<BR>　　synchronized(LazySingleton.class)<BR>　　{<BR>　　　if (m_instance == null)<BR>　　　{<BR>　　　　m_instance = new LazySingleton();<BR>　　　}<BR>　　}<BR>　}<BR>　return m_instance;<BR>}<BR>}</SPAN><BR>　　 这是一个错误的例子，请见下面的解释。<BR><BR>　　第一次接触到这个技巧的读者必定会有很多问题，诸如第一次检查或者第二次检查可不可以省掉等。回答是：按照多线程的原理和双重检查成例的预想方案，它们是不可以省掉的。本节不打算讲解的原因在于双重检查成例在Java 编译器中根本不能成立。<BR><BR>　　<B>双重检查成例对Java 语言编译器不成立</B><BR><BR>　　令人吃惊的是，在C 语言里得到普遍应用的双重检查成例在多数的Java 语言编译器里面并不成立[BLOCH01, GOETZ01, DCL01] 。上面使用了双重检查成例的“懒汉式”单例类，不能工作的基本原因在于，在Java 编译器中，LazySingleton 类的初始化与m_instance 变量赋值的顺序不可预料。如果一个线程在没有同步化的条件下读取m_instance 引用，并调用这个对象的方法的话，可能会发现对象的初始化过程尚未完成，从而造成崩溃。<BR><BR>　　文献[BLOCH01] 指出：一般而言，双重检查成立对Java 语言来说是不成立的。<BR><BR>　　<B>给读者的一点建议</B><BR><BR>　　有很多非常聪明的人在这个成例的Java 版本上花费了非常多的时间，到现在为止人们得出的结论是：一般而言，双重检查成例无法在现有的Java 语言编译器里工作[BLOCH01, GOETZ01, DCL01] 。<BR><BR>　　读者可能会问，是否有可能通过某种技巧对上面的双重检查的实现代码加以修改，从而使某种形式的双重检查成例能在Java 编译器下工作呢？这种可能性当然不能排除，但是除非读者对此有特别的兴趣，建议不要在这上面花费太多的时间。<BR><BR>　　在一般情况下使用饿汉式单例模式或者对整个静态工厂方法同步化的懒汉式单例模式足以解决在实际设计工作中遇到的问题。</SPAN> 
<P><BR></P></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/3414.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-04-18 14:41 <a href="http://www.blogjava.net/kapok/archive/2005/04/18/3414.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>单例模式完全剖析(3)---- 探究简单却又使人迷惑的单例模式</title><link>http://www.blogjava.net/kapok/archive/2005/04/18/3410.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Mon, 18 Apr 2005 06:33:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/04/18/3410.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/3410.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/04/18/3410.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/3410.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/3410.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width=760 align=center border=0>
<TBODY>
<TR>
<TD class=title vAlign=center align=middle height=56><B><FONT color=#ff0000 size=3><A href="http://www.uml.org.cn/j2ee/200501241.htm">http://www.uml.org.cn/j2ee/200501241.htm</A><BR><BR>单例模式完全剖析(3)---- 探究简单却又使人迷惑的单例模式<BR><!-- #EndEditable --></FONT></B></TD></TR>
<TR>
<TD class=formtitle align=middle height=40><!-- #BeginEditable "2" -->kevin 翻译 选自：java研究组织<!-- #EndEditable --></TD></TR></TBODY></TABLE>
<TABLE height=65 cellSpacing=0 cellPadding=0 width=760 align=center border=0>
<TBODY>
<TR>
<TD class=content height=65><!-- #BeginEditable "3" -->
<TABLE class=content width="85%" align=center border=0>
<TBODY>
<TR>
<TD class=content vAlign=top><SPAN class=myp11><FONT id=zoom></FONT></SPAN><SPAN class=p11b></SPAN><B>使用注册表 <BR><BR>使用一个单例类注册表可以：<BR>
<UL>
<LI>在运行期指定单例类<BR>
<LI>防止产生多个单例类子类的实例<BR>在例8的单例类中，保持了一个通过类名进行注册的单例类注册表：<BR>例8&nbsp;带注册表的单例类<BR>
<DIV class=codeStyle>
<OL>
<LI><FONT color=#0000ff>import</FONT>&nbsp;java.util.<FONT class=classLink><U>HashMap</U></FONT>; 
<LI><FONT color=#0000ff>import</FONT>&nbsp;org.apache.log4j.<FONT color=#ff0000>Logger</FONT>; 
<LI>　 
<LI><FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>class</FONT>&nbsp;Singleton&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT class=classLink><U>HashMap</U></FONT>&nbsp;map&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;<FONT class=classLink><U>HashMap</U></FONT>(); 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT color=#ff0000>Logger</FONT>&nbsp;logger&nbsp;=&nbsp;<FONT color=#ff0000>Logger</FONT>.getRootLogger(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>protected</FONT>&nbsp;Singleton()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//&nbsp;Exists&nbsp;only&nbsp;to&nbsp;thwart&nbsp;instantiation</FONT></I> 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT color=#0000ff>synchronized</FONT>&nbsp;Singleton&nbsp;getInstance(<FONT class=classLink><U>String</U></FONT>&nbsp;classname)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>if</FONT>(classname&nbsp;==&nbsp;<FONT color=#0000ff>null</FONT>)&nbsp;<FONT color=#0000ff>throw</FONT>&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;<FONT class=classLink><U>IllegalArgumentException</U></FONT>(<FONT color=#ff33ff>"Illegal&nbsp;classname"</FONT>); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Singleton&nbsp;singleton&nbsp;=&nbsp;(Singleton)map.get(classname); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>if</FONT>(singleton&nbsp;!=&nbsp;<FONT color=#0000ff>null</FONT>)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(<FONT color=#ff33ff>"got&nbsp;singleton&nbsp;from&nbsp;map:&nbsp;"</FONT>&nbsp;+&nbsp;singleton); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;singleton; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>if</FONT>(classname.equals(<FONT color=#ff33ff>"SingeltonSubclass_One"</FONT>)) 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleton&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;SingletonSubclass_One();&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>else</FONT>&nbsp;<FONT color=#0000ff>if</FONT>(classname.equals(<FONT color=#ff33ff>"SingeltonSubclass_Two"</FONT>)) 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleton&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;SingletonSubclass_Two(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map.put(classname,&nbsp;singleton); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(<FONT color=#ff33ff>"created&nbsp;singleton:&nbsp;"</FONT>&nbsp;+&nbsp;singleton); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;singleton; 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//&nbsp;Assume&nbsp;functionality&nbsp;follows&nbsp;that's&nbsp;attractive&nbsp;to&nbsp;inherit</FONT></I> 
<LI>} </LI></OL></DIV><BR>这段代码的基类首先创建出子类的实例，然后把它们存储在一个Map中。但是基类却得付出很高的代价因为你必须为每一个子类替换它的getInstance()方法。幸运的是我们可以使用反射处理这个问题。<BR>
<H3>使用反射</H3><BR>在例9的带注册表的单例类中，使用反射来实例化一个特殊的类的对象。与例8相对的是通过这种实现，Singleton.getInstance()方法不需要在每个被实现的子类中重写了。<BR>例9&nbsp;使用反射实例化单例类<BR>
<DIV class=codeStyle>
<OL>
<LI><FONT color=#0000ff>import</FONT>&nbsp;java.util.<FONT class=classLink><U>HashMap</U></FONT>; 
<LI><FONT color=#0000ff>import</FONT>&nbsp;org.apache.log4j.<FONT color=#ff0000>Logger</FONT>; 
<LI>　 
<LI><FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>class</FONT>&nbsp;Singleton&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT class=classLink><U>HashMap</U></FONT>&nbsp;map&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;<FONT class=classLink><U>HashMap</U></FONT>(); 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT color=#ff0000>Logger</FONT>&nbsp;logger&nbsp;=&nbsp;<FONT color=#ff0000>Logger</FONT>.getRootLogger(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>protected</FONT>&nbsp;Singleton()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//&nbsp;Exists&nbsp;only&nbsp;to&nbsp;thwart&nbsp;instantiation</FONT></I> 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT color=#0000ff>synchronized</FONT>&nbsp;Singleton&nbsp;getInstance(<FONT class=classLink><U>String</U></FONT>&nbsp;classname)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Singleton&nbsp;singleton&nbsp;=&nbsp;(Singleton)map.get(classname); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>if</FONT>(singleton&nbsp;!=&nbsp;<FONT color=#0000ff>null</FONT>)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(<FONT color=#ff33ff>"got&nbsp;singleton&nbsp;from&nbsp;map:&nbsp;"</FONT>&nbsp;+&nbsp;singleton); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;singleton; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>try</FONT>&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleton&nbsp;=&nbsp;(Singleton)<FONT class=classLink><U>Class</U></FONT>.forName(classname).newInstance(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>ClassNotFoundException</U></FONT>&nbsp;cnf)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"Couldn't&nbsp;find&nbsp;class&nbsp;"</FONT>&nbsp;+&nbsp;classname);&nbsp;&nbsp;&nbsp;&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>InstantiationException</U></FONT>&nbsp;ie)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"Couldn't&nbsp;instantiate&nbsp;an&nbsp;object&nbsp;of&nbsp;type&nbsp;"</FONT>&nbsp;+&nbsp;classname);&nbsp;&nbsp;&nbsp;&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>IllegalAccessException</U></FONT>&nbsp;ia)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"Couldn't&nbsp;access&nbsp;class&nbsp;"</FONT>&nbsp;+&nbsp;classname);&nbsp;&nbsp;&nbsp;&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map.put(classname,&nbsp;singleton); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(<FONT color=#ff33ff>"created&nbsp;singleton:&nbsp;"</FONT>&nbsp;+&nbsp;singleton); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;singleton; 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV><BR><BR>关于单例类的注册表应该说明的是：它们应该被封装在它们自己的类中以便最大限度的进行复用。<BR><BR>
<H3>封装注册表</H3><BR>例10列出了一个单例注册表类。<BR>例10&nbsp;一个SingletonRegistry类<BR>
<DIV class=codeStyle>
<OL>
<LI><FONT color=#0000ff>import</FONT>&nbsp;java.util.<FONT class=classLink><U>HashMap</U></FONT>; 
<LI><FONT color=#0000ff>import</FONT>&nbsp;org.apache.log4j.<FONT color=#ff0000>Logger</FONT>; 
<LI>　 
<LI><FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>class</FONT>&nbsp;SingletonRegistry&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;SingletonRegistry&nbsp;REGISTRY&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;SingletonRegistry(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT class=classLink><U>HashMap</U></FONT>&nbsp;map&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;<FONT class=classLink><U>HashMap</U></FONT>(); 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT color=#ff0000>Logger</FONT>&nbsp;logger&nbsp;=&nbsp;<FONT color=#ff0000>Logger</FONT>.getRootLogger(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>protected</FONT>&nbsp;SingletonRegistry()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//&nbsp;Exists&nbsp;to&nbsp;defeat&nbsp;instantiation</FONT></I> 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT color=#0000ff>synchronized</FONT>&nbsp;<FONT class=classLink><U>Object</U></FONT>&nbsp;getInstance(<FONT class=classLink><U>String</U></FONT>&nbsp;classname)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT class=classLink><U>Object</U></FONT>&nbsp;singleton&nbsp;=&nbsp;map.get(classname); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>if</FONT>(singleton&nbsp;!=&nbsp;<FONT color=#0000ff>null</FONT>)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;singleton; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>try</FONT>&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;singleton&nbsp;=&nbsp;<FONT class=classLink><U>Class</U></FONT>.forName(classname).newInstance(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(<FONT color=#ff33ff>"created&nbsp;singleton:&nbsp;"</FONT>&nbsp;+&nbsp;singleton); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>ClassNotFoundException</U></FONT>&nbsp;cnf)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"Couldn't&nbsp;find&nbsp;class&nbsp;"</FONT>&nbsp;+&nbsp;classname);&nbsp;&nbsp;&nbsp;&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>InstantiationException</U></FONT>&nbsp;ie)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"Couldn't&nbsp;instantiate&nbsp;an&nbsp;object&nbsp;of&nbsp;type&nbsp;"</FONT>&nbsp;+&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classname);&nbsp;&nbsp;&nbsp;&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>IllegalAccessException</U></FONT>&nbsp;ia)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"Couldn't&nbsp;access&nbsp;class&nbsp;"</FONT>&nbsp;+&nbsp;classname);&nbsp;&nbsp;&nbsp;&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;map.put(classname,&nbsp;singleton); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;singleton; 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV><BR>注意我是把SingletonRegistry类作为一个单例模式实现的。我也通用化了这个注册表以便它能存储和取回任何类型的对象。例11显示了的Singleton类使用了这个注册表。<BR>例11&nbsp;使用了一个封装的注册表的Singleton类<BR>
<DIV class=codeStyle>
<OL>
<LI><FONT color=#0000ff>import</FONT>&nbsp;java.util.<FONT class=classLink><U>HashMap</U></FONT>; 
<LI><FONT color=#0000ff>import</FONT>&nbsp;org.apache.log4j.<FONT color=#ff0000>Logger</FONT>; 
<LI>　 
<LI><FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>class</FONT>&nbsp;Singleton&nbsp;{ 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>protected</FONT>&nbsp;Singleton()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//&nbsp;Exists&nbsp;only&nbsp;to&nbsp;thwart&nbsp;instantiation.</FONT></I> 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;Singleton&nbsp;getInstance()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;(Singleton)SingletonRegistry.REGISTRY.getInstance(classname); 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV><BR>上面的Singleton类使用那个注册表的唯一实例通过类名取得单例对象。<BR>现在我们已经知道如何实现线程安全的单例类和如何使用一个注册表去在运行期指定单例类名，接着让我们考查一下如何安排类载入器和处理序列化。<BR>
<H3>Classloaders</H3><BR>在许多情况下，使用多个类载入器是很普通的--包括servlet容器--所以不管你在实现你的单例类时是多么小心你都最终可以得到多个单例类的实例。如果你想要确保你的单例类只被同一个的类载入器装入，那你就必须自己指定这个类载入器;例如：<BR>
<DIV class=codeStyle>
<OL>
<LI><FONT color=#0000ff>private</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT class=classLink><U>Class</U></FONT>&nbsp;getClass(<FONT class=classLink><U>String</U></FONT>&nbsp;classname)&nbsp; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>throws</FONT>&nbsp;<FONT class=classLink><U>ClassNotFoundException</U></FONT>&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT class=classLink><U>ClassLoader</U></FONT>&nbsp;classLoader&nbsp;=&nbsp;<FONT class=classLink><U>Thread</U></FONT>.currentThread().getContextClassLoader(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>if</FONT>(classLoader&nbsp;==&nbsp;<FONT color=#0000ff>null</FONT>) 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;classLoader&nbsp;=&nbsp;Singleton.<FONT color=#0000ff>class</FONT>.getClassLoader(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;(classLoader.loadClass(classname)); 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV><BR>这个方法会尝试把当前的线程与那个类载入器相关联;如果classloader为null，这个方法会使用与装入单例类基类的那个类载入器。这个方法可以用Class.forName()代替。<BR>
<H3>序列化</H3><BR>如果你序列化一个单例类，然后两次重构它，那么你就会得到那个单例类的两个实例，除非你实现readResolve()方法，像下面这样：<BR>例12&nbsp;一个可序列化的单例类<BR>
<DIV class=codeStyle>
<OL>
<LI><FONT color=#0000ff>import</FONT>&nbsp;org.apache.log4j.<FONT color=#ff0000>Logger</FONT>; 
<LI>　 
<LI><FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>class</FONT>&nbsp;Singleton&nbsp;<FONT color=#0000ff>implements</FONT>&nbsp;java.io.<FONT class=classLink><U>Serializable</U></FONT>&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;Singleton&nbsp;INSTANCE&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;Singleton(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>protected</FONT>&nbsp;Singleton()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<I><FONT color=#339900>//&nbsp;Exists&nbsp;only&nbsp;to&nbsp;thwart&nbsp;instantiation.</FONT></I> 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>[b]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;<FONT class=classLink><U>Object</U></FONT>&nbsp;readResolve()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;INSTANCE; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}[/b]} </LI></OL></DIV><BR>上面的单例类实现从readResolve()方法中返回一个唯一的实例;这样无论Singleton类何时被重构，它都只会返回那个相同的单例类实例。<BR>例13测试了例12的单例类：<BR>例13&nbsp;测试一个可序列化的单例类<BR>
<DIV class=codeStyle>
<OL>
<LI><FONT color=#0000ff>import</FONT>&nbsp;java.io.*; 
<LI><FONT color=#0000ff>import</FONT>&nbsp;org.apache.log4j.<FONT color=#ff0000>Logger</FONT>; 
<LI><FONT color=#0000ff>import</FONT>&nbsp;junit.framework.<FONT class=classLink><U>Assert</U></FONT>; 
<LI><FONT color=#0000ff>import</FONT>&nbsp;junit.framework.<FONT class=classLink><U>TestCase</U></FONT>; 
<LI>　 
<LI><FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>class</FONT>&nbsp;SingletonTest&nbsp;<FONT color=#0000ff>extends</FONT>&nbsp;<FONT class=classLink><U>TestCase</U></FONT>&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;Singleton&nbsp;sone&nbsp;=&nbsp;<FONT color=#0000ff>null</FONT>,&nbsp;stwo&nbsp;=&nbsp;<FONT color=#0000ff>null</FONT>; 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;<FONT color=#0000ff>static</FONT>&nbsp;<FONT color=#ff0000>Logger</FONT>&nbsp;logger&nbsp;=&nbsp;<FONT color=#ff0000>Logger</FONT>.getRootLogger(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;SingletonTest(<FONT class=classLink><U>String</U></FONT>&nbsp;name)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>super</FONT>(name); 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>void</FONT>&nbsp;setUp()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;sone&nbsp;=&nbsp;Singleton.INSTANCE; 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;stwo&nbsp;=&nbsp;Singleton.INSTANCE; 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>void</FONT>&nbsp;testSerialize()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(<FONT color=#ff33ff>"testing&nbsp;singleton&nbsp;serialization..."</FONT>); 
<LI>[b]&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writeSingleton(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Singleton&nbsp;s1&nbsp;=&nbsp;readSingleton(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Singleton&nbsp;s2&nbsp;=&nbsp;readSingleton(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT class=classLink><U>Assert</U></FONT>.assertEquals(<FONT color=#0000ff>true</FONT>,&nbsp;s1&nbsp;==&nbsp;s2);[/b]&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;<FONT color=#0000ff>void</FONT>&nbsp;writeSingleton()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>try</FONT>&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT class=classLink><U>FileOutputStream</U></FONT>&nbsp;fos&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;<FONT class=classLink><U>FileOutputStream</U></FONT>(<FONT color=#ff33ff>"serializedSingleton"</FONT>); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT class=classLink><U>ObjectOutputStream</U></FONT>&nbsp;oos&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;<FONT class=classLink><U>ObjectOutputStream</U></FONT>(fos); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Singleton&nbsp;s&nbsp;=&nbsp;Singleton.INSTANCE; 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;oos.writeObject(Singleton.INSTANCE); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;oos.flush(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>NotSerializableException</U></FONT>&nbsp;se)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"Not&nbsp;Serializable&nbsp;Exception:&nbsp;"</FONT>&nbsp;+&nbsp;se.getMessage()); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>IOException</U></FONT>&nbsp;iox)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"IO&nbsp;Exception:&nbsp;"</FONT>&nbsp;+&nbsp;iox.getMessage()); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>private</FONT>&nbsp;Singleton&nbsp;readSingleton()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Singleton&nbsp;s&nbsp;=&nbsp;<FONT color=#0000ff>null</FONT>; 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>try</FONT>&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT class=classLink><U>FileInputStream</U></FONT>&nbsp;fis&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;<FONT class=classLink><U>FileInputStream</U></FONT>(<FONT color=#ff33ff>"serializedSingleton"</FONT>); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT class=classLink><U>ObjectInputStream</U></FONT>&nbsp;ois&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;<FONT class=classLink><U>ObjectInputStream</U></FONT>(fis); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;s&nbsp;=&nbsp;(Singleton)ois.readObject(); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>ClassNotFoundException</U></FONT>&nbsp;cnf)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"Class&nbsp;Not&nbsp;Found&nbsp;Exception:&nbsp;"</FONT>&nbsp;+&nbsp;cnf.getMessage()); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>NotSerializableException</U></FONT>&nbsp;se)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"Not&nbsp;Serializable&nbsp;Exception:&nbsp;"</FONT>&nbsp;+&nbsp;se.getMessage()); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>catch</FONT>(<FONT class=classLink><U>IOException</U></FONT>&nbsp;iox)&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.fatal(<FONT color=#ff33ff>"IO&nbsp;Exception:&nbsp;"</FONT>&nbsp;+&nbsp;iox.getMessage()); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>return</FONT>&nbsp;s; 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>&nbsp;&nbsp;&nbsp;<FONT color=#0000ff>public</FONT>&nbsp;<FONT color=#0000ff>void</FONT>&nbsp;testUnique()&nbsp;{ 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(<FONT color=#ff33ff>"testing&nbsp;singleton&nbsp;uniqueness..."</FONT>); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Singleton&nbsp;another&nbsp;=&nbsp;<FONT color=#0000ff>new</FONT>&nbsp;Singleton(); 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;logger.info(<FONT color=#ff33ff>"checking&nbsp;singletons&nbsp;for&nbsp;equality"</FONT>); 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<FONT class=classLink><U>Assert</U></FONT>.assertEquals(<FONT color=#0000ff>true</FONT>,&nbsp;sone&nbsp;==&nbsp;stwo); 
<LI>&nbsp;&nbsp;&nbsp;} 
<LI>} </LI></OL></DIV><BR>前面这个测试案例序列化例12中的单例类，并且两次重构它。然后这个测试案例检查看是否被重构的单例类实例是同一个对象。下面是测试案例的输出：<BR>
<DIV class=codeStyle>
<OL>
<LI>　 
<LI>Buildfile:&nbsp;build.xml 
<LI>　 
<LI>init: 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[echo]&nbsp;Build&nbsp;20030422&nbsp;(22-04-2003&nbsp;11:32) 
<LI>　 
<LI>compile: 
<LI>　 
<LI>run-test-text: 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[java]&nbsp;.INFO&nbsp;main:&nbsp;testing&nbsp;singleton&nbsp;serialization... 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[java]&nbsp;.INFO&nbsp;main:&nbsp;testing&nbsp;singleton&nbsp;uniqueness... 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[java]&nbsp;INFO&nbsp;main:&nbsp;checking&nbsp;singletons&nbsp;<FONT color=#0000ff>for</FONT>&nbsp;equality 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[java]&nbsp;<FONT color=#ff0000>Time</FONT>:&nbsp;0.1 
<LI>　 
<LI>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;[java]&nbsp;OK&nbsp;(2&nbsp;tests) </LI></OL></DIV><BR>
<H3>单例模式结束语</H3><BR>单例模式简单却容易让人迷惑，特别是对于Java的开发者来说。在这篇文章中，作者演示了Java开发者在顾及多线程、类载入器和序列化情况如何实现单例模式。作者也展示了你怎样才能实现一个单例类的注册表，以便能够在运行期指定单例类。<BR></LI></UL></B></TD></TR></TBODY></TABLE></TD></TR></TBODY></TABLE><img src ="http://www.blogjava.net/kapok/aggbug/3410.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/kapok/" target="_blank">笨笨</a> 2005-04-18 14:33 <a href="http://www.blogjava.net/kapok/archive/2005/04/18/3410.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>More Nifty Corners</title><link>http://www.blogjava.net/kapok/archive/2005/04/17/3390.html</link><dc:creator>笨笨</dc:creator><author>笨笨</author><pubDate>Sun, 17 Apr 2005 15:26:00 GMT</pubDate><guid>http://www.blogjava.net/kapok/archive/2005/04/17/3390.html</guid><wfw:comment>http://www.blogjava.net/kapok/comments/3390.html</wfw:comment><comments>http://www.blogjava.net/kapok/archive/2005/04/17/3390.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/kapok/comments/commentRss/3390.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/kapok/services/trackbacks/3390.html</trackback:ping><description><![CDATA[<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<DIV style="FONT-SIZE: 16px; COLOR: black; FONT-FAMILY: tahoma,verdana,arial"><A href="http://pro.html.it/articoli/id_599/idcat_31/pro.html">http://pro.html.it/articoli/id_599/idcat_31/pro.html</A><BR><A href="http://www.forgetfoo.com/?blogid=3436">http://www.forgetfoo.com/?blogid=3436</A><BR><BR><A id=clp599 style="CURSOR: hand"><BR><BR><IMG alt="Aggiungi 'More Nifty Corners' ai tuoi preferiti" src="http://pro.html.it/img/add.gif" width=11 heigth="11"></A>
<SCRIPT type=text/javascript>
				allClippings[allClippings.length] =  new Clipping(599,"More Nifty Corners","http://pro.html.it/articoli/id_599/idcat_31/pro.html");
				</SCRIPT>
 &nbsp;<B>More Nifty Corners</B></DIV></TD>
<TD align=right width=59>
<DIV align=right><IMG height=15 alt="Giudizio dell'articolo" src="http://pro.html.it/img/voto4.gif" width=71 border=0></DIV></TD></TR></TBODY></TABLE><IMG height=1 alt="" src="http://pro.html.it/img/grigio.gif" width="100%" vspace=2 border=0><BR>
<TABLE cellSpacing=0 cellPadding=0 width="100%" border=0>
<TBODY>
<TR>
<TD>
<DIV align=left><FONT face=arial size=1>di Alessandro Fulciniti&nbsp;(<A title="Spedisci una e-mail a Alessandro Fulciniti" href="mailto:a.fulciniti@html.it">a.fulciniti@html.it</A>)</DIV></FONT></TD>
<TD align=right>
<DIV style="FONT-SIZE: 10px; COLOR: black; FONT-FAMILY: tahoma,verdana,arial" align=right>mercoledì 6 aprile 2005</DIV></TD></TR></TBODY></TABLE><BR><BR><!-- FINE TITOLO --><!-- TESTO SCRIPT --><A name=corpo></A><FONT face=arial size=2><!-- TESTO SCRIPT -->
<P>The article on <B>Nifty Corners</B> catched a <A title="delicious bookmarks on nifty corners" href="http://del.icio.us/url/64570f4974b50f3b29cdbbbfaefd2291">lot of interest</A> that we didn't expect at all. So, a big <B>Thank you</B> goes to the international webdesign community and in particular <A href="http://www.456bereastreet.com/">Roger Johansson from 456bereastreet.com</A> and <A href="http://www.web-graphics.com/">Nate Stainer from web-graphics.com</A> that originally launched the link through the <A title="bloglines citations for nifty corners" href="http://www.bloglines.com/citations?url=http%3A%2F%2Fpro.html.it%2Fesempio%2Fnifty%2F&amp;d=0&amp;mysubs=0&amp;submit=Search">blogosphere</A>.</P>
<P>Since its publication, I've worked a lot on Nifty Corners, bringing many improvements: looking back at the solution presented in the first article, seems to me that it was written ages ago. If you hadn't read it, I suggest you to <A href="http://pro.html.it/esempio/nifty/">have a look</A> to understand what I'm going to talk about. If you read it, but you're so curious that you couldn't wait, have a look at the <A href="http://pro.html.it/esempio/nifty2/nifty10js.html">final example</A> I prepared for this article. No images were used, just CSS and Javascript.</P><BR><SPAN style="FONT-SIZE: 110%; FONT-FAMILY: verdana"><B>The news about Nifty Corners</B></SPAN><BR>
<P>First, I'm going to present briefly the many improvements I brought to Nifty Corners.</P>
<P>The first and major improvement is that I' ve extended <B>browser support</B> (hat tip <A title=positioniseverything.net href="http://www.positioniseverything.net/">John Gallant from positioniseverything.net</A> for the <SPAN style="COLOR: #ba1b2a"><B>font-size: 1px</B></SPAN> CSS fix!). Now, Nifty Corners are fully supported also in Internet Explorer 5.5, covering a very wide range of modern browsers: the examples that we'll see in this article have been successfully tested in IE6, IE5.5, Opera 7.6, Firefox 1.0 and Safari 1.1. The support for IE5.0 is partial, only classic nifty corners are displayed well, but I got rid anyway of that not-so-good browser sniffing that was used in the first version.</P>
<P>Talking about support: now Nifty Corners work also on XHTML pages served with mime types of <B>application/xhtml+xml</B> (hat tip <A title=456bereastreet.com href="http://www.456bereastreet.com/">Roger Johansson</A> for <SPAN style="COLOR: #ba1b2a"><B>document.createElementNS</B></SPAN> hint!). There's also a more robust <B>error detection</B> for the script, and an improved support for <B>class selector</B>.</P>
<P>Second, it's now possible <B>choose the corners to round</B>: you can choose to round one to all corners.</P>
<P>The other improvements I did are all based on the use of borders, togheter with the coloured stripes and margins, to get a quite wide variety of effects. While with the first version it was possible to get rounded corners <EM>transparent outside</EM>, now it's possible to get them <B>transparent inside</B>. A light form of <B>antialiasing</B> is now possible with Nifty Corners, and you can also get <B>nifty corners with edges</B>. Next, I added a basic <B>vertical padding handling</B> that you could find useful in some case.</P>
<P>We'll cover this new features in depth later through <B>ten examples</B> I prepared. But first, let's see how nifty corners works in general and the usage of the main function of the Javascript library.</P><BR><SPAN style="FONT-SIZE: 110%; FONT-FAMILY: verdana"><B>How Nifty Corners work</B></SPAN><BR>
<P>Nifty Corners are a combination of CSS and Javascript to get <EM>rounded corners without images</EM>. The technique is made up of four essential parts:</P>
<OL>
<LI><A href="http://pro.html.it/esempio/nifty2/niftycssCode.html">CSS file</A> for the screen</A> 
<LI><A href="http://pro.html.it/esempio/nifty2/niftycssprintCode.html">CSS file</A> for the print</A> 
<LI><A href="http://pro.html.it/esempio/nifty2/niftyjsCode.html">Javascript library</A> to get Nifty Corners</A> 
<LI>The Javascript calls to round the elements you want </LI></OL>
<P>The firts three components just don't need changes, whatever you want to accomplish. They need just to be declared in the <SPAN style="COLOR: #ba1b2a"><B>head</B></SPAN> section of any HTML page like this:</P>
<P class=codice>&lt;link rel="stylesheet" type="text/css" href="niftyCorners.css"&gt;<BR>&lt;link rel="stylesheet" type="text/css" href="niftyPrint.css" media="print"&gt;<BR>&lt;script type="text/javascript" src="nifty.js"&gt;&lt;/script&gt; </P>
<P>As you can see, togheter with the support and the possibilities, even the complexity of Javascript and CSS file increased a bit respect the first version, but I've tried to mantain the usage really easy.</P><BR><SPAN style="FONT-SIZE: 110%; FONT-FAMILY: verdana"><B>The Javascript functions</B></SPAN><BR>
<P>Let's see the <A href="http://pro.html.it/esempio/nifty2/nifty1js.html">first example</A>: if you see the code, you'll note that I left embedded the CSS and part of the Javascript to show how the page is build. Here's the embedded Javascript code:</P>
<P class=codice>&lt;script type="text/javascript"&gt;<BR>window.onload=function(){<BR>if(!NiftyCheck())<BR>&nbsp;&nbsp;&nbsp;&nbsp;return;<BR><B>Rounded("div#nifty","all","#FFF","#D4DDFF","smooth");</B><BR>}<BR>&lt;/script&gt; </P>
<P>The function <B>NiftyCheck</B> performs a check for DOM support. If the test has passed, the <B>Rounded</B> function is called, that is now the only one function that you should call to get nifty corners. It accepts five parameters, that are in order:</P>
<OL>
<LI>A <B>CSS selector</B> that indicates on wich elements apply the function 
<LI>A string that indicates <B>wich corners to round</B> 
<LI><B>Outer color</B> of the rounded corners 
<LI><B>Inner color</B> of the rounded corners 
<LI>An <B>optional fifth parameter</B>, that will contain the options for Nifty Corners </LI></OL>
<P>Let's see them in depth.<BR><BR><BR><BR><BR><SPAN style="FONT-SIZE: 110%; FONT-FAMILY: verdana"><B>First parameter: the CSS selector</B></SPAN><BR></P>
<P>One of the strenghts of the function is that is capable of accepting a <B>CSS selector</B> to target the elements to round. Are accepted parameters:</P>
<UL>
<LI><B>Tag selector</B>, i.e. <SPAN style="COLOR: #ba1b2a"><B>"p"</B></SPAN> or <SPAN style="COLOR: #ba1b2a"><B>"blockquote"</B></SPAN> or <SPAN style="COLOR: #ba1b2a"><B>"h2"</B></SPAN> 
<LI><B>Id selector</B>, with specified tag of the element: for example <SPAN style="COLOR: #ba1b2a"><B>"div#content"</B></SPAN> or <SPAN style="COLOR: #ba1b2a"><B>"p#news"</B></SPAN> or <SPAN style="COLOR: #ba1b2a"><B>"li#home"</B></SPAN> 
<LI><B>Class selector</B>, with specified tag of the element: for example <SPAN style="COLOR: #ba1b2a"><B>"div.entry"</B></SPAN> or <SPAN style="COLOR: #ba1b2a"><B>"h2.highlight"</B></SPAN> 
<LI><B>Descendant selector</B>, with some limitation: this have to be composed by an id selector followed by a tag selector. Valid examples are: <SPAN style="COLOR: #ba1b2a"><B>"div#news div"</B></SPAN> or <SPAN style="COLOR: #ba1b2a"><B>"ul#menu li"</B></SPAN> </LI></UL>
<P><BR><SPAN style="FONT-SIZE: 110%; FONT-FAMILY: verdana"><B>Second parameter: specifing wich corners to round</B></SPAN><BR></P>
<P>Let's see again the Javascript call for the <A href="http://pro.html.it/esempio/nifty2/nifty1js.html">first example</A>:</P>
<P class=codice>Rounded("div#nifty",<B>"all"</B>,"#FFF","#D4DDFF","smooth");</P>
<P>The second parameter specify wich corners to round. It accept one or more of the following keywords separated by a space:</P>
<UL>
<LI><SPAN style="COLOR: #ba1b2a"><B>all</B></SPAN> will round all corners 
<LI><SPAN style="COLOR: #ba1b2a"><B>top</B></SPAN> will round top corners 
<LI><SPAN style="COLOR: #ba1b2a"><B>bottom</B></SPAN> will round bottom corners 
<LI><SPAN style="COLOR: #ba1b2a"><B>tl</B></SPAN> will round top-left corner 
<LI><SPAN style="COLOR: #ba1b2a"><B>tr</B></SPAN> will round top-right corner 
<LI><SPAN style="COLOR: #ba1b2a"><B>bl</B></SPAN> will round bottom-left corner 
<LI><SPAN style="COLOR: #ba1b2a"><B>br</B></SPAN> will round bottom-right corner </LI></UL>
<P>You can also combine the keywords. For example, you can use the parameter <SPAN style="COLOR: #ba1b2a"><B>"top br"</B></SPAN> to round the top corners and the bottom-right one. Note also that both the parameter <SPAN style="COLOR: #ba1b2a"><B>"top bottom"</B></SPAN> and <SPAN style="COLOR: #ba1b2a"><B>"tl tr bl br"</B></SPAN> are valid to get all the four corners rounded.</P>
<P><BR><SPAN style="FONT-SIZE: 110%; FONT-FAMILY: verdana"><B>Third and fourth parameters: specifying the colors</B></SPAN><BR></P>
<P>Let's look again the Javascript call for the <A href="http://pro.html.it/esempio/nifty2/nifty1js.html">first example</A>:</P>
<P class=codice>Rounded("div#nifty","all",<B>"#FFF","#D4DDFF"</B>,"smooth");</P>
<P>The third and fourth parameters are used to specifying respectively <B>outer</B> and <B>inner</B> color. They should be specified in hex code with <B>#</B> symbol, in three or six digits. The good news is that they could also be setted to <SPAN style="COLOR: #ba1b2a"><B>"transparent"</B></SPAN>. While in the first version you could only get the outer color transparent, now you can also get nifty corners transparent <EM>inside</EM>. Obviously, you cannot get them both. But transparency inside it's really useful for example to play with elements with background, or avoiding specifying inner color but just the outer color.</P>
<P><BR><SPAN style="FONT-SIZE: 110%; FONT-FAMILY: verdana"><B>Fifth parameter: nifty corners options</B></SPAN><BR></P>
<P>Here's the Javascript call for the <A href="http://pro.html.it/esempio/nifty2/nifty1js.html">first example</A>:</P>
<P class=codice>Rounded("div#nifty","all","#FFF","#D4DDFF",<B>"smooth"</B>);</P>
<P>As you can see, there's a fifth parameter setted to <SPAN style="COLOR: #ba1b2a"><B>"smooth"</B></SPAN>, in order to produce lighly smoothed rounded corners. The fifth parameter is optional, and if specified will render nifty corners in different way. There are some <B>keywords</B> that you could use:</P>
<UL>
<LI><SPAN style="COLOR: #ba1b2a"><B>smooth</B></SPAN> will produce lighly antialiased nifty corners: the Javascript library will compute automatically the intermediate color to blend the inner and outer color more gently: so, in this case they must be both be specified. 
<LI><SPAN style="COLOR: #ba1b2a"><B>border</B></SPAN> (followed by a color in hex code with <B>#</B> symbol, in three or six digits) will make corners with edges. Note that you can also 