- 浏览: 534566 次
- 性别:
- 来自: 广州
文章分类
- 全部博客 (377)
- J2EE (61)
- ORACLE (36)
- JS (15)
- EXT (0)
- win7 (12)
- TOMCAT (10)
- game (1)
- ie (5)
- etc (8)
- ibatis (3)
- ORACLE 客户端 (1)
- bat (3)
- 健康 (127)
- baby (4)
- html (12)
- myeclipse (16)
- 射手 字幕默认下载 位置 (1)
- office (0)
- car (2)
- xxx (1)
- python简介 (1)
- FusionCharts (1)
- dorado (4)
- english (3)
- weblogic (3)
- request/servlet (3)
- wsdl (1)
- offie2010 visio2010 下载 (1)
- nexus5 (0)
- chrome (3)
- ssi (2)
- 安卓 (3)
- nexus ipad (1)
- mysql (4)
- json (3)
- struts (6)
- datagrid (1)
- highcharts (1)
- tools (2)
- tool (1)
- spring3 (1)
- jedit (1)
- java (4)
- windows (1)
- easyui (2)
- 液晶显示器色温 (1)
- mybatis (1)
- where (1)
- sublime (1)
- spring4 (3)
最新评论
-
spring_springmvc:
如何在java Web项目中开发WebService接口,地址 ...
java 调用WebService服务接口 -
chenzheng8975:
阅
人生三大陷阱 -
ygbb007:
直接OD就能破解的
myBase Desktop 6.0 破解方法 -
lanlansnss:
直接删除nyfedit.ini文件即可
myBase Desktop 6.0 破解方法 -
yunzhu:
LZ写博客不太注重排版啊,排版不行别人没法看的
PermGen space
Weblogic与Java类加载器原理试验解析
sparta-紫杉 2011-4-12 16:47
通过试验,得出一个结论, 假设在Weblogic的Server/lib下有一个类,与应用的Webapp/WEB-INF/classes下的类名相同,方法名也相同,仅有在后台打印出来的字符的稍许差别,那在Weblogic启动后,无论个文件夹中的类谁是新编译的(版本新或旧),应用系统均默认是使用server/lib下的类,而不是引用Webapp/WEB-INF/classes下的类。
一、通过翻阅大量的资料了解到,java类加载的原理如下
JVM在运行时会产生三个ClassLoader:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader.其中,Bootstrap是用C++编写的,
我们在Java中看不到它,是null,它用来加载核心类库。
关于Bootstrap ClassLoader,在JVM源代码中这样写道:
static const char classpathFormat[] =
"%/lib/rt.jar: "
"%/lib/i18n.jar: "
"%/lib/sunrsasign.jar: "
"%/lib/jsse.jar: "
"%/lib/jce.jar: "
"%/lib/charsets.jar: "
"%/classes ";
知道为什么不需要在classpath中加载这些类了吧?人家在JVM启动的时候就自动加载了,并且在运行过程中根本不能修改Bootstrap加载路径。
Extension ClassLoader用来加载扩展类,即/lib/ext中的类。
最后AppClassLoader才是加载Classpath的。
ClassLoader加载类用的是委托模型。即先让Parent类(而不是Super,不是继承关系)寻找,Parent找不到才自己找。看来ClassLoader还是蛮孝顺的。三者的关系为:AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent为Bootstrap ClassLoader。加载一个类时,首先BootStrap先进行寻找,找不到再由ExtClassLoader寻找,最后才是AppClassLoader。
为什么要设计的这么复杂呢?其中一个重要原因就是安全性。比如在Applet中,如果编写了一个java.lang.String类并具有破坏性。假如不采用这种委托机制,就会将这个具有破坏性的String加载到了用户机器上,导致破坏用户安全。但采用这种委托机制则不会出现这种情况。因为要加载java.lang.String类时,系统最终会由Bootstrap进行加载,这个具有破坏性的String永远没有机会加载。
有一篇文章完整诠释了Java类加载的原理,参见如下:
---------------------------------------------------引用开始--------------------------------------------------------
《我对java中类装载的理解 》
1.Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,
类装载器所做的工作实质是把类文件从硬盘读取到内存中。
2.java中的类大致分为三种:
1.系统类
2.扩展类
3.由程序员自定义的类
3.类装载方式,有两种
1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中,
2.显式装载, 通过class.forname()等方法,显式加载需要的类
隐式加载与显式加载的区别:
两者本质是一样? ?
4.类加载的动态性体现
一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再
运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,这样的好处是节省了内存的开销,因为java最早就是为嵌入式系统而设计的,内存宝贵,这是一种可以理解的机制,而用到时再加载这也是java动态性的一种体现
5.java类装载器
Java中的类装载器实质上也是类,功能是把类载入jvm中,值得注意的是jvm的类装载器并不是一个,而是三个,层次结构如下:
Bootstrap Loader - 负责加载系统类
|
- - ExtClassLoader - 负责加载扩展类
|
- - AppClassLoader - 负责加载应用类
为什么要有三个类加载器,一方面是分工,各自负责各自的区块,另一方面为了实现委托模型,下面会谈到该模型
6. 类加载器之间是如何协调工作的
前面说了,java中有三个类加载器,问题就来了,碰到一个类需要加载时,它们之间是如何协调工作的,即java是如何区分一个类该由哪个类加载器来完成呢。
在这里java采用了委托模型机制,这个机制简单来讲,就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类”,注意喔,这句话具有递归性。
下面举一个例子来说明,为了更好的理解,先弄清楚几行代码:
Public class Test{
Public static void main(String[] arg){
ClassLoader c = Test.class.getClassLoader(); //获取Test类的类加载器
System.out.println(c);
ClassLoader c1 = c.getParent(); //获取c这个类加载器的父类加载器
System.out.println(c1);
ClassLoader c2 = c1.getParent();//获取c1这个类加载器的父类加载器
System.out.println(c2);
}
}
把以上代码存到d:\my 文件夹下,直接编译,然后在dos模式下运行
D:\my\java Test
。。。AppClassLoader。。。
。。。ExtClassLoader。。。
Null
D:\my
注: 。。。表示省略了内容
可以看出Test是由AppClassLoader加载器加载的
AppClassLoader的Parent 加载器是 ExtClassLoader
但是ExtClassLoader的Parent为 null 是怎么回事呵,朋友们留意的话,前面有提到Bootstrap Loader是用C++语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体,所以在java程序代码里试图打印出其内容时,我们就会看到输出为null。
【注:以下内容大部分引用java深度历险】
弄明白了上面的示例,接下来直接进入类装载的委托模型实例,写两个文件,如下:
文件:Test1.java
Public class Test1{
Public static void main(String[] arg){
System.out.println(Test1.class.getClassLoader());
Test2 t2 = new Test2();
T2.print();
}
}
文件: Test2.java
Public class Test2{
Public void prin(){
System.out.println(this.getClass().getClassLoader());
}
}
这两个类的作用就是打印出载入它们的类装载器是谁, 将这两个文件保存到d:\my目录下,编译后,我们在复制两份,分别置于jdk1.4\jre\classes下(注意,刚开始我们的系统下没有此目录,需自己建立) 与 jdk1.4\jre\lib\ext\classes下(同样注意,开始我们的系统下也没此目录,手工建立), 然后切换到d:\my目录下开始测试,
测试一:
<JRE所在目录>\classes下
Test1.class
Test2.class
<JRE所在目录>\lib\ext\classes下
Test1.class
Test2.class
D:\my下
Test1.class
Test2.class
dos下输入运行命令,结果如下:
D:\my>java Test1
Null
Null
D:\my>
从输出结果我们可以看出,当AppClassLoader要载入Test1.class时,先请其Parent,也就是ExtClassLoader来载入,而ExtclassLoader又请求其Parent,即Bootstrap Loader来载入Test1.class. 由于 <JRE所在目录>\Classes目录为Bootstrap Loader的搜索路径之一,所以Bootstrap Loader找到了Test1.class,因此将它载入,接着在Test1.class之内有载入Test2.class的需求,由于Test1.class是由Bootstrap Loader所载入,所以Test2.class内定是由Bootstrap Loader根据其搜索路径来找,因Test2.class也位于Bootstrap Loader可以找到的路径下,所以也被载入了,最后我们看到Test1.class与Test2.class都是由Bootstrap Loader(null)载入。
测试二:
<JRE所在目录>\classes下
Test1.class
<JRE所在目录>\lib\ext\classes下
Test1.class
Test2.class
D:\my下
Test1.class
Test2.class
dos下输入运行命令,结果如下:
D:\my>java Test1
Null
Exception in thread “main” java.lang.NoClassdefFoundError:Test2 at Test1.main。。。
D:\my>
从输出结果我们可以看出,当AppClassLoader要载入Test1.class时,先请其Parent,也就是ExtClassLoader来载入,而ExtclassLoader又请求其Parent,即Bootstrap Loader来载入Test1.class. 由于 <JRE所在目录>\Classes目录为Bootstrap Loader的搜索路径之一,所以Bootstrap Loader找到了Test1.class,因此将它载入,接着在Test1.class之内有载入Test2.class的需求,由于Test1.class是由Bootstrap Loader所载入,所以Test2.class内定是由Bootstrap Loader根据其搜索路径来找,但是因为Bootstrap Loader根本找不到Test2.class(被我们删除了),而Bootstrap Loader又没有Parent,所以无法载入Test2.class.最后我们看到Test1.class是由Bootstrap Loader(null)载入,而Test2.class则无法载入
测试三
<JRE所在目录>\classes下
Test2.class
<JRE所在目录>\lib\ext\classes下
Test1.class
Test2.class
D:\my下
Test1.class
Test2.class
dos下输入运行命令,结果如下:
D:\my>java Test1
。。。ExtClassLoader。。。
Null
D:\my>
从输出结果我们可以看出,当AppClassLoader要载入Test1.class时,先请其Parent,也就是ExtClassLoader来载入,而ExtclassLoader又请求其Parent,即Bootstrap Loader来载入Test1.class.但是Bootstrap Loader无法在其搜索路径下找到Test1.class(被我们删掉了),所以ExtClassLoader只得自己搜索,因此ExtClassLoader在其搜索路径 <JRE所在目录>\lib\ext\classes下找到了Test1.class,因此将它载入,接着在Test1.class之内有载入Test2.class的需求,由于Test1.class是由ExtClassLoader所载入,所以Test2.class内定是由ExtClassLoader根据其搜索路径来找,但是因为ExtClassLoader有Parent,所以先由Bootstrap Loader帮忙寻找,Test2.class位于Bootstrap Loader可以找到的路径下,所以被Bootstrap Loader载入了.最后我们看到Test1.class是由ExtClassLoader载入,而Test2.class则是由Bootstrap Loader(null)载入
了解了以上规则,请朋友们自行分析以下场景的执行结果
测试四:
<JRE所在目录>\classes下
<JRE所在目录>\lib\ext\classes下
Test1.class
Test2.class
D:\my下
Test1.class
Test2.class
测试五:
<JRE所在目录>\classes下
<JRE所在目录>\lib\ext\classes下
Test1.class
D:\my下
Test1.class
Test2.class
测试六:
<JRE所在目录>\classes下
<JRE所在目录>\lib\ext\classes下
Test2.class
D:\my下
Test1.class
Test2.class
测试七:
<JRE所在目录>\classes下
<JRE所在目录>\lib\ext\classes下
D:\my下
Test1.class
Test2.class
---------------------------------------------------引用结束,感谢作者哈!--------------------------------------------------------
上述经过自己的猜测,并进行实地测试,发现确实有一定的道理。
二、Weblogic对于类加载的原理
容器也对类加载进行了封装, 不过不同的容器对于类加载有所不同。 在Weblogic中,是这样的原理。
Weblogic中classloader是分层次的,它只能加载比它层次高的类及它自身的类,同层次的类及比它层次低的类都不能加载。
在weblogic中的classloader有5个层次,从高到低排:
a. jdk
b. jdk ext
c. system classpath
d. ( APP-INF/classes and APP-INF/lib )
e. ( WEB-INF/classes and WEB-INF/lib )
注意:这里是先加载classes中的类,再加载lib中的类, 若要修改它的加载顺序,可以通过在Weblogic.xml(版本为8)中加入以下代码:
<container-descriptor>
<prefer-web-inf-classes> true </prefer-web-inf-classes>
</container-descriptor>
f. ejb.jar
注意:e 和 f 的classloader是同级的。
所以APP-INF/lib和APP-INF/classes下类不能实例化webapp下的类,这点尤其要注意,否则会报类找不到的错误。
还有一篇网文《Weblogic10 Classloading 问题》比较详细地介绍了Weblogic加载类的原理。
来自Weblogic官方的说明文件中,对于Weblogic的类加载顺序给出了一个比较清晰和简单的描述:
当部署一个应用的时候,weblogic server会自动创建一个具有层次结构的类装载器。
1、a.Application Classloader负责装载应用中的所有的EJB JAR文件;
2、b.Web Application Classloader负责装载所有的Web application 中的WAR 文件(所有得jsp文件除外);
3、c.Jsp Classloader 负责装载Web application 中的所有的jsp 文件。
Tomcat与Weblogic是相反的:对于运行在 Java EE 容器中的 Web 应用来说,类加载器的实现方式与一般的 Java 应用有所不同。不同的 Web 容器的实现方式也会有所不同。以 Apache Tomcat 来说,每个 Web 应用都有一个对应的类加载器实例。该类加载器也使用代理模式,所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。这是 Java Servlet 规范中的推荐做法,其目的是使得 Web 应用自己的类的优先级高于 Web 容器提供的类。这种代理模式的一个例外是:Java 核心库的类是不在查找范围之内的。这也是为了保证 Java 核心库的类型安全。
最后,总结一下,在Weblogic服务启动的过程中,自动形成一个具有层次结构的类装载器,首先装载jdk及java扩展jar包或类;然后再加载Weblogic本身使用的各个jar包或类;然后再加载Web应用文件夹里面的classes下的类,然后再加载Web应用文件夹里面lib下的jar包或类。也就是说,每个层次的类装载器均对应不同的类路径,它们是一一对应的。 比如System装载器对应着jdk及扩展路径;Application装载器对应着Weblogic的相关类;而web 应用装载器对应着webapp应用下的classes和lib下的路径;而jsp装载器则对应着jsp文件。
当然,在加载过程中,若在高层次的加载器中已经加载了某类,那么再以后的加载中,再次遇到该类也不会加载,只是会忽略。加载完成之后,将类放入Cache中供系统应用调用。
在系统的运行过程中,若遇到使用该类的情况,则会遵循先通过其父类加载器进行加载的原则,比方说,我要加载一个WSWordManager类,
则系统会首先在Cache中寻找,若找不到,则调用父装载器到与之对应的路径里面去寻找,一直向上,找着了则进行加载,若找不着则报出ClassNotFound的异常。
哈哈,自己耗时一天的试验也能够证明了这一点。
在解决系统项目“原生类重复加载,异常为jacob.dll already loaded in another classloader”问题时,被迫研究了上述的原理。现在问题也算是较完美的解决了,更关键的是学到了很多相关底层的知识,对于自己技术的提升是很有好处的。
===========================
关于Tomcat下部署多个应用jar加载
由于项目的需要在tomcat服务器下发布多个项目,对于使用框架相同的项目,可以把相同的JAR包集中放置在Tomcat下的lib中共同使用,降低项目的大小和加载使用的内存。
首先分析一下Tomcat加载class的顺序:
由于Tomcat 5.x和6.x的lib放置有差别,5.x有三个地方common/lib,server/lib,shared/lib。其中common中项目和tomcat服务器都可以访问到,server下的lib只有tomcat能访问,shared下的只有项目可以访问,tomcat服务是访问不到。6.x更是简单了直接在根目录下lib包中,都可以访问。
说说5.x的加载顺序:
1.最先是$JAVA_HOME/jre/lib/ext/下的jar文件。
2.环境变量CLASSPATH中的jar和class文件。
3.$CATALINA_HOME/common/classes下的class文件。
4.$CATALINA_HOME/commons/endorsed下的jar文件。
5.$CATALINA_HOME/commons/i18n下的jar文件。
6.$CATALINA_HOME/common/lib 下的jar文件。
(JDBC驱动之类的jar文件可以放在这里,这样就可以避免在server.xml配置好数据源却出现找不到JDBC Driver的情况。)
7.$CATALINA_HOME/server/classes下的class文件。
8.$CATALINA_HOME/server/lib/下的jar文件。
9.$CATALINA_BASE/shared/classes 下的class文件。
10.$CATALINA_BASE/shared/lib下的jar文件。
11.各自具体的webapp /WEB-INF/classes下的class文件。
12.各自具体的webapp /WEB-INF/lib下的jar文件。
而class的搜寻方式是:
-------------
Bootstrap classes of your JVM
System class loader classses (described above)
/WEB-INF/classes of your web application
/WEB-INF/lib/*.jar of your web application
$CATALINA_HOME/common/classes
$CATALINA_HOME/common/endorsed/*.jar
$CATALINA_HOME/common/i18n/*.jar
$CATALINA_HOME/common/lib/*.jar
$CATALINA_BASE/shared/classes
$CATALINA_BASE/shared/lib/*.jar
所以在多个项目部署的时候可以,在项目中找到相同的包,放置在tomcat下lib中,项目都可以引用,各自独需的Jar放到项目就可以了。
另外关于lib中的jar加载方式是自上而下的方式,比如把同一个jar包,命不同的名字,每个输出不同的信息,自上而下开始寻找,发现后就不在寻找,位置靠上的先输出,而下面的不会输出。
sparta-紫杉 2011-4-12 16:47
通过试验,得出一个结论, 假设在Weblogic的Server/lib下有一个类,与应用的Webapp/WEB-INF/classes下的类名相同,方法名也相同,仅有在后台打印出来的字符的稍许差别,那在Weblogic启动后,无论个文件夹中的类谁是新编译的(版本新或旧),应用系统均默认是使用server/lib下的类,而不是引用Webapp/WEB-INF/classes下的类。
一、通过翻阅大量的资料了解到,java类加载的原理如下
JVM在运行时会产生三个ClassLoader:Bootstrap ClassLoader、Extension ClassLoader和AppClassLoader.其中,Bootstrap是用C++编写的,
我们在Java中看不到它,是null,它用来加载核心类库。
关于Bootstrap ClassLoader,在JVM源代码中这样写道:
static const char classpathFormat[] =
"%/lib/rt.jar: "
"%/lib/i18n.jar: "
"%/lib/sunrsasign.jar: "
"%/lib/jsse.jar: "
"%/lib/jce.jar: "
"%/lib/charsets.jar: "
"%/classes ";
知道为什么不需要在classpath中加载这些类了吧?人家在JVM启动的时候就自动加载了,并且在运行过程中根本不能修改Bootstrap加载路径。
Extension ClassLoader用来加载扩展类,即/lib/ext中的类。
最后AppClassLoader才是加载Classpath的。
ClassLoader加载类用的是委托模型。即先让Parent类(而不是Super,不是继承关系)寻找,Parent找不到才自己找。看来ClassLoader还是蛮孝顺的。三者的关系为:AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent为Bootstrap ClassLoader。加载一个类时,首先BootStrap先进行寻找,找不到再由ExtClassLoader寻找,最后才是AppClassLoader。
为什么要设计的这么复杂呢?其中一个重要原因就是安全性。比如在Applet中,如果编写了一个java.lang.String类并具有破坏性。假如不采用这种委托机制,就会将这个具有破坏性的String加载到了用户机器上,导致破坏用户安全。但采用这种委托机制则不会出现这种情况。因为要加载java.lang.String类时,系统最终会由Bootstrap进行加载,这个具有破坏性的String永远没有机会加载。
有一篇文章完整诠释了Java类加载的原理,参见如下:
---------------------------------------------------引用开始--------------------------------------------------------
《我对java中类装载的理解 》
1.Java中的所有类,必须被装载到jvm中才能运行,这个装载工作是由jvm中的类装载器完成的,
类装载器所做的工作实质是把类文件从硬盘读取到内存中。
2.java中的类大致分为三种:
1.系统类
2.扩展类
3.由程序员自定义的类
3.类装载方式,有两种
1.隐式装载, 程序在运行过程中当碰到通过new 等方式生成对象时,隐式调用类装载器加载对应的类到jvm中,
2.显式装载, 通过class.forname()等方法,显式加载需要的类
隐式加载与显式加载的区别:
两者本质是一样? ?
4.类加载的动态性体现
一个应用程序总是由n多个类组成,Java程序启动时,并不是一次把所有的类全部加载后再
运行,它总是先把保证程序运行的基础类一次性加载到jvm中,其它类等到jvm用到的时候再加载,这样的好处是节省了内存的开销,因为java最早就是为嵌入式系统而设计的,内存宝贵,这是一种可以理解的机制,而用到时再加载这也是java动态性的一种体现
5.java类装载器
Java中的类装载器实质上也是类,功能是把类载入jvm中,值得注意的是jvm的类装载器并不是一个,而是三个,层次结构如下:
Bootstrap Loader - 负责加载系统类
|
- - ExtClassLoader - 负责加载扩展类
|
- - AppClassLoader - 负责加载应用类
为什么要有三个类加载器,一方面是分工,各自负责各自的区块,另一方面为了实现委托模型,下面会谈到该模型
6. 类加载器之间是如何协调工作的
前面说了,java中有三个类加载器,问题就来了,碰到一个类需要加载时,它们之间是如何协调工作的,即java是如何区分一个类该由哪个类加载器来完成呢。
在这里java采用了委托模型机制,这个机制简单来讲,就是“类装载器有载入类的需求时,会先请示其Parent使用其搜索路径帮忙载入,如果Parent 找不到,那么才由自己依照自己的搜索路径搜索类”,注意喔,这句话具有递归性。
下面举一个例子来说明,为了更好的理解,先弄清楚几行代码:
Public class Test{
Public static void main(String[] arg){
ClassLoader c = Test.class.getClassLoader(); //获取Test类的类加载器
System.out.println(c);
ClassLoader c1 = c.getParent(); //获取c这个类加载器的父类加载器
System.out.println(c1);
ClassLoader c2 = c1.getParent();//获取c1这个类加载器的父类加载器
System.out.println(c2);
}
}
把以上代码存到d:\my 文件夹下,直接编译,然后在dos模式下运行
D:\my\java Test
。。。AppClassLoader。。。
。。。ExtClassLoader。。。
Null
D:\my
注: 。。。表示省略了内容
可以看出Test是由AppClassLoader加载器加载的
AppClassLoader的Parent 加载器是 ExtClassLoader
但是ExtClassLoader的Parent为 null 是怎么回事呵,朋友们留意的话,前面有提到Bootstrap Loader是用C++语言写的,依java的观点来看,逻辑上并不存在Bootstrap Loader的类实体,所以在java程序代码里试图打印出其内容时,我们就会看到输出为null。
【注:以下内容大部分引用java深度历险】
弄明白了上面的示例,接下来直接进入类装载的委托模型实例,写两个文件,如下:
文件:Test1.java
Public class Test1{
Public static void main(String[] arg){
System.out.println(Test1.class.getClassLoader());
Test2 t2 = new Test2();
T2.print();
}
}
文件: Test2.java
Public class Test2{
Public void prin(){
System.out.println(this.getClass().getClassLoader());
}
}
这两个类的作用就是打印出载入它们的类装载器是谁, 将这两个文件保存到d:\my目录下,编译后,我们在复制两份,分别置于jdk1.4\jre\classes下(注意,刚开始我们的系统下没有此目录,需自己建立) 与 jdk1.4\jre\lib\ext\classes下(同样注意,开始我们的系统下也没此目录,手工建立), 然后切换到d:\my目录下开始测试,
测试一:
<JRE所在目录>\classes下
Test1.class
Test2.class
<JRE所在目录>\lib\ext\classes下
Test1.class
Test2.class
D:\my下
Test1.class
Test2.class
dos下输入运行命令,结果如下:
D:\my>java Test1
Null
Null
D:\my>
从输出结果我们可以看出,当AppClassLoader要载入Test1.class时,先请其Parent,也就是ExtClassLoader来载入,而ExtclassLoader又请求其Parent,即Bootstrap Loader来载入Test1.class. 由于 <JRE所在目录>\Classes目录为Bootstrap Loader的搜索路径之一,所以Bootstrap Loader找到了Test1.class,因此将它载入,接着在Test1.class之内有载入Test2.class的需求,由于Test1.class是由Bootstrap Loader所载入,所以Test2.class内定是由Bootstrap Loader根据其搜索路径来找,因Test2.class也位于Bootstrap Loader可以找到的路径下,所以也被载入了,最后我们看到Test1.class与Test2.class都是由Bootstrap Loader(null)载入。
测试二:
<JRE所在目录>\classes下
Test1.class
<JRE所在目录>\lib\ext\classes下
Test1.class
Test2.class
D:\my下
Test1.class
Test2.class
dos下输入运行命令,结果如下:
D:\my>java Test1
Null
Exception in thread “main” java.lang.NoClassdefFoundError:Test2 at Test1.main。。。
D:\my>
从输出结果我们可以看出,当AppClassLoader要载入Test1.class时,先请其Parent,也就是ExtClassLoader来载入,而ExtclassLoader又请求其Parent,即Bootstrap Loader来载入Test1.class. 由于 <JRE所在目录>\Classes目录为Bootstrap Loader的搜索路径之一,所以Bootstrap Loader找到了Test1.class,因此将它载入,接着在Test1.class之内有载入Test2.class的需求,由于Test1.class是由Bootstrap Loader所载入,所以Test2.class内定是由Bootstrap Loader根据其搜索路径来找,但是因为Bootstrap Loader根本找不到Test2.class(被我们删除了),而Bootstrap Loader又没有Parent,所以无法载入Test2.class.最后我们看到Test1.class是由Bootstrap Loader(null)载入,而Test2.class则无法载入
测试三
<JRE所在目录>\classes下
Test2.class
<JRE所在目录>\lib\ext\classes下
Test1.class
Test2.class
D:\my下
Test1.class
Test2.class
dos下输入运行命令,结果如下:
D:\my>java Test1
。。。ExtClassLoader。。。
Null
D:\my>
从输出结果我们可以看出,当AppClassLoader要载入Test1.class时,先请其Parent,也就是ExtClassLoader来载入,而ExtclassLoader又请求其Parent,即Bootstrap Loader来载入Test1.class.但是Bootstrap Loader无法在其搜索路径下找到Test1.class(被我们删掉了),所以ExtClassLoader只得自己搜索,因此ExtClassLoader在其搜索路径 <JRE所在目录>\lib\ext\classes下找到了Test1.class,因此将它载入,接着在Test1.class之内有载入Test2.class的需求,由于Test1.class是由ExtClassLoader所载入,所以Test2.class内定是由ExtClassLoader根据其搜索路径来找,但是因为ExtClassLoader有Parent,所以先由Bootstrap Loader帮忙寻找,Test2.class位于Bootstrap Loader可以找到的路径下,所以被Bootstrap Loader载入了.最后我们看到Test1.class是由ExtClassLoader载入,而Test2.class则是由Bootstrap Loader(null)载入
了解了以上规则,请朋友们自行分析以下场景的执行结果
测试四:
<JRE所在目录>\classes下
<JRE所在目录>\lib\ext\classes下
Test1.class
Test2.class
D:\my下
Test1.class
Test2.class
测试五:
<JRE所在目录>\classes下
<JRE所在目录>\lib\ext\classes下
Test1.class
D:\my下
Test1.class
Test2.class
测试六:
<JRE所在目录>\classes下
<JRE所在目录>\lib\ext\classes下
Test2.class
D:\my下
Test1.class
Test2.class
测试七:
<JRE所在目录>\classes下
<JRE所在目录>\lib\ext\classes下
D:\my下
Test1.class
Test2.class
---------------------------------------------------引用结束,感谢作者哈!--------------------------------------------------------
上述经过自己的猜测,并进行实地测试,发现确实有一定的道理。
二、Weblogic对于类加载的原理
容器也对类加载进行了封装, 不过不同的容器对于类加载有所不同。 在Weblogic中,是这样的原理。
Weblogic中classloader是分层次的,它只能加载比它层次高的类及它自身的类,同层次的类及比它层次低的类都不能加载。
在weblogic中的classloader有5个层次,从高到低排:
a. jdk
b. jdk ext
c. system classpath
d. ( APP-INF/classes and APP-INF/lib )
e. ( WEB-INF/classes and WEB-INF/lib )
注意:这里是先加载classes中的类,再加载lib中的类, 若要修改它的加载顺序,可以通过在Weblogic.xml(版本为8)中加入以下代码:
<container-descriptor>
<prefer-web-inf-classes> true </prefer-web-inf-classes>
</container-descriptor>
f. ejb.jar
注意:e 和 f 的classloader是同级的。
所以APP-INF/lib和APP-INF/classes下类不能实例化webapp下的类,这点尤其要注意,否则会报类找不到的错误。
还有一篇网文《Weblogic10 Classloading 问题》比较详细地介绍了Weblogic加载类的原理。
来自Weblogic官方的说明文件中,对于Weblogic的类加载顺序给出了一个比较清晰和简单的描述:
当部署一个应用的时候,weblogic server会自动创建一个具有层次结构的类装载器。
1、a.Application Classloader负责装载应用中的所有的EJB JAR文件;
2、b.Web Application Classloader负责装载所有的Web application 中的WAR 文件(所有得jsp文件除外);
3、c.Jsp Classloader 负责装载Web application 中的所有的jsp 文件。
Tomcat与Weblogic是相反的:对于运行在 Java EE 容器中的 Web 应用来说,类加载器的实现方式与一般的 Java 应用有所不同。不同的 Web 容器的实现方式也会有所不同。以 Apache Tomcat 来说,每个 Web 应用都有一个对应的类加载器实例。该类加载器也使用代理模式,所不同的是它是首先尝试去加载某个类,如果找不到再代理给父类加载器。这与一般类加载器的顺序是相反的。这是 Java Servlet 规范中的推荐做法,其目的是使得 Web 应用自己的类的优先级高于 Web 容器提供的类。这种代理模式的一个例外是:Java 核心库的类是不在查找范围之内的。这也是为了保证 Java 核心库的类型安全。
最后,总结一下,在Weblogic服务启动的过程中,自动形成一个具有层次结构的类装载器,首先装载jdk及java扩展jar包或类;然后再加载Weblogic本身使用的各个jar包或类;然后再加载Web应用文件夹里面的classes下的类,然后再加载Web应用文件夹里面lib下的jar包或类。也就是说,每个层次的类装载器均对应不同的类路径,它们是一一对应的。 比如System装载器对应着jdk及扩展路径;Application装载器对应着Weblogic的相关类;而web 应用装载器对应着webapp应用下的classes和lib下的路径;而jsp装载器则对应着jsp文件。
当然,在加载过程中,若在高层次的加载器中已经加载了某类,那么再以后的加载中,再次遇到该类也不会加载,只是会忽略。加载完成之后,将类放入Cache中供系统应用调用。
在系统的运行过程中,若遇到使用该类的情况,则会遵循先通过其父类加载器进行加载的原则,比方说,我要加载一个WSWordManager类,
则系统会首先在Cache中寻找,若找不到,则调用父装载器到与之对应的路径里面去寻找,一直向上,找着了则进行加载,若找不着则报出ClassNotFound的异常。
哈哈,自己耗时一天的试验也能够证明了这一点。
在解决系统项目“原生类重复加载,异常为jacob.dll already loaded in another classloader”问题时,被迫研究了上述的原理。现在问题也算是较完美的解决了,更关键的是学到了很多相关底层的知识,对于自己技术的提升是很有好处的。
===========================
关于Tomcat下部署多个应用jar加载
由于项目的需要在tomcat服务器下发布多个项目,对于使用框架相同的项目,可以把相同的JAR包集中放置在Tomcat下的lib中共同使用,降低项目的大小和加载使用的内存。
首先分析一下Tomcat加载class的顺序:
由于Tomcat 5.x和6.x的lib放置有差别,5.x有三个地方common/lib,server/lib,shared/lib。其中common中项目和tomcat服务器都可以访问到,server下的lib只有tomcat能访问,shared下的只有项目可以访问,tomcat服务是访问不到。6.x更是简单了直接在根目录下lib包中,都可以访问。
说说5.x的加载顺序:
1.最先是$JAVA_HOME/jre/lib/ext/下的jar文件。
2.环境变量CLASSPATH中的jar和class文件。
3.$CATALINA_HOME/common/classes下的class文件。
4.$CATALINA_HOME/commons/endorsed下的jar文件。
5.$CATALINA_HOME/commons/i18n下的jar文件。
6.$CATALINA_HOME/common/lib 下的jar文件。
(JDBC驱动之类的jar文件可以放在这里,这样就可以避免在server.xml配置好数据源却出现找不到JDBC Driver的情况。)
7.$CATALINA_HOME/server/classes下的class文件。
8.$CATALINA_HOME/server/lib/下的jar文件。
9.$CATALINA_BASE/shared/classes 下的class文件。
10.$CATALINA_BASE/shared/lib下的jar文件。
11.各自具体的webapp /WEB-INF/classes下的class文件。
12.各自具体的webapp /WEB-INF/lib下的jar文件。
而class的搜寻方式是:
-------------
Bootstrap classes of your JVM
System class loader classses (described above)
/WEB-INF/classes of your web application
/WEB-INF/lib/*.jar of your web application
$CATALINA_HOME/common/classes
$CATALINA_HOME/common/endorsed/*.jar
$CATALINA_HOME/common/i18n/*.jar
$CATALINA_HOME/common/lib/*.jar
$CATALINA_BASE/shared/classes
$CATALINA_BASE/shared/lib/*.jar
所以在多个项目部署的时候可以,在项目中找到相同的包,放置在tomcat下lib中,项目都可以引用,各自独需的Jar放到项目就可以了。
另外关于lib中的jar加载方式是自上而下的方式,比如把同一个jar包,命不同的名字,每个输出不同的信息,自上而下开始寻找,发现后就不在寻找,位置靠上的先输出,而下面的不会输出。
发表评论
-
HttpClient4.X的代理密码
2018-07-11 23:48 871HttpClient4.X的代理添加实现(转自http://b ... -
Enable debug logging for this logger for a complete list of JARs that were scann
2018-06-28 21:59 1794修改EL表达式,例如"${owner.new}& ... -
job spring3--spring4
2017-12-28 09:21 461job spring3-->spring4 org ... -
java的(PO,VO,TO,BO,DAO,POJO)解释
2017-06-12 10:26 536java的(PO,VO,TO,BO,DAO,POJO) ... -
高內聚、低耦合。多聚合、少繼承
2017-05-22 15:52 799面向對象原則:高內聚、低耦合。多聚合、少繼承 2015-05 ... -
savesavesavesavesavesave
2017-05-18 17:24 493savesavesavesavesavesave uploa ... -
eclipse 图标的含义
2016-10-13 15:11 518... -
log4j-struts异常日志打印
2016-08-24 17:04 1062log4j.appender.myfile.layout.Co ... -
struts2 Action中获取request, response对象
2016-07-21 09:43 1171ajax不能完成文件下载, ... -
request和response的中文乱码问题
2016-03-04 10:39 1377request和response的中文乱码问题 request ... -
sun.misc.BASE64Encoder找不到jar包的解决方法
2015-06-29 15:10 845sun.misc.BASE64Encoder找不到jar包的解 ... -
jar 包 用途(jar功能对照表)
2015-04-08 16:07 1646jar包用途(jar功能对照表) axis.jar SOAP ... -
struts上传
2014-08-22 15:37 477jsp页面 <meta http-equiv=" ... -
java调用打印机打印自定义的图片
2014-08-19 16:03 3831package test; import java.awt. ... -
JAVA基类和派生类
2014-05-22 16:02 1299JAVA基类和派生类 从 ... -
web.xml配置详解
2014-02-20 08:56 8242007-10-16 17:12 Web.XML 配 ... -
不要使用sun.misc.BASE64Encoder
2014-02-18 10:10 1179一直以来Base64的加密解密都是使用sun.misc包下的B ... -
自定义Result
2014-01-13 10:18 752自定义Result 5.7.1 什么是自定义Result ... -
ant+javadoc生成API文档
2014-01-02 17:16 972ant+javadoc生成API文档 分类: ant 2006 ... -
ssi包下载
2013-12-27 10:15 970Spring官网改版后找了好 ...
相关推荐
WebLogic的主要类加载器包括Bootstrap类加载器、Extension类加载器、Application类加载器以及Domain类加载器等。 2. **Bootstrap类加载器** Bootstrap类加载器是JVM的一部分,负责加载JDK的核心类库,如rt.jar,这...
总之,Java的类加载器是理解JVM工作原理的关键一环,其背后涉及到了许多高级特性,如设计模式、动态加载、模块化以及安全性。通过对这些知识点的深入学习,开发者可以更好地控制和优化应用程序的行为。
由于 Java 类加载器不支持卸载类,因此在热部署过程中,旧的类加载器会被替换为新的类加载器,所有旧类加载器加载的类都需要重新加载,相应实例也需要重新创建。每个应用拥有独立的类加载器层次结构,确保了更新不...
WebLogic&Java精华 weblogic&java精华java weblogic 面试题 下面是根据提供的文件信息生成的相关知识点: Java基础 1. Java 基础知识 在 Java 中,加载和启动 Servlet 后,为一个 Servlet 创建了一个还是多个...
java实现自定义Weblogic监控java实现自定义Weblogic监控java实现自定义Weblogic监控
源码通常会包含示例的Servlet、EJB、JSP或其他Java EE组件,通过分析和运行这些代码,学习者能够更好地理解Java EE的架构和工作原理。 在压缩包“codes”中,我们可以期待找到各种J2EE组件的源代码,例如: 1. ...
WebLogic Dump 文件分析器是一款专为管理和解析Oracle WebLogic Server应用服务器生成的诊断(dump)文件而设计的实用工具。WebLogic Server是企业级Java EE应用程序的流行平台,它在运行过程中可能会因为各种原因...
对于Java类文件的更改,WebLogic可以通过Classloader机制来检测变化并自动重新加载。但默认情况下,WebLogic可能不会自动检测到源代码的修改。 2. **Eclipse集成开发环境的设置**: 使用Eclipse这样的IDE时,可以...
7. **分析类加载顺序**:通过WebLogic的日志或工具,如`jarscan`,可以分析类加载顺序,找出冲突的根源。 8. **使用Maven或Gradle的依赖管理**:通过构建工具的依赖管理功能,可以更有效地控制应用的依赖关系,防止...
了解并掌握这些步骤和Eclipse与WebLogic的集成特性,将有助于你高效地进行Java EE项目的开发和调试。同时,提供的文档如`weblogic.doc`和`EclipseWebLogic1.1.1.pdf`将提供更深入的指导,包括可能遇到的问题解决方案...
自己写好的一个简单的weblogic jms的简单实现,包括点对点的实现,以及topic的实现,适合初学者参考
Java 实现自定义 Weblogic 监控 Java 是一种广泛使用的编程语言,Weblogic 是一款流行的应用服务器,而 Java 实现自定义 Weblogic 监控则是指使用 Java 语言来实现对 Weblogic 服务器的监控。本文将详细介绍如何...
补丁号与WebLogic Server的版本相对应,例如: - 对于9.1.0版本,使用Patch 8194412 - 对于9.2.2、9.2.3、9.2.4版本,也使用Patch 8194412 - 对于10.0.1版本,同样使用Patch 8194412 修复此问题的步骤如下: 1. ...
这种问题在Weblogic服务器中尤其常见,因为Weblogic服务器具有自己的类加载器机制,可能会与项目中的类加载器冲突。 解决包冲突问题的方法有多种,下面我们将逐一介绍: 1. 使用endorsed目录 endorsed目录是...
在IT行业中,开发Java Web项目是一项常见的任务,而Eclipse作为一款强大的集成开发环境(IDE)和WebLogic作为企业级的Java应用服务器,是许多开发者首选的工具组合。本篇文章将详细阐述如何利用Eclipse进行Java Web...
在Java企业级应用开发中,WebLogic Server是一个广泛使用的应用服务器,由Oracle公司提供,它支持Java EE(Enterprise Edition)规范,包括EJB(Enterprise JavaBeans)、JMS(Java Message Service)以及WS(Web ...
【Java中间件之WebLogic详解】 WebLogic,全称WebLogic Server,是由Oracle公司开发的一款企业级Java EE应用服务器,是构建、部署和管理互联网和内部网Web应用程序的重要平台。虽然描述中提到了WebSphere ...
Java版 jmx 监控weblogic 生成html
在新Linux系统上安装旧版Java 1.4和WebLogic 8是一项具有挑战性的任务,因为这些软件可能不直接支持最新的操作系统版本。不过,通过一些步骤和注意事项,我们可以成功地在新Linux环境下运行这两个经典的组件。以下是...