Topic: Java中的Classloader

  Print this page

1.Java中的Classloader Copy to clipboard
Posted by: yjwang
Posted on: 2003-03-16 20:01

Java中的Classloader

作为一种编程语言, 我总觉得Java有那么一点奇异, 或者说混血: 它不象传统的编译型语言(比如C/C++)那么纯粹, 它不仅仅是一种"语言". 比如Java中有Classloader的概念, 而这通常是操作系统的一部分. 理解这一概念对于J2EE尤其重要. 下面的文章译自IBM的一篇文档, 很清楚地解释了这个重要的概念.

Classloader是如何工作的?

Java虚拟机中的每个Java类都是由某个classloader载入的, classloader本身也是Java类, 所以这一点就特别有趣. 那么, 这些classloader又是如何载入的呢? 这好像是一个悖论. 幸运的是, 事实并非如此. Java包含一个自举classloader, 它是用本地代码写的, 是JVM的一部分. 这个自举classloader的主要作用是载入Java核心类, 从而自举整个Java环境.

在一个企业Java应用中, 使用到的许多类都不是Java核心类. 比如, 程序员也许会引用其应用中的另外一个类, 或者Java扩展中的一个类. Java扩展是扩展Java核心平台功能的一些Java包. 为了隔离这两种不同的Java类, Java采用两种不同的classloader: application和extension classloader. 它们都是用Java写的. 这意味着这些类将被它们特定的classloader载入, 如下例所示.
public class WhichClassLoader {
WhichClassLoader( ) {}
public static void main (String args[] ) throws Exception {
//Retrieve the classpaths
StringBuffer bootstrapClassPath=new StringBuffer(System.getProperties().getProperty("sun.boot.class.path"));
StringBuffer extensionClassPath=new StringBuffer(System.getProperties().getProperty("java.ext.dirs"));
StringBuffer systemClassPath=new StringBuffer(System.getProperties().getProperty("java.class.path"));
System.out.println("\nBootstrap classpath= \n"+ bootstrapClassPath + "\n");
System.out.println("Extension classpath= "+ extensionClassPath + "\n");
System.out.println("System classpath= "+ systemClassPath + "\n" );

//Create new object instances
java.lang.Object object = new java.lang.Object();
javax.naming.InitialContext initialContext = new javax.naming.InitialContext();
WhichClassLoader whichClassLoader = new WhichClassLoader();
System.out.println("\nJava Core file,java.lang.Object, was loaded by: " + object.getClass().getClassLoader());
System.out.println("\nExtension file, javax.naming.InitialContext, was loaded by: " + initialContext.getClass().getClassLoader());
System.out.println("\nUser file, WhichClassLoader, was loaded by: " + whichClassLoader1.getClass().getClassLoader() + "\n");
}
}

这个例子相当简单, 它查询和显示classloader的路径, 然后创建三个新的对象示例, 类型各不相同: 一个Java核心类(java.lang.Object), 一个Java扩展类(javax.naming.InitialContext)和一个用户类(WhichClassLoader). 最后它打印出载入这些类的classloader. 运行这个例子的结果是:
D:\Classpath_Project\src>java -classpath . WhichClassLoader
Bootstrap classpath= D:\jdk1.2.2\jre\lib\rt.jar;D:\jdk1.2.2\jre\lib\i18n.jar;D:\jdk1.2.2\jre\classes
Extension classpath= D:\jdk1.2.2\jre\lib\ext
System classpath=
Java Core file,java.lang.Object, was loaded by: null
Extension file, javax.naming.InitialContext, was loaded by: sun.misc.Launcher$ExtClassLoader@f0272827
User file, WhichClassLoader, was loaded by: sun.misc.Launcher$AppClassLoader@f023282

这个结果中有几点值得一提. 比如, 你也许想知道为什么用户类是被sun.misc.Launcher$AppClassLoader@f023282所载入的, 而不是更通用的形式比如sun.misc.Launcher$AppClassLoader或者Application ClassLoader. 原因在于getClassLoader()方法的返回类型的是java.lang.ClassLoader. 当这个对象实例被送到一个输出流上时, 打印的是实例的名字, 这一名字在JVM每次启动时都会改变. 在我们这次特定的运行中, application classloader的实例名是sun.misc.Launcher$AppClassLoader@a1b1234. 对于extension classloader也是这样. 有趣的是, 自举classloader的实例名是空. 这是因为自举classloader不是用Java写的, 所以没有java.lang.ClassLoader的实例供返回.

另外一件引起你兴趣的事情也许是我们如何通过系统类查询路径, 比如System.getProperties().getProperty("sun.boot.class.path"). 在这个例子中, 你也许会想我们可以通过动态地改变路径, 从而强制用一个特定的classloader来载入一个特定的类. 我们可以这样修改来验证一下这个想法:
import javax.naming.InitialContext;
public class WhichClassLoader1 {
//Constructor
WhichClassLoader1( ) {
//do nothing
}

public static void main (String args[] ) throws Exception {
//Retrieve the classpaths
StringBuffer bootstrapClassPath=new StringBuffer(System.getProperties().getProperty("sun.boot.class.path"));
StringBuffer extensionClassPath=new StringBuffer(System.getProperties().getProperty("java.ext.dirs"));
StringBuffer systemClassPath=new StringBuffer(System.getProperties().getProperty("java.class.path"));

//modifying the bootstrapclasspath to include the jar file which contains the InitialContext Class
// This should force the class to be loaded by the bootstrap classloader???????????
String fileSeparator = System.getProperty("file.separator");
String InitialContextJar = "D:"+fileSeparator+"jdk1.2.2"+fileSeparator + "jre"+fileSeparator+"lib"+fileSeparator+"ext"+fileSeparator+"iioprt.jar"
bootstrapClassPath.append(System.getProperty("path.separator")).append(InitialContextJar);
System.setProperty("sun.boot.class.path",bootstrapClassPath.toString());
System.out.println("\nBootstrap classpath=\n"+ bootstrapClassPath + "\n");
System.out.println("Extension classpath="+ extensionClassPath + "\n");
System.out.println("System classpath="+ systemClassPath + "\n" );

//Create new object instances
Object object = new java.lang.Object()
InitialContext initialContext = new InitialContext();
WhichClassLoader1 whichClassLoader1 = new WhichClassLoader1();
System.out.println("\nJava Core file,java.lang.Object, was loaded by: " + object.getClass().getClassLoader());
System.out.println("\nExtension file, Javax.naming.InitialContext, was loaded by: " + initialContext.getClass().getClassLoader());
System.out.println("\nUser file, WhichClassLoader1, was loaded by: " + whichClassLoader1.getClass().getClassLoader() + "\n");
}
}

运行的结果很有趣:
Bootstrap classpath=D:\jdk1.2.2\jre\lib\rt.jar;D:\jdk1.2.2\jre\lib\i18n.jar;D:\jdk1.2.2\jre\classes;D:\jdk1.2.2\jre\lib\ext\iioprt.jar
Extension classpath=D:\jdk1.2.2\jre\lib\ext
System classpath=.
Java Core file,java.lang.Object, was loaded by: null
Extension file, Javax.naming.InitialContext, was loaded by: sun.misc.Launcher$ExtClassLoader@f01b290a
User file, WhichClassLoader1, was loaded by: sun.misc.Launcher$AppClassLoader@f01f290a

和我们期待的不同. 事实是改变这些系统级的属性完全没有效果. 这说明了这些classloader很重要的一个特性: 一旦JVM启动后, 它们的路径是不可更改的, 或者说是静态的.

让我们回到这个例子原来的目的. 到目前为止, 看上去Java的classloader正如我们被告知的那样工作: 自举classloader载入Java核心类, extension classloader载入Java扩展包, 而application classloader载入用户类. 然而, classloader在本质上又是分层的, 遵循"委托给父类"的模式. 这意味着除了自举classloader, 每一个classloader都有一个父classloader, 当一个classloader试图载入一个类时, 它首先将这一责任委托给它的父classloader. 这个模式是这样工作的: 一个classloader将尝试载入一个类, 当且仅当这个类在这个classloader所属的层次结构中还未被载入, 并且这个classloader的父classloader找不到这个类.

自顶向下地遍历classloader层次, 我们看到首先是自举classloader, 然后是extension classloader和application classloader. 如果你将classloader层次看作稀疏树结构, 那么自举classloader是根节点, application classloader是叶节点.

这种层次结构可以写程序演示如下:
public class ShowHierarchy{
public static void main (String args[] ) throws Exception {
System.out.println("The System ClassLoader is: " + ClassLoader.getSystemClassLoader().getClass());
System.out.println("The System ClassLoader's Parent is: " + ClassLoader.getSystemClassLoader().getParent().getClass());
System.out.println("The System ClassLoader's Parent's Parent is: " +ClassLoader.getSystemClassLoader().getParent().getParent());
}
}
结果是:
D:\WebSphere\Documents\Classpaths\src>java .classpath . ShowHierarchy
The System ClassLoader is: class sun.misc.Launcher$AppClassLoader
The System ClassLoader's Parent is: class sun.misc.Launcher$ExtClassLoader

为了演示"委托给父类"的关系, 让我们在每个classloader的路径上放一份包含InitialContext.class的JAR文件, 然后执行第一个例子. 记住一旦JVM运行以后, 它的classloader路径是不可变的, 所以我们需要通过Java开关-classpath和-bootclasspath在运行之前改变它们.
D:\Classpath_Project\src\java -classpath D:\jdk1.2.2\jre\lib\ext\jndi.jar;. -XbootclasspathD:\jdk1.2.2\jre\lib\rt.jar;D:\jdk1.2.2\jre\lib\i18n.jar;D:\jdk1.2.2\jre\classes;D:\jdk1.2.2\jre\lib\ext\jndi.jar WhichClassLoader
Bootstrap classpath= D:\jdk1.2.2\jre\lib\rt.jar;D:\jdk1.2.2\jre\lib\i18n.jar;D:\jdk1.2.2\jre\classes;D:\jdk1.2.2\jre\lib\ext\jndi.jar
Extension classpath= D:\jdk1.2.2\jre\lib\ext
System classpath= D:\jdk1.2.2\jre\lib\ext\jndi.jar;.
Java Core file,java.lang.Object, was loaded by: null
Extension file, javax.naming.InitialContext, was loaded by: null
User file, WhichClassLoader, was loaded by: sun.misc.Launcher$AppClassLoader@f01a2987

正如你所见, InitialContext类如我们预见的那样是由自举classloader载入的, 你能理解这是为什么吗? 让我们详细描述一下发生的事吧:
1. WhichClassLoader类创建一个InitialContext类的新实例: javax.naming.InitialContext initialContext = new javax.naming.InitialContext();
2. Application classloader查看InitialContext类是否已经在它所属的classloader层次结构中被载入, 也就是说被自举classloader, 被extension classloader或者被它自己所载入.
3. 结果是否定的.
4. 遵循"委托给父类"的模式, application classloader将载入的任务委托给extension classloader.
5. 再一次遵循"委托给父类"的模式, extension classloader将载入的任务委托给自举classloader.
6. 自举classloader没有父classloader, 尝试载入类.
7. 自举classloader成功地载入了InitialContext类.
8. InitialContext的新实例被创建并返回给WhichClassloader.

这一场景需要进一步的解释. 看上去似乎很明显的是, application classloader是第一个收到创建InitialContext新实例的classloader, 因为它位于classloader层次结构的最底层. 但并不总是这样. 随着Java 2的出现, 类将通过调用者的classloader被载入, 这可能是, 但也可能不是位于层次结构最底层的classloader. 在我们的例子中, 调用者是WhichClassLoader, 我们知道它是由application classloader载入的.

Java 2中的classloader遵循"委托给父类, 并组织成层次结构"的模式, 这允许你做一些有趣的事情. 但当类不是由classloader层次结构的叶节点载入时, 也可能引起问题. 比如, 让我们修改一下WhichClassLoader, 重命名为WhichClassLoader2, 并创建两个新类WhichClassLoader3和WhichClassLoader4.

文件WhichClassLoader2.java:
public class WhichClassLoader2 {
//Constructor
WhichClassLoader2() {
//do nothing
}
public static void main (String args[] ) throws Exception {
//Retrieve the classpaths
StringBuffer bootstrapClassPath=new StringBuffer(System.getProperties().getProperty("sun.boot.class.path"));
StringBuffer extensionClassPath=new StringBuffer(System.getProperties().getProperty("java.ext.dirs"));
StringBuffer systemClassPath=new StringBuffer(System.getProperties().getProperty("java.class.path"));

System.out.println("\nBootstrap classpath="+ bootstrapClassPath + "\n");
System.out.println("Extension classpath="+ extensionClassPath + "\n");
System.out.println("System classpath="+ systemClassPath + "\n" );
//Create new object instances
java.lang.Object object = new java.lang.Object();
javax.naming.InitialContext initialContext = new javax.naming.InitialContext();
WhichClassLoader2 whichClassLoader2 = new WhichClassLoader2();
WhichClassLoader3 whichClassLoader3 = new WhichClassLoader3();
System.out.println("\nJava Core file,java.lang.Object, was loaded by: " + object.getClass().getClassLoader());
System.out.println("\nExtension file, javax.naming.InitialContext, was loaded by: " + initialContext.getClass().getClassLoader());
System.out.println("\nUser file, WhichClassLoader2, was loaded by: " + whichClassLoader2.getClass().getClassLoader() + "\n");
System.out.println("\nUser file, WhichClassLoader3, was loaded by: " + whichClassLoader3.getClass().getClassLoader() + "\n");
whichClassLoader3.getTheClass();
}
}

文件WhichClassloader3.java:
public class WhichClassLoader3 {
public WhichClassLoader3 () {
}
public void getTheClass() {
WhichClassLoader4 wcl4 = new WhichClassLoader4 ();
System.out.println("WhichClassLoader4 was loaded by " + wcl4.getClass().getClassLoader());
}
}

文件WhichClassloader4.java:
public class WhichClassLoader4 {
WhichClassLoader4 () {
}
}

我们将WhichClassLoader2和WhichClassLoader4放在当前目录下, 这样它们仅仅存在于application classloader的路径之中. 接下来我们将WhichClassLoader3放在extension classloader的路径中.
D:\Classpath_Project\src>ls
WhichClassLoader2.class
WhichClassLoader2.java
WhichClassLoader1.java
WhichClassLoader4.class
WhichClassLoader4.java
D:\Classpath_Project\src>ls D:\jdk1.2.2\jre\lib\ext\
WhichClassLoader3.jar
cosnaming.jar
iiimp.jar
iioprt.jar
jndi.jar
providerutil.jar
rmiorb.jar
rmiregistry.jar

我们来看一下运行WhichClassLoader2会发生什么.
D:\Classpath_Project\src>java -classpath . WhichClassLoader2
Bootstrap classpath=D:\jdk1.2.2\jre\lib\rt.jar;D:\jdk1.2.2\jre\lib\i18n.jar;D:\jdk1.2.2\jre\classes
Extension classpath=D:\jdk1.2.2\jre\lib\ext
System classpath=.
Java Core file,java.lang.Object, was loaded by: null
Extension file, javax.naming.InitialContext, was loaded by: sun.misc.Launcher$ExtClassLoader@f01b3492
User file, WhichClassLoader2, was loaded by: sun.misc.Launcher$AppClassLoader@f01f3492
User file, WhichClassLoader3, was loaded by: sun.misc.Launcher$ExtClassLoader@f01b3492
java.lang.NoClassDefFoundError: WhichClassLoader4
at WhichClassLoader3.getTheClass(WhichClassLoader3.java:8)
at WhichClassLoaderTest.main(WhichClassLoaderTest.java:38)
Exception in thread "main"

你也许会问既然WhichClassLoader4明明在application classloader的路径中, 载入WhichClassLoader4为什么会失败. 这种失败不是因为classloader找不到类, 而是因为WhichClassLoader3的classloader找不到类. 为了更好地理解发生的事情, 让我们从载入WhichClassLoader3的classloader(也就是extension classloader)的角度来看一下.

首先, 要求创建一个WhichClassLoader4的实例, 于是遵循Java 2的"委托给父类"模式, extension classloader先查看这个类是否已经被它所属的classloader层次结构所载入, 这种尝试将失败. 于是它请求它的父classloader, 就是自举classloader, 来载入这个类. 这一请求将返回一个NoClassDefFound异常. Extension classloader将捕获这个异常并求助于它的最后选择: 它试图自己载入这个类. 搜寻了自己的搜索路径之后, extension classloader找不到WhichClassLoader4类的定义, 所以它重新抛出NoClassDefFoundError异常. 此时不再有classloader来捕获这个异常, 于是在屏幕上打印异常, 程序退出.

这就引起一个问题, 既然有可能发生这样的问题, 为什么要自寻烦恼地采用多个classloader呢? 为什么不回到Java 2之前的框架, 在那样的框架之下, 只有一个系统classloader来载入所有东西? 与引发的问题相比, classloader层次结构和"委托给父类"模式带来的好处是否值得呢? 简而言之, 是值得的:
1. 保护. Java预定义了自举classloader和extension classloader, 缺省情况下, 只把application classloader的定义留给了用户. 因为"委托给父类"的模式, 用户定义的任何类都不可能覆盖Java扩展类和核心类.
2. 用户友好. 在同样的方式下, 因为自举classloader和extension classloader是预定义的, 你不再需要把classes.zip文件放在-classpath属性中.
3. 隔离. 也许这是新的classloader定义做提供的最重要的好处. 到目前为止, 这看上去不是那么有用, 但我们还没有谈到Java允许你定义自己的classloader. 使用继承, 可以形成自己的classloader层次树.

理解这个模型所提供的益处的最佳方式是和操作系统的名字空间作一个类比. 让我们假设我们正在开发一个叫MyApp的应用, 这个应用有三个版本: 一个是实际用的, 一个处于Beta测试中, 一个还在开发中. 我们想要在任何时候运行这些应用程序版本中的任何一个, 于是我们将它们放在分开的目录结构中:
/usr/MyApp
/usr/MyApp/v1
/usr/MyApp/v2
/usr/MyApp/v3

所有版本公用的文件放在MyApp目录下, 版本特定的文件分别放在v1, v2和v3目录下. 使用这种方法, 我们能很有效地使用硬盘空间, 同时又能管理版本的差异. 同样的想法对classloader也适用. 核心类是最通用的, 就放在层次结构的根节点上, 而那些特定"版本"的类放在叶节点上.

译者注: 比较了WebSphere, WebLogic和JBoss之后, 发现它们的classloader设计都不一样. 移植代码时, 这可能引发一些问题.

2.Re:Java中的Classloader [Re: yjwang] Copy to clipboard
Posted by: suziniren
Posted on: 2003-03-16 20:35

我是新手

3.Re:Java中的Classloader [Re: yjwang] Copy to clipboard
Posted by: javait
Posted on: 2003-03-16 22:52

Thanks, yjwang.

Sorry for using English here. But you all know what I am talking about.Smile

The class loading is an essential topic of Java programming language. It is the key to design and understand good Java applications. For example, Singleton pattern may not guarantee the single instance in a JVM. A surprise?

The class loading is also the key to understand the packaging of EJB applications and the run-time behavior of packaged EJB applications deployed on application servers, as well as the different packaging requirements on different application servers. Let's compare WebLogic v7.0 and WebSphere v5.0.

In WebLogic v7.0, every EJB application receives its own classloader hierarchy; the parent of this hierarchy is the system classloader. The hierarchy is automatically created when an application is deployed. The application classloader in the hierarchy loads any EJB JAR files in the application. A child classloader is created for each Web application WAR file. Note that this child Web application classloader contains all classes for the Web application except for the servlet implementation classes and JSPs. Each servlet implementation class and JSP class obtains its own classloader, which is a child of the Web application classloader. This allows servlets and JSPs to be individually reloaded.

The classloading design in WebLogic v7.0 isolates applications so that application A cannot see the classes of application B. Application classloaders can only see their parent classloader, the system classloader. This allows WebLogic Server to host multiple isolated applications within the same JVM.

In WebLogic, there is a PreferWebInfClasses setting which subverts the standard Java classloader delegation model when it is set to true. In my opinion, this is a poor design because the same class can be loaded into both the Web application and system classloader, which in turn makes it very easy to obtain a ClassCastException. Users must be very careful not to mix instances of classes loaded from different classloaders.

The class loading in WebSphere v5.0 is more sophisticated than in WebLogic v7.0. The overall classloader hierarchy is similar to WebLogic except that there is a WebSphere-specific extensions classloader sits in between the system classloader and application classloader. This WebSphere extensions classloader loads the WebSphere run time and J2EE classes that are required at run time from "ws.ext.dirs".

Unlike WebLogic which does not provide much flexibility of EJB packaging due to its classloading design, WebSphere uses Classloader Isolation Policies to specify the number and function of the application classloaders, therefore to control the isolation of applications and modules. This enables different application packaging schemes to run on an application server. There are two classloader policies.

The Application Classloader Policy specifies how an application classloader can be shared by multiple applications. When set to SINGLE, applications are not isolated. When set to MULTIPLE, applications are isolated from each other because each of them has its own application classloader.

The WAR Classloader Policy controls the isolation of Web modules. If this policy is set to APPLICATION, then the Web module contents also are loaded by the application classloader. If the policy is set to MODULE, then each web module receives its own classloader whose parent is the application classloader.

As a result of the classloading design, WebSphere can offer more flexible EJB packaging options, and better run-time configuration. For example, in WebLogic, you are forced to edit its script that runs WebLogic Server when you just want to share utility classes among your EJB applications.

Cheers SmileSmile

-javait

4.Re:Java中的Classloader [Re: javait] Copy to clipboard
Posted by: yjwang
Posted on: 2003-03-17 14:16

Thanks. It's clear and valuable. Smile

5.Re:Java中的Classloader [Re: yjwang] Copy to clipboard
Posted by: tier3
Posted on: 2003-04-04 16:43

有个问题我不太明白,请高人指教。:)

在yjwang的贴子中部谈到这点:
2. Application classloader查看InitialContext类是否已经在它所属的classloader层次结构中被载入, 也就是说被自举classloader, 被extension classloader或者被它自己所载入.
3. 结果是否定的
^^^^^^^^^^,为什么结果是否定的呢,我们在设classpath里不是把jndi.jar放到系统classpath了吗?
"System classpath= D:\jdk1.2.2\jre\lib\ext\jndi.jar;."
我基本上明白classloader的机制,但这个细节没想清楚,谢谢。:)

另外,我想请教javait关于weblogic一个war如何找一个jar的问题;
我在一个war里要调用ejb, 该ejb已经单独地被成功部署到weblogic了,然后
我又单独地部署war文件,他们都在D:\bea702\user_projects\mydomain\applications目录下面,但是运行war时,总是出现找不到jar里的类的错误,
不知道是不是weblogic有个属性让war也可以找到jar里的类库?
谢谢。

6.Re:Java中的Classloader [Re: yjwang] Copy to clipboard
Posted by: javait
Posted on: 2003-04-05 01:10

Hi tier3

To let the war to find the jar, you need the ear. Smile

And this has to comply with the J2EE standard. In the ear, there's a description to tell the relationships between war and jar.

欢迎常来J2EE 提问 Smile

谢谢
-JAVAIT

7.Re:Java中的Classloader [Re: yjwang] Copy to clipboard
Posted by: tier3
Posted on: 2003-04-07 08:56

我知道EAR文件如何生成,但是在开发阶段可能war的数量和jar的数量是未定
的。我的意思是在开发阶段有没有设classpath的解决方案,等最后产品结束以
后再根据需要打包成ear.
您的意思是ear是唯一的solution吗?

非常感谢。

8.Re:Java中的Classloader [Re: yjwang] Copy to clipboard
Posted by: javait
Posted on: 2003-04-07 11:37

Hi tir3,

在开发阶段可能war的数量和jar的数量是定的。不定的是它们的内容,例如有几个 session bean和 entity bean。只有在有ear以后,你的war才能去call EJB.
所以,答案是YES。

希望能对你有所帮助。:)

JAVAIT

9.Re:Java中的Classloader [Re: yjwang] Copy to clipboard
Posted by: tier3
Posted on: 2003-04-07 14:21

解决了。:)
以下是我的application.xml里的部分内容,
<module>
<ejb>customermanager-ejb.jar</ejb>
</module>
<module>
<web>
<web-uri>customermanager.war</web-uri>
<context-root>customermanager</context-root>
</web>
</module>

以前用jboss,直接copy到deploy目录即可,现在换成weblogic,只能打包成ear了。。。

我们的开发是分模块的,不知道客户要哪几个模块,所以我说war和jar的数量是未定的,不过现在直接打成ear也可以,只不过没想到app server之间差别太大,是不是J2ee1.4也应该规定一下deploy的方法。。。
多谢各位朋友。:)

10.Re:Java中的Classloader [Re: yjwang] Copy to clipboard
Posted by: javait
Posted on: 2003-04-07 23:03

恭喜你。
不用谢。:)

-JAVAIT

11.Re:Java中的Classloader [Re: tier3] Copy to clipboard
Posted by: floater
Posted on: 2003-04-08 01:31

tier3 wrote:
解决了。:)
以下是我的application.xml里的部分内容,
<module>
<ejb>customermanager-ejb.jar</ejb>
</module>
<module>
<web>
<web-uri>customermanager.war</web-uri>
<context-root>customermanager</context-root>
</web>
</module>

以前用jboss,直接copy到deploy目录即可,现在换成weblogic,只能打包成ear了。。。

我们的开发是分模块的,不知道客户要哪几个模块,所以我说war和jar的数量是未定的,不过现在直接打成ear也可以,只不过没想到app server之间差别太大,是不是J2ee1.4也应该规定一下deploy的方法。。。
多谢各位朋友。:)

In JBoss, you are using the auto deployment feature to drop your code in there. I think weblogic has the same feature too. This auto deployment feature is not part of J2EE, it's up to the vendor of the server. But J2EE(ear fiel) should be applicable to both JBoss and weblogic.


   Powered by Jute Powerful Forum® Version Jute 1.5.6 Ent
Copyright © 2002-2021 Cjsdn Team. All Righits Reserved. 闽ICP备05005120号-1
客服电话 18559299278    客服信箱 714923@qq.com    客服QQ 714923