现在的位置: 首页 > 综合 > 正文

Java和JavaEE类加载机制

2013年10月06日 ⁄ 综合 ⁄ 共 3104字 ⁄ 字号 评论关闭

JavaJavaEE类加载机制简单介绍

类加载是java 语言提供的最强大的机制之一。尽管类加载并不是讨论的热点话题,但所有的编程人员都应该了解其工作机制,明白如何做才能让其满足我们的需要。这能有效节省我们的编码时间,从不断调试ClassNotFoundException, ClassCastException 的工作中解脱出来。

 

所有的java类都是继承了Object这个类,在Object这个类中有一个方法:getClass(),这个方法返回的是一个Class类对象。一旦一个类被载入JVM中,同一个类就不会被再次载入了。这里存在一个问题就是什么是"同一个类"?一个对象有一个具体的标识,同理,载入JVM的类也应该有一个具体的标识。JAVA中,一个类用其完全匹配类名 (fully qualified class name)作为标识,这里指的完全匹配类名是包名和类名。不过在JVM中一个类是用其全名再附加上一个加载类ClassLoader的实例作为唯一标识。因此,如果一个名为Pg的包中,有一个名为Cl的类,被类加载器KlassLoader的一个实例对象kl1加载,生成Cl的对象,即C1.class (这里指类,而非对象),在JVM中表示为(Cl, Pg, kl1)。这意味着两个类加载器的实例(Cl, Pg, kl1) (Cl, Pg, kl2)是不同的,被它们所加载的类也因此完全不同,互不兼容的(项目中经常出现的ClassNotFound的错误,很有可能就是这种不兼容引起的)。

java中每个类都是由某个类加载器的实体来载入的,因此在Class类的实体中,都会有字段记录载入它的类加载器的实体(当为null时,其实是指 Bootstrap ClassLoader)。 在java类加载器中除了引导类加载器(既Bootstrap ClassLoader),所有的类加载器都有一个父类加载器(因为他们本身自己就是java)

Java中类加载器的委托模型java中的类加载器有一个继承体系,子加载器在加载类的时候首先委托父加载器来加载,以此类推,如果所有的父加载器都不能加载,那么子加载器再加载,此时如果子加载器没有发现类文件,则抛出java.lang.ClassNotFoundException.

但是在JavaEE应用中,java默认的委托模型将会被禁用,此时加载类的时候,首先是子加载器加载类,如果子加载器找不到相应的类文件,那么委托给父加载器来加载。

子加载器可以看到父加载器加载的类,但是父加载器看不到子加载器加载的类。同级的类加载器是不能看到对方加载的类的。(假如ear包中包括了很多个war包,这些war包中的类是不能相互引用的。)

 

 

1、Java类加载器体系:

Java的类加载器分为以下几种:

1Bootstrap ClassLoader(启动类加载器),用C++实现,一切的开始,是所有类加载器的最终父加载器。负责将一些关键的Java类,如java.lang.Object和其他一些运行时代码先加载进内存中。

主要负责jdk_home/lib目录下的核心 api -Xbootclasspath 选项指定的jar包装入工作。

2ExtClassLoader(扩展类加载器),用java实现,是Launcher.java的内部类,编译后的名字为:Launcher$ ExtClassLoader.class 。此类由Bootstrap ClassLoader加载,但由于Bootstrap ClassLoader已经脱离了java体系(c++),所以Launcher$ExtClassLoader.classParent(父加载器) 被设置为null;它用于装载Java运行环境扩展包(jre/lib/ext)中的类,而且一旦建立其加载的路径将不再改变。

主要负责jdk_home/lib/ext目录下的jar包或 -Djava.ext.dirs 指定目录下的jar包装入工作。

3AppClassLoader(系统类加载器),java实现,也是Launcher.java的内部类,编译后的名字为:Launcher$ AppClassLoader.class AppClassLoader是当Bootstrap ClassLoader加载完ExtClassLoader后,再被Bootstrap ClassLoader加载。所以ExtClassLoaderAppClassLoader都是被Bootstrap ClassLoader加载,但AppClassLoaderParent被设置为ExtClassLoader。可Parent和由哪个类加载器来加载不一定是对应的

主要负责java -classpath/-Djava.class.path所指的目录下的类与jar包装入工作。(AppClassLoader才是加载Classpath的)

4ClassLoader:一般我们自定义的类加载器从java.lang.ClassLoader类继承而来。比如:URLClassloader ClassLoader的一个子类,而URLClassloader也是ExtClassLoaderAppClassLoader的父类(注意不是父加载器)。 URLClassloader的父加载器是AppClassLoader

在程序运行期间, 通过java.lang.ClassLoader的子类动态加载class文件, 体现java动态实时类装入特性。

 

所有的类加载器,除了引导类加载器,都有一个父类加载器,而且,它们都是java.lang.ClassLoader类型的。在Java中,每个类都会被java.lang.ClassLoader的一个实例加载。开发者可以自由的创建ClassLoader的子类,添加自己功能的类加载器。

 

自定义类加载器加载一个类的步骤:

 


2、JavaEE类加载器

JavaEE中的类加载器是在java的类加载器的基础上实现的,如下图:

 

从图中可以看书,application server类加载器是EJB类加载器的父加载器,EJB包的类加载器是war包的父加载器。

       EJB ClassLoader的加载范围仅限于jar或者ear范围之内。war包类加载器在加载类时,首先在自己对应的路径中查找类(WEB-INF/classesWEB-INF/lib,以及lib目录下的jar文件META-INF/manifest.mfclasspath指定的jar),如果找不到才会委托给父加载器(ejb包类加载器)加载,以此类推,如果所有的父加载器都不能加载,那么就抛出java.lang.ClassNotFoundException。(Java的默认委托模型在JavaEE应用的类加载器模型中不再使用)

 

针对JBoss服务器(其他服务器可能不一样),ear包的类加载有以下几点要注意的:

1、  ear包中的所有ejb模块都共享同一个ClassLoader

2、  ear包中的每个web模块都会有对应的一个ClassLoader

3、  ejb模块中类对web模块是可见的,反之则不成立;不同的web模块之间看不到对方ClassLoader已经加载的类。

4、  如果我们把一个类设计为单例的,要确保web模块和ejb模块中用到的单例类是同一个类加载器加载的,否则的话web模块和ejb模块的实例是不一样的。

最后,一般我们在打ear包的时候,会把web模块和ejb模块都用到的类放到ear包的lib目录下,这样确保公共类是同一个类加载器加载的。

抱歉!评论已关闭.