作用:加载.class文件到jvm中
触发节点:
- 执行new操作
- Class.forName
- classloader.loadclass
Java内置ClassLoader
BootstrapClassloader
最顶层的类加载器,主要用来加载Java核心类,如rt.jar、resources.jar、charsets.jar等。它不是 java.lang.ClassLoader的子类,而是由JVM自身实现的该类c 语言实现,Java程序访问不到该加载器。
URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
for (int i = 0; i < urls.length; i++) {
System.out.println(urls[i].toExternalForm());
}
ExtClassloader
扩展类加载器,主要负责加载Java的扩展类库,默认加载JAVA_HOME/jre/lib/ext/目下的所有jar包或者由java.ext.dirs系统属性指定的jar包。放入这个目录下的jar包对所有AppClassloader都是可见的(后面会知道ExtClassloader是AppClassloader的父加载器)。那么ext都是在那些地方加载类内:
System.getProperty("java.ext.dirs")
AppClassloader
它负责在JVM启动时,加载来自 CLASSPATH下的Jar包。调用ClassLoader.getSystemClassLoader()可以获取该类加载器。如果没有特别指定,则用户自定义的任何类加载器都将该类加载器作为它的父加载器,这点通过ClassLoader的无参构造函数可以知道如下:
protected ClassLoader() {
this(checkCreateClassLoader(), getSystemClassLoader());
}
执行以下代码即可获得classpath加载路径:
System.out.println(System.getProperty("java.class.path"));
内置Classloader关系
一般我们都认为ExtClassloader的父类加载器是BootStarpClassloader,但是其实他们之间根本是没有父子关系的,只是在ExtClassloader找不到要加载类时候会去委托BootStrap加载器去加载。
通过如下代码可以知道父加载器为null
ClassLoader.getSystemClassLoader().getParent().getParent()
Classloader原理
Java类加载器使用的是委托机制,也就是子类加载器在加载一个类时候会让父类来加载,保证了安全。例如,String已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String。
class ClassLoader {
// name是要加载的类全名
protected Class<?> loadClass(String name, boolean resolve) {
synchronized (getClassLoadingLock(name)) {
Class<?> c = findLoadedClass(name);
if (c == null) { // 如果该类已经被加载就直接return
if (parent != null) {
c = parent.loadClass(name, false); // 寻找父classLoader加载
} else {
c = findBootstrapClassOrNull(name); // 没有父classLoader就委派给bootstrap类加载加载
}
}
if (c == null) {
// 如果所有父类加载器都无法加载,再通过当前加载器定义的findClass方法进行加载。
long t1 = System.nanoTime();
c = findClass(name);
// this is the defining class loader; record the stats
sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);
sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);
sun.misc.PerfCounter.getFindClasses().increment();
}
return c;
}
}
}
从上面源码知道要想修改类加载委托机制,实现自己的载入策略 可以通过覆盖ClassLoader的findClass方法或者覆盖loadClass方法来实现。
JVM初始化类加载器
class Launcher {
public Launcher() { // 构造方法
localExtClassLoader = ExtClassLoader.getExtClassLoader(); // 创建ExtClassLoader
//然后以ExtClassloader作为父加载器创建了AppClassLoader
this.loader = AppClassLoader.getAppClassLoader(localExtClassLoader);
// 默认的线程上下文类加载器是AppClassLoader
Thread.currentThread().setContextClassLoader(this.loader);
}
private static File[] getExtDirs() {
// ExtClassloader要加载的jar包路径
String str = System.getProperty("java.ext.dirs");
File[] arrayOfFile;
if (str != null)
{
StringTokenizer localStringTokenizer = new StringTokenizer(str, File.pathSeparator);
int i = localStringTokenizer.countTokens();
arrayOfFile = new File[i];
for (int j = 0; j < i; j++) {
arrayOfFile[j] = new File(localStringTokenizer.nextToken());
}
}
else
{
arrayOfFile = new File[0];
}
return arrayOfFile;
}
public static ClassLoader getAppClassLoader(final ClassLoader paramClassLoader)
throws IOException
{ //可知AppClassLoader类加载路径为java.class.path
String str = System.getProperty("java.class.path");
final File[] arrayOfFile = str == null ? new File[0] : Launcher.getClassPath(str);
(ClassLoader)AccessController.doPrivileged(new PrivilegedAction()
{
public Launcher.AppClassLoader run()
{
URL[] arrayOfURL = this.val$s == null ? new URL[0] : Launcher.pathToURLs(arrayOfFile);
return new Launcher.AppClassLoader(arrayOfURL, paramClassLoader);
}
});
}
}
ContextClassLoader
- 当父类加载器需要加载子类加载器中的资源时候可以通过设置和获取线程上下文类加载器来实现。
- 一个类加载器要使用不在当前类加载器类查找路径路径中的情况,这种情况下可以新建一个在指定路径查找类的类加载器
网友评论