Launcher类源码分析
我们通常通过ClassLoader.getSystemClassLoader();方法类获取系统类加载器,那么有没有想过这个方法的内部是怎么实现的呢?
接下来我们一起来分析一下这个方法
点进来
那个红框套住的方法就是最主要的方法了,接下来我将这个方法的代码拷贝下来并写上注释,便于理解
private static synchronized void initSystemClassLoader() {
// sclSet 这个成员属性表示 如果系统类加载器已经 保存到该类的 scl这个属性中了, 那么为true
if (!sclSet) {
// scl 表示 引用的系统类加载器,这段代码表示 ,如果 sclSet为false 但是 scl却有值了,那么肯定是哪里出了问题,直接抛异常
if (scl != null)
throw new IllegalStateException("recursive invocation");
// 接下来下面会重点分析这个类,这个类也是我们几天的主角
sun.misc.Launcher l = sun.misc.Launcher.getLauncher();
if (l != null) {
Throwable oops = null;
// 从Launcher 类中获取系统类加载器
scl = l.getClassLoader();
try {
// doPrivileged 方法会接收一个 PrivilegedExceptionAction类型的属性
// 并且会调用这个接口中的 run方法 这里下面会详细介绍这个方法
scl = AccessController.doPrivileged(
new SystemClassLoaderAction(scl));
} catch (PrivilegedActionException pae) {
oops = pae.getCause();
if (oops instanceof InvocationTargetException) {
oops = oops.getCause();
}
}
if (oops != null) {
if (oops instanceof Error) {
throw (Error) oops;
} else {
// wrap the exception
throw new Error(oops);
}
}
}
// 设置完成之后就把 sclSet设置成true
sclSet = true;
}
}
下面我们重点的分析一下我们今天的主角 Launcher 类
由于Launcher类是没有开源的,所以我们在ida中打开的也是反编译后的代码
所以这里我们在github上面看 我这里提供这个地址
这个类中自己定义了一个静态属性来保存这个类的实例
我们重点来看下这个类的构造方法
public Launcher() {
// Create the extension class loader
// 创建扩展类加载器
ClassLoader extcl;
try {
// 下面会重点分析 ,就是通过这个方法来获取到这个扩展类加载器
extcl = ExtClassLoader.getExtClassLoader();
} catch (IOException e) {
throw new InternalError(
"Could not create extension class loader", e);
}
// Now create the class loader to use to launch the application
try {
// 创建系统类加载器 下面也会重点分析
loader = AppClassLoader.getAppClassLoader(extcl);
} catch (IOException e) {
throw new InternalError(
"Could not create application class loader", e);
}
// Also set the context class loader for the primordial thread.
// 将当前线程上下文类加载器设置成系统类加载器
Thread.currentThread().setContextClassLoader(loader);
// Finally, install a security manager if requested
String s = System.getProperty("java.security.manager");
if (s != null) {
SecurityManager sm = null;
if ("".equals(s) || "default".equals(s)) {
sm = new java.lang.SecurityManager();
} else {
try {
sm = (SecurityManager)loader.loadClass(s).newInstance();
} catch (IllegalAccessException e) {
} catch (InstantiationException e) {
} catch (ClassNotFoundException e) {
} catch (ClassCastException e) {
}
}
if (sm != null) {
System.setSecurityManager(sm);
} else {
throw new InternalError(
"Could not create SecurityManager: " + s);
}
}
}
接下来分析一下 ExtClassLoader.getExtClassLoader(); 方法
这里将 ExtClassLoader类的定义拷贝过来了,但是只保留了比较重要的方法
可以 看到 扩展类加载器其实是继承了 URLClassLoader类的 而UrlClassLoader类则继承了
SecureClassLoader类,它又继承了ClassLoader类,所以我在之前那篇 自定义类加载器 博客中讲到过,除根类加载器外,启动类加载器都直接或间接的继承了 ClassLoader类
/*
* The class loader used for loading installed extensions.
*/
static class ExtClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
/**
* create an ExtClassLoader. The ExtClassLoader is created
* within a context that limits which files it can read
*/
public static ExtClassLoader getExtClassLoader() throws IOException
{
// 获取扩展类加载器加载哪些目录下的类文件
final File[] dirs = getExtDirs();
try {
// Prior implementations of this doPrivileged() block supplied
// aa synthesized ACC via a call to the private method
// ExtClassLoader.getContext().
return AccessController.doPrivileged(
new PrivilegedExceptionAction<ExtClassLoader>() {
public ExtClassLoader run() throws IOException {
int len = dirs.length;
for (int i = 0; i < len; i++) {
MetaIndex.registerDirectory(dirs[i]);
}
// 直接创建一个扩展类加载器返回
return new ExtClassLoader(dirs);
}
});
} catch (java.security.PrivilegedActionException e) {
throw (IOException) e.getException();
}
}
/*
* Creates a new ExtClassLoader for the specified directories.
* 这个扩展类加载器的构造方法
*/
public ExtClassLoader(File[] dirs) throws IOException {
super(getExtURLs(dirs), null, factory);
SharedSecrets.getJavaNetAccess().
getURLClassPath(this).initLookupCache(this);
}
private static File[] getExtDirs() {
// 其实扩展类加载器加载的目录就保存在 java.ext.dirs这个环境变量中
// 如果我们想知道扩展类加载器到底加载哪些目录下的文件
// 则我们也可以调用System.getProperty("java.ext.dirs");这个方法来获取
String s = System.getProperty("java.ext.dirs");
File[] dirs;
// 如果这个环境变量的值不是为null的话
if (s != null) {
// 可以理解为将一个字符串按照指定的分隔符分开 类似于 String.split()方法
StringTokenizer st =
new StringTokenizer(s, File.pathSeparator);
int count = st.countTokens();
dirs = new File[count];
for (int i = 0; i < count; i++) {
dirs[i] = new File(st.nextToken());
}
} else {
dirs = new File[0];
}
// 最后就是返回了一个File数组对象
return dirs;
}
}
这个 getAppClassLoader()方法也是和上面的差不多的,只是这里将 扩展类加载器作为了系统类加载器的父类加载器 它也继承了URLClassLoader类
static class AppClassLoader extends URLClassLoader {
static {
ClassLoader.registerAsParallelCapable();
}
public static ClassLoader getAppClassLoader(final ClassLoader extcl)
throws IOException
{
final String s = System.getProperty("java.class.path");
final File[] path = (s == null) ? new File[0] : getClassPath(s);
// Note: on bugid 4256530
// Prior implementations of this doPrivileged() block supplied
// a rather restrictive ACC via a call to the private method
// AppClassLoader.getContext(). This proved overly restrictive
// when loading classes. Specifically it prevent
// accessClassInPackage.sun.* grants from being honored.
//
return AccessController.doPrivileged(
new PrivilegedAction<AppClassLoader>() {
public AppClassLoader run() {
URL[] urls =
(s == null) ? new URL[0] : pathToURLs(path);
return new AppClassLoader(urls, extcl);
}
});
}
}
接下来回到我们的Class Loader类来
重点的分析一下 红框类的那段代码,同样的我们将源代码拷贝出来分析
class SystemClassLoaderAction
implements PrivilegedExceptionAction<ClassLoader> {
private ClassLoader parent;
// 这个构造方法接收一个类加载器,刚刚上面调用这个代码的时候将 Launcher类获取到的
// 系统类加载器传了进来
SystemClassLoaderAction(ClassLoader parent) {
this.parent = parent;
}
// 可以理解为由于实现了PrivilegedExceptionAction接口 这个run方法会自动执行
public ClassLoader run() throws Exception {
// 如果这个环境变量 里面的值为null的话则直接返回 parent这个类加载器
// 也就是刚刚传进来的那个系统类加载器
String cls = System.getProperty("java.system.class.loader");
if (cls == null) {
return parent;
}
// 如果不为null的话,则创建上面那个环境变量中的那个类的Class对象,并获取它的构造方法对象
// 所以由这段代码可见,上面那个环境变量中保存的值是一个类的完全限定名
Constructor<?> ctor = Class.forName(cls, true, parent)
.getDeclaredConstructor(new Class<?>[] { ClassLoader.class });
// 创建对象
ClassLoader sys = (ClassLoader) ctor.newInstance(
new Object[] { parent });
// 并且将线程上下文的类加载器设置成了刚刚从环境变量中创建的一个类加载器对象
Thread.currentThread().setContextClassLoader(sys);
// 然后返回
return sys;
}
}
注意:SystemClassLoaderAction类不是ClassLoader类的一个内部类哦,他是和ClassLoader类平级的哦,只是写在了一个java文件中
那么到现在我们就明白了上面红框中那段代码的意思了,如果设置了"java.system.class.loader" 这么个环境变量的话,那么将系统类加载器就设置成了这个环境变量中设置的那个加载器。那么好玩的也就来了
将我们自己创建的自定义类加载器设置成为 "系统类加载器”
我们将在下篇博客介绍
上一篇: kerberos入坑指南
下一篇: 杂项