1. ClassLoader
类加载器(class loader)用来加载 Java 类到 Java 虚拟机中。Java 源程序(.java 文件)在经过 Java 编译器编译之后就被转换成 Java 字节代码(.class 文件)。类加载器负责读取 Java 字节代码,并转换成 java.lang.Class 类的一个实例。
2. ClassLoader Hierarchy
JVM在加载类时,使用的是双亲委托模式(delegation model),也就是说除了Bootstrap ClassLoader之外,每个ClassLoader都有一个Parent ClassLoader。ClassLoader是按需进行加载class文件。当ClassLoader试图加载一个类时,首先检查本地缓冲,查看类是否已被加载,如果类没有被加载,尝试委托给父ClassLoader进行加载,如果父ClassLoader加载失败,才会由该ClassLoader进行加载,从而避免了重复加载的问题。一下为类装载器层次图:
Bootstrap ClassLoader:负责加载java_home/lib目录下的核心类或- Xbootclasspath指定目录下的类。
Extension ClassLoader:负责加载java_home/lib/ext目录下的扩展类或 -Djava.ext.dirs 指定目录下的类。
System ClassLoader:负责加载-classpath/-Djava.class.path所指的目录下的类。
如果类App1在本地缓冲中没有class文件(没有被加载),那么它会自底向上依次查找是否已经加载了类,如果已经加载,则直接返回该类实例的引用。如果BootstrapClassLoader也未成功加载该类,那么会抛出异常,然后自顶向下依次尝试加载,如果到App1 ClassLoader还没有加载成功,那么会抛出ClassNotFoundException异常给调用者。
public static void main(String[] args) {
ClassLoader cl = ClassLoader.getSystemClassLoader();
while(cl != null){
System.out.println(cl);
System.out.println("parent class loader: " + cl.getParent());
cl = cl.getParent();
}
}
sun.misc.Launcher$AppClassLoader@19821f
parent class loader: sun.misc.Launcher$ExtClassLoader@addbf1
sun.misc.Launcher$ExtClassLoader@addbf1
parent class loader: null
我们看到,当前系统类装载器为AppClassLoader,AppClassLoader的父类装载器是ExtClassLoader,ExtClassLoader的父装载器为null,表示为BootstrapClassLoader。BootstrapClassLoader由JVM采用本地代码实现,因此没有对应的Java类,所以ExtClassLoader的getParent()返回null。
ClassLoader的职责之一是保护系统名字空间。以下为ClassLoader类部分代码:
private ProtectionDomain preDefineClass(String name,
ProtectionDomain protectionDomain)
{
if (!checkName(name))
throw new NoClassDefFoundError("IllegalName: " + name);
if ((name != null) && name.startsWith("java.")) {
throw new SecurityException("Prohibited package name: " +
name.substring(0, name.lastIndexOf('.')));
}
if (protectionDomain == null) {
protectionDomain = getDefaultDomain();
}
if (name != null)
checkCerts(name, protectionDomain.getCodeSource());
return protectionDomain;
}
那么,当我们定义如下类Foo,虽然能够通过编译,但是会报java.lang.SecurityException: Prohibited package name: java.lang异常,因为我们试图将Foo类写入到java.lang包下。
package java.lang;
public class Foo {
public static void main(String args[]) throws Exception {
Foo f = new Foo();
System.out.println(f.toString());
}
}
3. 定制ClassLoader
Java自带的ClassLoader类的定义为:
public abstract class ClassLoader{
}
启动类加载器是JVM通过调用ClassLoader.loadClass()方法。
public Class<?> loadClass(String name) throws ClassNotFoundException {
return loadClass(name, false);
}
protected synchronized Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
// First, check if the class has already been loaded
Class c = findLoadedClass(name);
if (c == null) {
try {
if (parent != null) {
c = parent.loadClass(name, false);
} else {
c = findBootstrapClass0(name);
}
} catch (ClassNotFoundException e) {
// If still not found, then invoke findClass in order
// to find the class.
c = findClass(name);
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
throw new ClassNotFoundException(name);
}
loadClass(String name, boolean resolve)方法中的resolve如果为true,表示分析这个Class对象,包括检查Class Loader是否已经初始化等。loadClass(String name) 在加载类之后不会对该类进行初始化,直到第一次使用该类时,才会对该类进行初始化。
那么,我们在定制ClassLoader的时候,通常只需要覆写findClass(String name)方法。在findClass(String name)方法内,我们可以通过文件、网络(URL)等形式获取字节码。以下为获取字节码的方法:
public InputStream getResourceAsStream(String name);
public URL getResource(String name);
public InputStream getResourceAsStream(String name);
public Enumeration<URL> getResources(String name) throws IOException;
在取得字节码后,需要调用defineClass()方法将字节数组转换成Class对象,该方法签名如下:
protected final Class<?> defineClass(String name, byte[] b, int off, int len,
ProtectionDomain protectionDomain)
throws ClassFormatError
对于相同的类,JVM最多会载入一次。如果同一个class文件被不同的ClassLoader载入(定义),那么载入后的两个类是完全不同的。
public class Foo{
//
private static final AtomicInteger COUNTER = new AtomicInteger(0);
public Foo() {
System.out.println("counter: " + COUNTER.incrementAndGet());
}
public static void main(String args[]) throws Exception {
URL urls[] = new URL[]{new URL("file:/c:/")};
URLClassLoader ucl1 = new URLClassLoader(urls);
URLClassLoader ucl2 = new URLClassLoader(urls);
Class<?> c1 = ucl1.loadClass("Foo");
Class<?> c2 = ucl2.loadClass("Foo");
System.out.println(c1 == c2);
c1.newInstance();
c2.newInstance();
}
}
以上程序需要保证Foo.class文件不在classpath路径下。从而使AppClassLoader无法加载Foo.class。
输出结果:
false
counter: 1
counter: 1
4. Web应用的ClassLoader
绝大多数的EJB容器,Servlet容器等都会提供定制的ClassLoader,来实现特定的功能。但是通常情况下,所有的servlet和filter使用一个ClassLoader。每个jsp都使用一个独立的ClassLoader。
5. 隐式(implicit)和显示(explicit)的加载
隐式加载:我们使用new关键字实例化一个类,就是隐身的加载了类。
显示加载分为两种:
java.lang.Class的forName()方法;
java.lang.ClassLoader的loadClass()方法。
Class.forName()方法有两个重载的版本:
public static Class<?> forName(String className)
throws ClassNotFoundException {
return forName0(className, true, ClassLoader.getCallerClassLoader());
}
public static Class<?> forName(String name, boolean initialize,
ClassLoader loader)
throws ClassNotFoundException
可以看出,forName(String className)默认以true和ClassLoader.getCallerClassLoader()调用了三参数的重载方法。ClassLoader.getCallerClassLoader()表示以caller class loader加载类,并会初始化类(即静态变量会被初始化,静态初始化块中的代码也会被执行)。如果以false和ClassLoader.getCallerClassLoader()调用三参数的重载方法,表示加载后的类不会被初始化。
ClassLoader.loadClass()方法在类加载后,也同样不会初始化类。
6. 两个异常(exception)
NoClassDefFoundError: 当java源文件已编译成.class文件,但是ClassLoader在运行期间搜寻路径load某个类时,没有找到.class文件则抛出这个异常。
ClassNotFoundException: 试图通过一个String变量来创建一个Class类时不成功则抛出这个异常
分享到:
相关推荐
自己收集的java class loader相关的一些网络资源文档, 希望对大家有所帮助
Java Class Loader总结
java 类加载器 class loaderjava 类加载器 class loaderjava 类加载器 class loaderjava 类加载器 class loaderjava 类加载器 class loaderjava 类加载器 class loaderjava 类加载器 class loader
深入 Java 的Class Loader(类加载器)
Dynamic class loading in the Java Virtual Machine
The Class Loader Architecture The Java Class File The Java API The Java Programming Language Architectural Tradeoffs Future Trends On the CD-ROM The Resources Page 2 Platform independence Why ...
loader Byte code Verifier Javac Hello.java Netwo Hello class Interpreter code Runtime generator/ Hardware 1:编写代码 首先把我们想要计算机做的事情,通过Java表达出来,写成Java文件,这个过程就是 编写代码的...
Class Loader 是一个可视化的基本 .net 2.0 软件。 使用类加载器,您可以在没有命令提示符的情况下执行 .class 文件。 只需打开类文件!
js 类加载器(Java) 一种免费软件工具,用于捆绑和提供具有内置依赖项检测功能的大型 Javascript 代码库。 请参阅网站 和 github 仓库: : 了解更多信息。
6.2Security Checks in the Class Loader 6.3The Byte Code Verification Process 6.3.1The Byte Code Verifier 6.4Security in the Java Networking Package 6.5Summary 7. Multithreading 7.1...
主要介绍了Java中的类加载器,是Java入门学习中的基础知识,需要的朋友可以参考下
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:421) at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:412) at org.eclipse.osgi.internal....
你可以创建个新的class loader,然后用loadClass加载,再newInstance;原来加载的classloader是不能重新加载的;这算是一个典型的容器思路。 《深入理解java虚拟机》 2、对java“书写一次,到处运行”(Write once, ...
at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:421) at org.eclipse.osgi.internal.loader.BundleLoader.findClass(BundleLoader.java:412) at org.eclipse.osgi.internal....
描述Java虚拟机规范中关于内存管理的部分 主要介绍Runtime Data Area,包括Java Stack,Native Method Stack, ...还简要介绍了Runtime Data Area周边的模块,包括Class Loader,Execution Engine,Native Interface
Java虚拟机的基本结构图:Java虚拟机包含一个类装载器(class loader),可以从程序和API中装载class文件,其中API只有程序执行时需要的那
With OSGi-style classloading getting more and more traction, and a number of new Java modules/classloading specifications on the horizon, it was high time we revamped our classloading layer in order ...
The class definitions are basically ports to Java of the original header files in C/C++, and I deliberately decided to keep as much of the original syntax as possible. For example, here is a method ...
at org.apache.catalina.loader.WebappClassLoader.findClass(WebappClassLoader.java:872) at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1325) at org.apache.catalina...
类加载器(class loader)是 Java™ 中的一个很重要的概念。类加载器负责加载 Java 类的字节代码到 Java 虚拟机中。本文首先详细介绍了 Java 类加载器的基本概念,包括代理模式、加载类的具体过程和线程上下文类加载...