﻿<?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-thisliy-随笔分类-j2se</title><link>http://www.blogjava.net/thisliy/category/37188.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 24 Dec 2009 07:50:26 GMT</lastBuildDate><pubDate>Thu, 24 Dec 2009 07:50:26 GMT</pubDate><ttl>60</ttl><item><title>JNDI详解</title><link>http://www.blogjava.net/thisliy/archive/2009/12/20/306721.html</link><dc:creator>liyang</dc:creator><author>liyang</author><pubDate>Sun, 20 Dec 2009 04:29:00 GMT</pubDate><guid>http://www.blogjava.net/thisliy/archive/2009/12/20/306721.html</guid><description><![CDATA[<span class="Tit" style="font-family: 微软雅黑">JNDI<br />
Java Naming and Directory Interface<br />
<br />
首先需要对naming与directory 有最基本的认识，进而了解使用JNDI 的原因：<br />
1、naming<br />
Naming 在电脑系统上是最基本的服务之一，藉著名称 (names)与物件 (objects)的系结 (binding)，使用者透过正确地描述环境(context)来存取欲使用的物件。例如：DNS(Domain Name System)将主机名称「 javacenter.cis.th u.edu.tw」对应到 IP Address「 140.128.104.30」，以方便使用者记忆。 <br />
2、directory<br />
Directory可视为 naming概念的扩充，让物件拥有属性 (attributes)以记录额外的资讯。於是，我们可以透过名称来查看(look up)物件并获得该物件的属性资讯，或是利用属性作为搜寻的过滤条件 (search filter)。例如：电话簿，记录著每个人登记的电话号码。从这个最常见的例子中不难看出directory与 database明显的差异之一：directory 的属性可以有很多笔资料，正如每个人可以同时拥有很多个电话号码。 Directory目前在电脑系统上较著名的应用系统与架构有 Novell公司的 NDS(Novell Directory Services)、Sun公司的NIS(Network Information Service)，与即将成为网络上一个新标准的 LDAP(Lightweight Directory Access Protocol)。<br />
<br />
JNDI(Java Naming and Directory Interface)是SUN公司提供的一种标准的Java命名系统接口，JNDI提供统一的客户端API，通过不同的访问提供者接口JNDI SPI的实现，由管理者将JNDI API映射为特定的命名服务和目录系统，使得Java应用程序可以和这些命名服务和目录服务之间进行交互。集群JNDI实现了高可靠性JNDI[8]，通过服务器的集群，保证了JNDI的负载平衡和错误恢复。在全局共享的方式下，集群中的一个应用服务器保证本地JNDI树的独立性，并拥有全局的JNDI 树。每个应用服务器在把部署的服务对象绑定到自己本地的JNDI树的同时，还绑定到一个共享的全局JNDI树，实现全局JNDI和自身JNDI的联系。<br />
<br />
JNDI是一个应用程序设计的API，为开发人员提供了查找和访问各种命名和目录服务的通用、统一的接口，类似JDBC都是构建在抽象层上。<br />
<br />
JNDI可访问的现有的目录及服务有：<br />
DNS、XNam 、Novell目录服务、LDAP(Lightweight Directory Access Protocol 轻型目录访问协议)、 CORBA对象服务、文件系统、Windows XP/2000/NT/Me/9x的注册表、RMI、DSML v1&amp;v2、NIS。<br />
<br />
JNDI优点：<br />
包含了大量的命名和目录服务，使用通用接口来访问不同种类的服务；<br />
可以同时连接到多个命名或目录服务上；<br />
建立起逻辑关联，允许把名称同Java对象或资源关联起来，而不必指导对象或资源的物理ID。<br />
<br />
JNDI程序包：<br />
javax.naming：命名操作；<br />
javax.naming.directory：目录操作；<br />
javax.naming.event：在命名目录服务器中请求事件通知；<br />
javax.naming.ldap：提供LDAP支持；<br />
javax.naming.spi：允许动态插入不同实现。<br />
利用JNDI的命名与服务功能来满足企业级APIs对命名与服务的访问，诸如EJBs、JMS、JDBC 2.0以及IIOP上的RMI通过JNDI来使用CORBA的命名服务。<br />
JNDI与JDBC：<br />
JNDI提供了一种统一的方式，可以用在网络上查找和访问服务。通过指定一个资源名称，该名称对应于数据库或命名服务中的一个纪录，同时返回数据库连接建立所必须的信息。<br />
代码示例：<br />
try{<br />
Context cntxt = new InitialContext();<br />
DataSource ds = (DataSource) cntxt.lookup("jdbc/dpt");<br />
}<br />
catch(NamingException ne){<br />
...<br />
}<br />
<br />
JNDI与JMS：<br />
消息通信是软件组件或应用程序用来通信的一种方法。JMS就是一种允许应用程序创建、发送、接收、和读取消息的JAVA技术。<br />
代码示例：<br />
try{<br />
Properties env = new Properties();<br />
InitialContext inictxt = new InitialContext(env);<br />
TopicConnectionFactory connFactory = (TopicConnectionFactory) inictxt.lookup("TTopicConnectionFactory");<br />
...<br />
}<br />
catch(NamingException ne){<br />
...<br />
}<br />
<br />
访问特定目录：举个例子，人是个对象，他有好几个属性，诸如这个人的姓名、电话号码、电子邮件地址、邮政编码等属性。通过getAttributes()方法<br />
Attribute attr =directory.getAttributes(personName).get("email");<br />
String email = (String)attr.get();<br />
通过使用JNDI让客户使用对象的名称或属性来查找对象：<br />
foxes = directory.search("o=Wiz,c=US", "sn=Fox", controls);<br />
通过使用JNDI来查找诸如打印机、数据库这样的对象，查找打印机的例子：<br />
Printer printer = (Printer)namespace.lookup(printerName);<br />
printer.print(document);<br />
<br />
浏览命名空间：<br />
NamingEnumeration list = namespace.list("o=Widget, c=US");<br />
while (list.hasMore()) {<br />
NameClassPair entry = (NameClassPair)list.next();<br />
display(entry.getName(), entry.getClassName());<br />
}<br />
<br />
常用的JNDI操作：<br />
void bind(String sName,Object object);――绑定：把名称同对象关联的过程<br />
void rebind(String sName,Object object);――重新绑定：用来把对象同一个已经存在的名称重新绑定<br />
void unbind(String sName);――释放：用来把对象从目录中释放出来<br />
void lookup(String sName,Object object);――查找：返回目录总的一个对象<br />
void rename(String sOldName,String sNewName);――重命名：用来修改对象名称绑定的名称<br />
NamingEnumeration listBinding(String sName);――清单：返回绑定在特定上下文中对象的清单列表<br />
NamingEnumeration list(String sName);<br />
<br />
代码示例：重新得到了名称、类名和绑定对象。<br />
NamingEnumeration namEnumList = ctxt.listBinding("cntxtName");<br />
...<br />
while ( namEnumList.hasMore() )&nbsp;&nbsp; {<br />
Binding bnd = (Binding) namEnumList.next();<br />
String sObjName = bnd.getName();<br />
String sClassName = bnd.getClassName();<br />
SomeObject objLocal = (SomeObject) bnd.getObject();<br />
} </span>
<img src ="http://www.blogjava.net/thisliy/aggbug/306721.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/thisliy/" target="_blank">liyang</a> 2009-12-20 12:29 <a href="http://www.blogjava.net/thisliy/archive/2009/12/20/306721.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>String.getBytes()和new String()</title><link>http://www.blogjava.net/thisliy/archive/2009/12/09/305313.html</link><dc:creator>liyang</dc:creator><author>liyang</author><pubDate>Wed, 09 Dec 2009 09:27:00 GMT</pubDate><guid>http://www.blogjava.net/thisliy/archive/2009/12/09/305313.html</guid><description><![CDATA[<div id="blog_text" class="cnt">
<p>在Java中，String.getBytes(String decode)方法会根据指定的decode编码返回某字符串在该编码下的byte数组表示，如</p>
<p>byte[] b_gbk = "中".getBytes("GBK");<br />
byte[] b_utf8 = "中".getBytes("UTF-8");<br />
byte[] b_iso88591 = "中".getBytes("ISO8859-1");</p>
<p>将分别返回&#8220;中&#8221;这个汉字在GBK、UTF-8和ISO8859-1编码下的byte数组表示，此时b_gbk的长度为2，b_utf8的长度为3，b_iso88591的长度为1。</p>
<p>而与getBytes相对的，可以通过new String(byte[], decode)的方式来还原这个&#8220;中&#8221;字时，这个new String(byte[], decode)实际是使用decode指定的编码来将byte[]解析成字符串。</p>
<p>String s_gbk = new String(b_gbk,"GBK");<br />
String s_utf8 = new String(b_utf8,"UTF-8");<br />
String s_iso88591 = new String(b_iso88591,"ISO8859-1");</p>
<p>通过打印s_gbk、s_utf8和s_iso88591，会发现，s_gbk和s_utf8都是&#8220;中&#8221;，而只有s_iso88591是一个不认识
的字符，为什么使用ISO8859-1编码再组合之后，无法还原&#8220;中&#8221;字呢，其实原因很简单，因为ISO8859-1编码的编码表中，根本就没有包含汉字
字符，当然也就无法通过"中".getBytes("ISO8859-1");来得到正确的&#8220;中&#8221;字在ISO8859-1中的编码值了，所以再通过new
String()来还原就无从谈起了。</p>
<p>因此，通过String.getBytes(String decode)方法来得到byte[]时，一定要确定decode的编码表中确实存在String表示的码值，这样得到的byte[]数组才能正确被还原。</p>
<p>有时候，为了让中文字符适应某些特殊要求（如http header头要求其内容必须为iso8859-1编码），可能会通过将中文字符按照字节方式来编码的情况，如</p>
<p>String s_iso88591 = new
String("中".getBytes("UTF-8"),"ISO8859-1")，这样得到的s_iso8859-1字符串实际是三个在
ISO8859-1中的字符，在将这些字符传递到目的地后，目的地程序再通过相反的方式String s_utf8 = new
String(s_iso88591.getBytes("ISO8859-1"),"UTF-8")来得到正确的中文汉字&#8220;中&#8221;。这样就既保证了遵守协
议规定、也支持中文。</p>
</div>
<img src ="http://www.blogjava.net/thisliy/aggbug/305313.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/thisliy/" target="_blank">liyang</a> 2009-12-09 17:27 <a href="http://www.blogjava.net/thisliy/archive/2009/12/09/305313.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Classloader的基本概念</title><link>http://www.blogjava.net/thisliy/archive/2009/10/31/300528.html</link><dc:creator>liyang</dc:creator><author>liyang</author><pubDate>Sat, 31 Oct 2009 15:20:00 GMT</pubDate><guid>http://www.blogjava.net/thisliy/archive/2009/10/31/300528.html</guid><wfw:comment>http://www.blogjava.net/thisliy/comments/300528.html</wfw:comment><comments>http://www.blogjava.net/thisliy/archive/2009/10/31/300528.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/thisliy/comments/commentRss/300528.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/thisliy/services/trackbacks/300528.html</trackback:ping><description><![CDATA[<span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Classloader在运行期会以父/子的层次结构存在，每个Classloader的实例都持有其父Classloader的引用，而父Classloader并不持有子Classloader的引用，从而形成一条单向链，当一个类装载请求被提交到某个Classloader时，其默认的类装载过程如下：<br />
</span></span></span></span></span>
<li>
<p><span style="color: red"><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">检查这个类有没有被装载过，如果已经装载过，则直接返回；</span></span></span></span></span></span></p>
<li>
<p><span style="color: red"><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">调用父Classloader去装载类，如果装载成功直接返回；</span></span></span></span></span></span></p>
<li>
<p><span style="color: red"><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">调用自身的装载类的方法，如果装载成功直接返回；</span></span></span></span></span></span></p>
<li>
<p><span style="color: red"><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">上述所有步骤都没有成功装载到类，抛出ClassNotFoundException；</span></span></span></span></span></span></p>
</li>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;每一层次的Classloader都重复上述动作。</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;简单说，当Classloader链上的某一Classloader收到类装载请求时，会按顺序向上询问其所有父节点，直至最顶端（BootstrapClassLoader），任何一个节点成功受理了此请求，则返回，如果所有父节点都不能受理，这时候才由被请求的Classloader自身来装载这个类，如果仍然不能装载，则抛出异常。<br />
<br />
<strong>类装载的方式</strong></span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;类装载的方式主要有两种：<span style="background-color: #ffff00">显式</span>的和<span style="background-color: #ffff00">隐式</span>的。</span></span></span></span></span></p>
<p><u><em><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;显式类装载</span></span></span></span></span></em></u></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;发生在使用以下方法调用进行装载类的时候：</span></span></span></span></span></p>
<p><span style="color: red"><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;ClassLoader.loadClass()（使用指定的Classloader进行装载）</span></span></span></span></span></span></p>
<p><span style="color: red"><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class.forName()（使用当前类的Caller Classloader进行装载）</span></span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;当调用上述方法的时候，指定的Class（以类名为参数）由Classloader装入。<span style="color: red"><span style="background-color: #ffff00"><span style="color: red">这两个方法的行为有轻微的区别，Class.forName()在类装载完成后，会对类进行初始化，而ClassLoader.loadClass()只负责装载类</span></span>。</span></span></span></span></span></span></p>
<p><u><em><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;隐式类装载</span></span></span></span></span></em></u></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;发生在由于引用、实例化或继承导致需要装载类的时候。隐式类装载是在幕后启动的，JVM会解析必要的引用并装载类。</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;类的装载通常组合了显式和隐式两种方式。例如，Classloader可能先显式地装载一个类，然后再隐式地装载它引用的其它类。<br />
<br />
</span></span></span></span></span><strong><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">一个基本的Classloader的层次结构<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<br />
&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;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<img src="http://infocenter.apusic.com/help/topic/aas/v6/img/cl1.jpg" align="center"  alt="" /><br />
</span></span></span></span></span></strong></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;上图显示了一个基本的Classloader的层次结构。<span style="color: red; background-color: #ffff00">在给定层次上的Classloader不能引用任何层次低于它的Classloader，另外，它的子Classloader装载的类对于其是不可见的。</span>在上图中，如果Foo.class是由ClassLoaderB装载的，并且Foo.class依赖于Bar.class，那么Bar.class必须由ClassLoaderA或B装载。如果Bar.class只是对ClassLoaderC和D可见，那么将会发生ClassNotFoundException或者NoClassDefFoundError异常。</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">如果Bar.class分别对于两个平级的Classloader可见（例如C和D），但对于它们的父Classloader不可见，那么当类装载请求发送到这两个Classloader时，每一个Classloader会装载自己版本的类。ClassLoaderC装载的Bar.class的实例将不兼容于ClassLoaderD装载的Bar.class的实例。如果对Classloader的层次结构不了解，试图使用由ClassLoaderC装载的类去造型一个ClassLoaderD装载的Bar.class的实例，则会发生造型失败（ClassCastException）。</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt"><strong>基本的Classloader<br />
</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;最基本的Classloader是Bootstrap Classloader和System Classloader（也有人称之为AppClassLoader），只要写过java程序，都会用到这两个Classloader。</span></span></span></span></span></p>
<ul type="disc">
    <li>
    <p><strong><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">Bootstrap Classloader</span></span></span></span></span></strong></p>
    <p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">这个Classloader装载Java虚拟机提供的基本运行时刻类（$JAVA_HOME/jre/lib），还包括放置在系统扩展目录（$JAVA_HOME/jre/lib/ext）内的JAR文件中的类。这个Classloader是java程序最顶层的Classloader，只有它没有父Classloader。如果你将一个自己写的类或第三方jar包放进$JAVA_HOME/jre/lib/ext目录中，那么它将被Bootstrap Classloader装载。</span></span></span></span></span></p>
    <li>
    <p><strong><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">System Classloader</span></span></span></span></span></strong></p>
    <p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">System Classloader通常负责装载系统环境变量CLASSPATH中设置的类。由System Classloader装载的类对于Apusic服务器内部的类和部署在Apusic服务器上的J2EE应用（通常打包成ear）都是可见的。%APUSIC_HOME%/lib目录下的jar文件是Apusic应用服务器的核心类，一般把这些jar文件都加在系统CLASSPATH中。另外，一些公用类也可以加在系统CLASSPATH中，如JDBC驱动程序等。</span></span></span></span></span></p>
    </li>
</ul>
<br />
<span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt"><strong>自定义Classloader<br />
</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;在编写应用代码的时候，常常有需要动态加载类和资源，比如显式的调用classLoader.loadClass(&#8220;ClassName&#8221;)，<span style="color: red">虽然直接使用ClassLoader.getSystemClassLoader()，可以得到SystemlassLoader来完成这项任务。但是，由于System Classloader是JVM创建的Classloader，它的职责有限，只适合于普通的java应用程序，在很多复杂场景中不能满足需求，比如在应用服务器中。</span>这时候就需要自行实现一个Classloader的子类，实现特定的行为。Apusic应用服务器中就定义了若干个特有的Classloader，负责装载部署在Apusic中的JavaEE应用中的类，这里并不试图去描述如何实现一个自定义的Classloader.<br />
<br />
</span></span></span></span></span><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt"><strong>Caller Classloader和线程上下文Classloader<br />
</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;动态加载资源时，往往有三种Classloader可选择：<strong>System Classloader</strong>、<strong>Caller Classloader</strong>、<strong>当前线程的上下文Classloader</strong>。System Classloader前面已经描述过了，下面我们看看什么是Caller Classloader、当前线程的上下文Classloader。<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span></span></span></span></span>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<u><em>&nbsp;&nbsp;&nbsp;Caller Classloader</em></u></span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Caller Classloader指的是当前所在的类装载时使用的Classloader，它可能是System Classloader，也可能是一个自定义的Classloader，这里，我们都称之为Caller Classloader。我们可以通过getClass().getClassLoader()来得到Caller Classloader。例如，存在A类，是被AClassLoader所加载，A.class.getClassLoader()为AClassLoader的实例，它就是A.class的Caller Classloader。</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;如果在A类中使用new关键字，或者Class.forName(String className)和Class.getResource(String resourceName)方法，那么这时也是使用Caller Classloader来装载类和资源。比如在A类中初始化B类：<br />
<br />
/**<br />
&nbsp; * A.java<br />
*/<br />
...<br />
public void foo() {<br />
&nbsp;&nbsp;&nbsp; B b = new B();<br />
&nbsp;&nbsp;&nbsp; b.setName("b");<br />
}<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;那么，B类由当前Classloader，也就是AClassloader装载。同样的，修改上述的foo方法，其实现改为：</span></span></span></span></span></p>
<span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;Class clazz = Class.forName("foo.B");<br />
</span></span></span></span></span>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">最终获取到的clazz，也是由AClassLoader所装载。</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;那么，如何使用指定的Classloader去完成类和资源的装载呢？或者说，当需要去实例化一个Caller Classloader和它的父Classloader都不能装载的类时，怎么办呢？</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;一个很典型的例子是JAXP，当使用xerces的SAX实现时，我们首先需要通过rt.jar中的javax.xml.parsers.SAXParserFactory.getInstance()得到xercesImpl.jar中的org.apache.xerces.jaxp.SAXParserFactoryImpl的实例。由于JAXP的框架接口的class位于JAVA_HOME/lib/rt.jar中，由Bootstrap Classloader装载，处于Classloader层次结构中的最顶层，而xercesImpl.jar由低层的Classloader装载，也就是说SAXParserFactoryImpl是在SAXParserFactory中实例化的，如前所述，使用SAXParserFactory的Caller Classloader(这里是Bootstrap Classloader)是完成不了这个任务的。</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">这时，我们就需要了解一下线程上下文Classloader了<br />
<br />
</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<u><em>&nbsp;线程上下文Classloader</em></u></span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">每个线程都有一个关联的上下文Classloader。如果使用new Thread()方式生成新的线程，新线程将继承其父线程的上下文Classloader。如果程序对线程上下文Classloader没有任何改动的话，程序中所有的线程将都使用System Classloader作为上下文Classloader。</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">当使用Thread.currentThread().setContextClassLoader(classloader)时，线程上下文Classloader就变成了指定的Classloader了。此时，在本线程的任意一处地方，调用Thread.currentThread().getContextClassLoader()，都可以得到前面设置的Classloader。</span></span></span></span></span></p>
<p><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">回到JAXP的例子，假设xercesImpl.jar只有AClassLoader能装载，现在A.class内部要使用JAXP，但是A.class却不是由AClassLoader或者它的子Classloader装载的，那么在A.class中，应该这样写才能正确得到xercesImpl的实现：</span></span></span></span></span></p>
<span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt">AClassLoader aClassLoader = new AClassLoader(parent);<br />
Thread.currentThread().setContextClassLoader(aClassLoader);<br />
SAXParserFactory factory = SAXParserFactory.getInstance();<br />
...<br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JAXP这时就可以通过线程上下文Classloader装载xercesImpl的实现类了，当然，还有一个前提是在配制文件或启动参数中指定了使用xerces作为JAXP的实现。下面是JAXP中的代码片断：<br />
ClassLoader cl = Thread.currentThread().getContextClassLoader();<br />
&#8230;<br />
Class providerClass = cl.loadClass(className);<br />
&#8230;<br />
</span></span></span></span></span><span style="font-size: 10pt"><span style="font-size: 12pt"><span style="font-family: 微软雅黑"><span style="font-family: 幼圆"><span style="font-size: 10pt"><strong>JVM中类的唯一性<br />
</strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;JVM为每一个Classloader维护一个唯一标识。在一个JVM里（对应一个Java进程），可以由不同的Classloader装载多个同名的类（指包名和类名都完全相同，下同），为了唯一地标识被不同Classloader装载的类，JVM会在被装载的类名前加上装载该类的Classloader的标识。</span></span></span></span></span>
<img src ="http://www.blogjava.net/thisliy/aggbug/300528.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/thisliy/" target="_blank">liyang</a> 2009-10-31 23:20 <a href="http://www.blogjava.net/thisliy/archive/2009/10/31/300528.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>PropertyChangeSupport类</title><link>http://www.blogjava.net/thisliy/archive/2009/07/07/285832.html</link><dc:creator>liyang</dc:creator><author>liyang</author><pubDate>Tue, 07 Jul 2009 07:58:00 GMT</pubDate><guid>http://www.blogjava.net/thisliy/archive/2009/07/07/285832.html</guid><description><![CDATA[<span style="font-size: 12pt"><span style="font-size: 10pt"><span style="font-family: ">最近看代码一直碰到这个类，先做一个总结。<br />
<br />
先看PropertyChangeSupport类的官方文档解释：<br />
This is a utility class that can be used by beans that support bound properties.  You can use an instance of this class as a member field of your bean and delegate various work to it.<br />
<br />
1．关联属性<br />
在JavaBean的设计中，按照属性的不同作用又细分为四类：单值属性、索引属性、关联属性、限制属性。关联属性，也称之为绑定属性。绑定属性会在属性值发生变化时，通知所有相关的监听器。为了实现一个绑定属性，必须实现两个机制。<br />
1）  无论何时，只要属性的值发生变化，该bean必须发送一个PropertyChange事件给所有已注册的监听器。<br />
2）  为了使监听器能够注册，bean必须实现以下两个方法：void addPropertyChangeListener(PropertyChangeListener listener);<br />
void removePropertyChangerListener(PropertyChangerListener listener);<br />
<br />
<br />
<br />
2．使用PropertyChangeSupport管理监听器<br />
可以通过java.bean包下的PropertyChangeSupport类来管理监听器。要使用这个类，bean必须有一个此类的数据域。private PropertyChangeSupport listeners = new PropertyChangeSupport(this);<br />
<br />
<br />
这样可以将添加和移除监听器的任务交给这个对象。public void addPropertyChangeListener(PropertyChangeListener listener) {<br />
listeners.addPropertyChangeListener(listener);<br />
}<br />
<br />
public void removePropertyChangeListener(PropertyChangeListener listener) {<br />
listeners.removePropertyChangeListener(listener);<br />
}<br />
<br />
<br />
当bean的属性发生变化时，使用PropertyChangeSupport对象的firePropertyChange方法，会将一个事件发送给所有已经注册的监听器。该方法有三个参数：属性的名字、旧的值以及新的值。属性的值必须是对象，如果是简单数据类型，则必须进行包装。listeners.firePropertyChange("ourString", oldString, newString);<br />
<br />
<br />
所有注册的监听器实现PropertyChangeListener接口，该接口中只有一个方法。public void propertyChange(PropertyChangeEvent e)<br />
<br />
当bean的属性值发生变化时，该方法中的代码就会被触发。可以通过e.getOldValue();<br />
e.getNewValue();<br />
<br />
来得到changes.firePropertyChange("ourString", oldString, newString);中的oldString和newString。<br />
<br />
3．为什么要使用PropertyChangeSupport<br />
使用这个类管理监听器的好处是，它是线程安全的。如果使用一个循环体来set Bean的属性，则这个类可以保证所有监听器执行触发事件的有序。<br />
还有一个好处是，这个类支持fire带索引的属性改变事件（详见java.bean.IndexedPropertyChangeEvent）。此时向注册的监听器发送一个PropertyChangeEvent的方法为：void fireIndexedPropertyChange(String PropertyName,int index,Object oldValue,Object newValue)<br />
<br />
<br />
4．实例代码 <br />
<br />
public class SomeBean {<br />
<br />
private String property;<br />
<br />
private PropertyChangeSupport changeSupport;<br />
<br />
public void setProperty(String newValue) {<br />
<br />
String oldValue = property;<br />
<br />
property = newValue;<br />
<br />
changeSupport.firePropertyChange("property", oldValue, newValue);<br />
<br />
}<br />
<br />
public void addPropertyChangeListener(PropertyChangeListener l) {<br />
<br />
changeSupport.add(l);<br />
<br />
}<br />
<br />
public void removePropertyChangeListener(PropertyChangeListener l) {<br />
<br />
changeSupport.remove(l);<br />
<br />
}<br />
<br />
}</span></span><span style="font-family: "><br />
</span></span>
<p><span style="font-size: 12pt"><span style="font-family: "><br />
</span></span></p>
<p><span style="font-size: 12pt"><span>最近看代码一直碰到这个类，先做一个总结。<br />
<br />
先看PropertyChangeSupport类的官方文档解释：<br />
This is a utility class that can be used by beans that support bound properties.  You can use an instance of this class as a member field of your bean and delegate various work to it.<br />
<br />
1．关联属性<br />
在JavaBean的设计中，按照属性的不同作用又细分为四类：单值属性、索引属性、关联属性、限制属性。关联属性，也称之为绑定属性。绑定属性会在属性值发生变化时，通知所有相关的监听器。为了实现一个绑定属性，必须实现两个机制。<br />
1）  无论何时，只要属性的值发生变化，该bean必须发送一个PropertyChange事件给所有已注册的监听器。<br />
2）  为了使监听器能够注册，bean必须实现以下两个方法：void addPropertyChangeListener(PropertyChangeListener listener);<br />
void removePropertyChangerListener(PropertyChangerListener listener);<br />
<br />
<br />
<br />
2．使用PropertyChangeSupport管理监听器<br />
可以通过java.bean包下的PropertyChangeSupport类来管理监听器。要使用这个类，bean必须有一个此类的数据域。private PropertyChangeSupport listeners = new PropertyChangeSupport(this);<br />
<br />
<br />
这样可以将添加和移除监听器的任务交给这个对象。public void addPropertyChangeListener(PropertyChangeListener listener) {<br />
listeners.addPropertyChangeListener(listener);<br />
}<br />
<br />
public void removePropertyChangeListener(PropertyChangeListener listener) {<br />
listeners.removePropertyChangeListener(listener);<br />
}<br />
<br />
<br />
当bean的属性发生变化时，使用PropertyChangeSupport对象的firePropertyChange方法，会将一个事件发送给所有已经注册的监听器。该方法有三个参数：属性的名字、旧的值以及新的值。属性的值必须是对象，如果是简单数据类型，则必须进行包装。listeners.firePropertyChange("ourString", oldString, newString);<br />
<br />
<br />
所有注册的监听器实现PropertyChangeListener接口，该接口中只有一个方法。public void propertyChange(PropertyChangeEvent e)<br />
<br />
当bean的属性值发生变化时，该方法中的代码就会被触发。可以通过e.getOldValue();<br />
e.getNewValue();<br />
<br />
来得到changes.firePropertyChange("ourString", oldString, newString);中的oldString和newString。<br />
<br />
3．为什么要使用PropertyChangeSupport<br />
使用这个类管理监听器的好处是，它是线程安全的。如果使用一个循环体来set Bean的属性，则这个类可以保证所有监听器执行触发事件的有序。<br />
还有一个好处是，这个类支持fire带索引的属性改变事件（详见java.bean.IndexedPropertyChangeEvent）。此时向注册的监听器发送一个PropertyChangeEvent的方法为：void fireIndexedPropertyChange(String PropertyName,int index,Object oldValue,Object newValue)<br />
<br />
<br />
4．实例代码 <br />
<br />
public class SomeBean {<br />
<br />
private String property;<br />
<br />
private PropertyChangeSupport changeSupport;<br />
<br />
public void setProperty(String newValue) {<br />
<br />
String oldValue = property;<br />
<br />
property = newValue;<br />
<br />
changeSupport.firePropertyChange("property", oldValue, newValue);<br />
<br />
}<br />
<br />
public void addPropertyChangeListener(PropertyChangeListener l) {<br />
<br />
changeSupport.add(l);<br />
<br />
}<br />
<br />
public void removePropertyChangeListener(PropertyChangeListener l) {<br />
<br />
changeSupport.remove(l);<br />
<br />
}<br />
<br />
}</span></span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span style="font-size: 12pt"><span>最近看代码一直碰到这个类，先做一个总结。<br />
</span></span><span style="font-size: 12pt"><span style><br />
先看PropertyChangeSupport类的官方文档解释：<br />
This is a utility class that can be used by beans that support bound properties.  You can use an instance of this class as a member field of your bean and delegate various work to it.<br />
<br />
1．关联属性<br />
在JavaBean的设计中，按照属性的不同作用又细分为四类：单值属性、索引属性、关联属性、限制属性。关联属性，也称之为绑定属性。绑定属性会在属性值发生变化时，通知所有相关的监听器。为了实现一个绑定属性，必须实现两个机制。<br />
1）  无论何时，只要属性的值发生变化，该bean必须发送一个PropertyChange事件给所有已注册的监听器。<br />
2）  为了使监听器能够注册，bean必须实现以下两个方法：void addPropertyChangeListener(PropertyChangeListener listener);<br />
void removePropertyChangerListener(PropertyChangerListener listener);<br />
<br />
<br />
<br />
2．使用PropertyChangeSupport管理监听器<br />
可以通过java.bean包下的PropertyChangeSupport类来管理监听器。要使用这个类，bean必须有一个此类的数据域。private PropertyChangeSupport listeners = new PropertyChangeSupport(this);<br />
<br />
<br />
这样可以将添加和移除监听器的任务交给这个对象。public void addPropertyChangeListener(PropertyChangeListener listener) {<br />
listeners.addPropertyChangeListener(listener);<br />
}<br />
<br />
public void removePropertyChangeListener(PropertyChangeListener listener) {<br />
listeners.removePropertyChangeListener(listener);<br />
}<br />
<br />
<br />
当bean的属性发生变化时，使用PropertyChangeSupport对象的firePropertyChange方法，会将一个事件发送给所有已经注册的监听器。该方法有三个参数：属性的名字、旧的值以及新的值。属性的值必须是对象，如果是简单数据类型，则必须进行包装。listeners.firePropertyChange("ourString", oldString, newString);<br />
<br />
<br />
所有注册的监听器实现PropertyChangeListener接口，该接口中只有一个方法。public void propertyChange(PropertyChangeEvent e)<br />
<br />
当bean的属性值发生变化时，该方法中的代码就会被触发。可以通过e.getOldValue();<br />
e.getNewValue();<br />
<br />
来得到changes.firePropertyChange("ourString", oldString, newString);中的oldString和newString。<br />
<br />
3．为什么要使用PropertyChangeSupport<br />
使用这个类管理监听器的好处是，它是线程安全的。如果使用一个循环体来set Bean的属性，则这个类可以保证所有监听器执行触发事件的有序。<br />
还有一个好处是，这个类支持fire带索引的属性改变事件（详见java.bean.IndexedPropertyChangeEvent）。此时向注册的监听器发送一个PropertyChangeEvent的方法为：void fireIndexedPropertyChange(String PropertyName,int index,Object oldValue,Object newValue)<br />
<br />
<br />
4．实例代码 <br />
<br />
public class SomeBean {<br />
<br />
private String property;<br />
<br />
private PropertyChangeSupport changeSupport;<br />
<br />
public void setProperty(String newValue) {<br />
<br />
String oldValue = property;<br />
<br />
property = newValue;<br />
<br />
changeSupport.firePropertyChange("property", oldValue, newValue);<br />
<br />
}<br />
<br />
public void addPropertyChangeListener(PropertyChangeListener l) {<br />
<br />
changeSupport.add(l);<br />
<br />
}<br />
<br />
public void removePropertyChangeListener(PropertyChangeListener l) {<br />
<br />
changeSupport.remove(l);<br />
<br />
}<br />
<br />
}<br />
<br />
<br />
</span></span></p>
<span style="font-size: 12pt"><span style="font-family: ">
</span></span>
<img src ="http://www.blogjava.net/thisliy/aggbug/285832.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/thisliy/" target="_blank">liyang</a> 2009-07-07 15:58 <a href="http://www.blogjava.net/thisliy/archive/2009/07/07/285832.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>