Leo's Blog

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  13 随笔 :: 3 文章 :: 18 评论 :: 0 Trackbacks

#

时间:2005-11-04
作者:juxtapose


 如果大家对一般类的装载器熟悉的话,就知道在java中类的装载采用“代理机制”,即子装载器如果需要装载一个类文件,首先会将此任务提交给父 装载器,如果父装载器找不到此类文件,才有子装载器来装载类文件,如果子装载器也找不到,那么就会报告ClassNotFoundException异 常。下面简单谈一下我对weblogic server的类装载器原理的了解,希望能和大家分享。

1.Weblogic允许定制的类装载器,同时也有一个默认的类装载器。其默认的装载器的结构分层如下:

 

  当部署一个应用的时候,weblogic server会自动创建一个具有层次结构的类装载器。在图中,a.Application Classloader负责装载应用中的所有的EJB JAR文件;

  b.Web Application Classloader负责装载所有的Web application 中的WAR 文件(所有得jsp文件除外);

  c.Jsp Classloader 负责装载Web application 中的所有的jsp 文件;

  这样的分层结构有一个好处,就是在Jsp,Servlet中可以直接访问EJB的接口。这种上层装载EJB,下层装载servlet等,最下面装载jsp文件的结构,使得经常变动的jsp,servlet等可以被重新装载而不会涉及到EJB层。

在这种默认的类装载器结构下,有一点需要提出的是:

  a. 我们的应用必须打包成一个EAR文件,才会允许我们应用中的jsp和servlet文件直接访问ejb;如果将WAR与JAR文件分别打包。 Weblogic server会为他们分别生成一个类装载器,作为兄弟节点,这时如果需要在jsp或者servlet中使用ejb,就必须将EJB的Home接口与 remote接口打包到WAR中才可以。后面这种情况,适合用在将EJB的客户端和EJB部署在不同的JVM中;

  b.web application classloader中,不会装载jsp文件,jsp文件由web application classloader的子装载器Jsp classloader负责装载,因为jsp文件经常的变动,通过为jsp设立一个单独的classloader可以避免对jsp的装载影响到其他的 java class或者ejb;

默认装载器的优点:

  a. 调用ejb的时候可以采用call-by-referrence的方式;

  b. 允许web module独立的装载,不影响其它的web module;

  • 通过在将整个应用打包成一个EAR文件,可以方便的不用再web module中包含EJB的home和remote接口,就可以方便的通过call-by-referrence来调用ejb;

2. 定制classloader
  如果觉得默认的类装载器不能满足需要,weblogic server支持定制的类装载器。在weblogic的文档中指出,自定义的classloader多用于开发者使用,当应用发布之后,不推荐使用。自定 义的类装载器通过xml文件来描述。描述文件放在weblogic-application.xml中。Weblogic官方提供的DTD描述文件如下:

<classloader-structure> 
    
<module-ref> 
        
<module-uri>ejba.jar</module-uri> 
    
</module-ref> 
    
<module-ref> 
        
<module-uri>webc.war</module-uri> 
    
</module-ref>
    
<classloader-structure> 
        
<module-ref> 
            
<module-uri>weba.war</module-uri> 
        
</module-ref> 
    
</classloader-structure>
    
<classloader-structure> 
        
<module-ref> 
            
<module-uri>ejbc.jar</module-uri> 
        
</module-ref> 
        
<module-ref> 
            
<module-uri>webb.war</module-uri> 
        
</module-ref>
        
<classloader-structure> 
            
<module-ref> 
    
<module-uri>webd.war</module-uri> 
            
</module-ref> 
        
</classloader-structure> 
        
<classloader-structure> 
            
<module-ref> 
    
<module-uri>ejbb.jar</module-uri> 
            
</module-ref> 
        
</classloader-structure>
    
</classloader-structure>
</classloader-structure>

  通过我们给出的配置文件,我们自定义的classloader的层次结构如下图:

 

  在J2EE的规范中明确的指出,J2EE应用不应该依赖于任一个给定的类装载器。所以,我们自定义的类装载器,在开发过程中还是可以使用的,但一定不要应用于发布后的应用中。

自定义的类装载器有如下得限制:
  a.不能够装载servlet;

  b.嵌套的深度最大为3,也就是说,最多只能够嵌套三层;

  c.自定义装载器的module类型仅限于 Web和 EJB这两种;

  d.Jsp Classloader不受此自定义类装载器的影响,它永远都是web module的子类装载器;

  • 相同的类可能导致部署异常;
  • 在自定义的类装载器中,如果要使用EJB,就必须将EJB的home和remote接口打包到相应的web module中去;
  •  

3.Ejb的单独加载
  
有时候我们可能需要单独加载某个EJB,这个时候我们可以通过以下两种方法来实现:

  第一:将应用需要的jar文件放在APP-INF/lib中,或者将类文件放在APP-INF/classes中,这些类文件和JAR文件会被root classloader进行装载,可以被多个应用共享;

  第二:可以通过META-INF/MANIFEST.MF文件来指定需要的classes。通常的用法是在META-INF/MANIFEST.MF文件中增加Class-Path:一行。举例如下:

  Class-Path:/d:ejb/add.jar

  这样就会在当前的jar包中可以找到我们需要的add.jar文件。需要说明的是,在Class-Path:行的最后一定要有一个换行,否则会发生错误。还有,通过Class-Path只能指定本地的JAR文件。

  如果能对应用服务器的类装载原理有了较清楚地了解,会对我们的应用移植,在开发中避免不必要的类装载的错误会有很大的帮助。

转载自:http://dev2dev.bea.com.cn/bbsdoc/20051104121.html

posted @ 2006-02-23 18:37 Leo 阅读(381) | 评论 (0)编辑 收藏

作者:刘学超

一、引言

Java虚拟机(JVM)的类装载就是指将包含在类文件中的字节码装载到JVM中, 并使其成为JVM一部分的过程。JVM的类动态装载技术能够在运行时刻动态地加载或者替换系统的某些功能模块, 而不影响系统其他功能模块的正常运行。本文将分析JVM中的类装载系统,探讨JVM中类装载的原理、实现以及应用。

二、Java虚拟机的类装载实现与应用

2.1  装载过程简介

所谓装载就是寻找一个类或是一个接口的二进制形式并用该二进制形式来构造代表这个类或是这个接口的class对象的过程,其中类或接口的名称是给定了的。当然名称也可以通过计算得到,但是更常见的是通过搜索源代码经过编译器编译后所得到的二进制形式来构造。

在Java中,类装载器把一个类装入Java虚拟机中,要经过三个步骤来完成:装载、链接和初始化,其中链接又可以分成校验、准备和解析三步,除了解析外,其它步骤是严格按照顺序完成的,各个步骤的主要工作如下:

  • 装载:查找和导入类或接口的二进制数据;
  • 链接:执行下面的校验、准备和解析步骤,其中解析步骤是可以选择的;
  • 校验:检查导入类或接口的二进制数据的正确性;
  • 准备:给类的静态变量分配并初始化存储空间;
  • 解析:将符号引用转成直接引用;
  • 初始化:激活类的静态变量的初始化Java代码和静态Java代码块。

至于在类装载和虚拟机启动的过程中的具体细节和可能会抛出的错误,请参看《Java虚拟机规范》以及《深入Java虚拟机》,它们在网络上面的资源地址是: http://java.sun.com/docs/books/vmspec/2nd-edition/html/Preface.doc.htmlhttp://www.artima.com/insidejvm/ed2/index.html。 由于本文的讨论重点不在此就不再多叙述。

2.2  装载的实现

JVM中类的装载是由ClassLoader和它的子类来实现的,Java ClassLoader 是一个重要的Java运行时系统组件。它负责在运行时查找和装入类文件的类。

在Java中,ClassLoader是一个抽象类,它在包java.lang中,可以这样说,只要了解了在 ClassLoader中的一些重要的方法,再结合上面所介绍的JVM中类装载的具体的过程,对动态装载类这项技术就有了一个比较大概的掌握,这些重要的 方法包括以下几个:

①loadCass方法  loadClass(String name ,boolean resolve)其中name参数指定了JVM需要的类的名称,该名称以包表示法表示,如Java.lang.Object;resolve参数告诉方法 是否需要解析类,在初始化类之前,应考虑类解析,并不是所有的类都需要解析,如果JVM只需要知道该类是否存在或找出该类的超类,那么就不需要解析。这个 方法是ClassLoader 的入口点。

②defineClass方法  这个方法接受类文件的字节数组并把它转换成Class对象。字节数组可以是从本地文件系统或网络装入的数据。它把字节码分析成运行时数据结构、校验有效性等等。

③findSystemClass方法  findSystemClass方法从本地文件系统装入文件。它在本地文 件系统中寻找类文件,如果存在,就使用defineClass将字节数组转换成Class对象,以将该文件转换成类。当运行Java应用程序时,这是 JVM 正常装入类的缺省机制。

④resolveClass方法  resolveClass(Class c)方法解析装入的类,如果该类已经被解析过那么将不做处理。当调用loadClass方法时,通过它的resolve 参数决定是否要进行解析。

⑤findLoadedClass方法  当调用loadClass方法装入类时,调用 findLoadedClass 方法来查看ClassLoader是否已装入这个类,如果已装入,那么返回Class对象,否则返回NULL。如果强行装载已存在的类,将会抛出链接错 误。

2.3  装载的应用

一般来说,我们使用虚拟机的类装载时需要继承抽象类java.lang.ClassLoader,其中必须实现的方 法是loadClass(),对于这个方法需要实现如下操作:(1) 确认类的名称;(2) 检查请求要装载的类是否已经被装载;(3) 检查请求加载的类是否是系统类;(4) 尝试从类装载器的存储区获取所请求的类;(5) 在虚拟机中定义所请求的类;(6) 解析所请求的类;(7) 返回所请求的类。

所有的Java 虚拟机都包括一个内置的类装载器,这个内置的类库装载器被称为根装载器(bootstrap ClassLoader)。根装载器的特殊之处是它只能够装载在设计时刻已知的类,因此虚拟机假定由根装载器所装载的类都是安全的、可信任的,可以不经过 安全认证而直接运行。当应用程序需要加载并不是设计时就知道的类时,必须使用用户自定义的装载器(user-defined ClassLoader)。下面我们举例说明它的应用。

 1 public abstract class MultiClassLoader extends ClassLoader{
 2     
 3     public synchronized Class loadClass(String s, boolean flag)
 4         throws ClassNotFoundException
 5     {
 6         /* 检查类s是否已经在本地内存*/
 7         Class class1 = (Class)classes.get(s);
 8 
 9         /* 类s已经在本地内存*/
10         if(class1 != null)  return class1; 
11         try/*用默认的ClassLoader 装入类*/  {
12             class1 = super.findSystemClass(s);
13             return class1;
14         }
15         catch(ClassNotFoundException _ex)  {
16             System.out.println(">> Not a system class.");
17         }
18 
19         /* 取得类s的字节数组*/
20         byte abyte0[] = loadClassBytes(s);
21         if(abyte0 == null)   throw new ClassNotFoundException();
22 
23         /* 将类字节数组转换为类*/
24         class1 = defineClass(null, abyte0, 0, abyte0.length);
25         if(class1 == nullthrow new ClassFormatError();
26         if(flag)   resolveClass(class1); /*解析类*/
27 
28         /* 将新加载的类放入本地内存*/
29         classes.put(s, class1);
30         System.out.println(">> Returning newly loaded class.");
31 
32         /* 返回已装载、解析的类*/
33         return class1;
34     }
35     
36 }

三、Java虚拟机的类装载原理

前面我们已经知道,一个Java应用程序使用两种类型的类装载器:根装载器(bootstrap)和用户定义的装载 器(user-defined)。根装载器是Java虚拟机实现的一部分,举个例子来说,如果一个Java虚拟机是在现在已经存在并且正在被使用的操作系 统的顶部用C程序来实现的,那么根装载器将是那些C程序的一部分。根装载器以某种默认的方式将类装入,包括那些Java API的类。在运行期间一个Java程序能安装用户自己定义的类装载器。根装载器是虚拟机固有的一部分,而用户定义的类装载器则不是,它是用Java语言 写的,被编译成class文件之后然后再被装入到虚拟机,并像其它的任何对象一样可以被实例化。 Java类装载器的体系结构如下所示:

图1  Java的类装载的体系结构

Java的类装载模型是一种代理(delegation)模型。当JVM 要求类装载器CL(ClassLoader)装载一个类时,CL首先将这个类装载请求转发给他的父装载器。只有当父装载器没有装载并无法装载这个类时, CL才获得装载这个类的机会。这样, 所有类装载器的代理关系构成了一种树状的关系。树的根是类的根装载器(bootstrap ClassLoader) , 在JVM 中它以"null"表示。除根装载器以外的类装载器有且仅有一个父装载器。在创建一个装载器时, 如果没有显式地给出父装载器, 那么JVM将默认系统装载器为其父装载器。Java的基本类装载器代理结构如图2所示:

图2  Java类装载的代理结构

下面针对各种类装载器分别进行详细的说明。

根(Bootstrap) 装载器:该装载器没有父装载器,它是JVM实现的一部分,从sun.boot.class.path装载运行时库的核心代码。

扩展(Extension) 装载器:继承的父装载器为根装载器,不像根装载器可能与运行时的操作系统有关,这个类装载器是用纯Java代码实现的,它从java.ext.dirs (扩展目录)中装载代码。

系统(System or Application) 装载器:装载器为扩展装载器,我们都知道在安装JDK的时候要设置环境变量(CLASSPATH ),这个类装载器就是从java.class.path(CLASSPATH 环境变量)中装载代码的,它也是用纯Java代码实现的,同时还是用户自定义类装载器的缺省父装载器。

小应用程序(Applet) 装载器: 装载器为系统装载器,它从用户指定的网络上的特定目录装载小应用程序代码。

在设计一个类装载器的时候,应该满足以下两个条件:

  1. 对于相同的类名,类装载器所返回的对象应该是同一个类对象
  2. 如果类装载器CL1将装载类C的请求转给类装载器CL2,那么对于以下的类或接口,CL1和CL2应该返回同一个类对象:a)S为C的 直接超类;b)S为C的直接超接口;c)S为C的成员变量的类型;d)S为C的成员方法或构建器的参数类型;e)S为C的成员方法的返回类型。

每个已经装载到JVM中的类都隐式含有装载它的类装载器的信息。类方法getClassLoader 可以得到装载这个类的类装载器。一个类装载器认识的类包括它的父装载器认识的类和它自己装载的类,可见类装载器认识的类是它自己装载的类的超集。注意我们 可以得到类装载器的有关的信息,但是已经装载到JVM中的类是不能更改它的类装载器的。

Java中的类的装载过程也就是代理装载的过程。比如:Web浏览器中的JVM需要装载一个小应用程序 TestApplet。JVM调用小应用程序装载器ACL(Applet ClassLoader)来完成装载。ACL首先请求它的父装载器, 即系统装载器装载TestApplet是否装载了这个类, 由于TestApplet不在系统装载器的装载路径中, 所以系统装载器没有找到这个类, 也就没有装载成功。接着ACL自己装载TestApplet。ACL通过网络成功地找到了TestApplet.class 文件并将它导入到了JVM中。在装载过程中, JVM发现TestAppet是从超类java.applet.Applet继承的。所以JVM再次调用ACL来装载 java.applet.Applet类。ACL又再次按上面的顺序装载Applet类, 结果ACL发现他的父装载器已经装载了这个类, 所以ACL就直接将这个已经装载的类返回给了JVM , 完成了Applet类的装载。接下来,Applet类的超类也一样处理。最后, TestApplet及所有有关的类都装载到了JVM中。

四、结论

类的动态装载机制是JVM的一项核心技术, 也是容易被忽视而引起很多误解的地方。本文介绍了JVM中类装载的原理、实现以及应用,尤其分析了ClassLoader的结构、用途以及如何利用自定义 的ClassLoader装载并执行Java类,希望能使读者对JVM中的类装载有一个比较深入的理解。

转载自:http://gceclub.sun.com.cn/yuanchuang/week-8/jvm.html

posted @ 2006-02-23 18:31 Leo 阅读(348) | 评论 (0)编辑 收藏

昨天在整合Spring MVC和Velocity,Sitemesh时,又碰到了久违的中文问题。唉,JSP, mysql, struts,每次都会碰到这样的问题,总是以为这种以后不会碰到这种看似初级的问题了,结果还是躲不过。网上没查到相关资料,于是开始动手跟踪Spring和Velocity的源码,弄了一天终于搞定。后来一个同学告诉我这个问题在Spring中文论坛里有精华贴,跟我最后的解决方案一样的,气死我也。不过跟踪Spring的源代码收获还是不错的,现在又对Spring的MVC framework有了更深的认识。这里把以前碰到的中文问题大概列一下,方便以后参考。

1、JSP页面显示的中文问题
这是最初级的东西,网上到处都有,不过还是列一下吧:
Page的第一行改成:<%@ page contentType="text/html; charset=gb2312" %>
Head里加:
2、页面Form 内容提交的中文问题
在web.xml里加入:

CharacterEncodingFilter
Character Encoding Filter
no description
org.springframework.web.filter.CharacterEncodingFilter

encoding
GB2312


forceEncoding
true



CharacterEncodingFilter
/*

呵呵,这是个简单得要命的filter,如果不用Spring的话,完全可以自己写一个。其实任何的interceptor机制都可以处理这个的,不管用Webwork还是Spring的interceptor,甚至用AOP,只要在取参数前加那么一句:request.setCharacterEncoding("GB2312");就行了。以前我用struts就是在它的RequestProcessor的populate之前加了这么一行。
3、request 的parameter里要传中文参数的问题
这个问题跟Web Container有关系,记得以前我同学用WebLogic时好象没出现这样的问题。(我一般不传中文参数,呵呵)。
Tomcat里的解决方案是在server.xml里Connector port="8080"的attribute里加URIEncoding="GB2312"
当然还有最土的解决方案,虽然不太会用到,不过还是列出来,以备最无奈的时候使用:
String encodeStr=new String(fieldValue.getBytes("8859_1"), "gb2312");
4、mysql的中文问题
首先要修改mysql配置文件的encoding为GB2312,这部分的操作不记得了,毕竟好久没用mysql了。不过据说新版的mysql里有wizard可以设的。然后把jdbc connection改成如下:
jdbc:mysql://localhost:3306/bsfbookstore?useUnicode=true;characterEncoding=GB2312
另外在写程序成尽量用PrepareStatement,少用Statement,好象jdbc驱动在解析statement里的SQL包含中文时会有问题。(用PrepareStatement也是好习惯, hibernate里全用PrepareStatement的,哈哈)
5、Spring与Veclocity结合的中文问题
第一步:
在"velocityConfig"里配置velocity.propeties文件,加下面一行:
/WEB-INF/velocity.properties
呵呵,也可以在config里直接用Map把参数写进去,这样就不用properties文件,这个Spring的文档里都有。
然后在velocity.properties里写:
input.encoding=GB2312
output.encoding=GB2312
default.contentType=text/html; charset=GB2312(ms这一行没有用处,Spring有个地方读进这个参数,不过后来又覆盖掉了)
第二步:
接下来就是我昨天调了半天的那个地方,最后的解决方案很简单,在viewResolver配置里加一行:
text/html; charset=GB2312
呵呵,就这么一行害我debug了好久,跟踪了Velocity的Context设置,甚至改了Spring的源码,用了Filter,Spring的Handler interceptor来设置reponse的contentType就是没效果,结果发现Spring在Velocity View的render里加了这么一行:
response.setContentType(getContentType());
呵呵,原先设好的contentType都被冲掉了,因为render的时机是在postHandler之后,呵呵。
这个参数对jsp是没有用的,因为jsp会根据自己页面的contentType设定的,所以每个JSP必须设置自己的contentType,Velocity就不用啦。难怪以前用JSP的时候没碰到这个问题。

posted @ 2005-09-14 10:37 Leo 阅读(1071) | 评论 (3)编辑 收藏

仅列出标题
共2页: 上一页 1 2