源码类位置:sun.misc.Launcher、java.lang.ClassLoader
注意:建议阅读前先了解一下双亲委派机制:jvm的类加载器(classloader)及类的加载过程
背景
sun.misc.Launcher是类加载器实始化过程中会创建的一个实例,也是说整个JVM启动只会创建仅有一个Launcer的实例,所以该对角是一个核心。而Lancer中使用了单例模式进行创造,通过创建完实例后再进入初始化类加载器双亲委派的实例。
为什么要有双亲委派机制?直接加载不是效率更高?
1.双亲委派机制也叫沙箱安全机制,主要是防止核 心API被随意篡改。
2.避免类重复加载,每次加载先寻找父类是否加载过,如果加载了加载类全局仅加载过1次;
分析如下
以下是Launcher的构建方法代码分析
//构建方法
public Launcher() {
Launcher.ExtClassLoader var1;
try {
//获取扩展类加载器
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
//失败抛出异常提示
throw new InternalError("Could not create extension class loader", var10);
}
try {
//初始化appClassLoader同时通过appClassLoader进行加载 扩展类加载器类对象(就是我们自定义类,自已编写程序)
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
//失败抛出异常提示
throw new InternalError("Could not create application class loader", var9);
}
//设置当前线程为这个this.loader(就是classLoader)
Thread.currentThread().setContextClassLoader(this.loader);
//获取系统配置
String var2 = System.getProperty("java.security.manager");
//不为空 并且不等于""和default 进行单例加载(这里用到了单例模式)
if (var2 != null) {
SecurityManager var3 = null;
if (!"".equals(var2) && !"default".equals(var2)) {
try {
var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();
} catch (IllegalAccessException var5) {
} catch (InstantiationException var6) {
} catch (ClassNotFoundException var7) {
} catch (ClassCastException var8) {
}
} else {
var3 = new SecurityManager();
}
if (var3 == null) {
throw new InternalError("Could not create SecurityManager: " + var2);
}
//进行设置安全管理器 就是校验是不是java.lang下面的对象
System.setSecurityManager(var3);
}
}
Launcer下面还有一个AppClassLoader内部类值得一读,位置:sun.misc.Launcher.AppClassLoader#loadClass
//可以发现AppClassLoader是继承URLClassLoader而不是extClassLoader 所以这里的关系不是继续只是引用
static class AppClassLoader extends URLClassLoader {
//获取当前类URL地址
final URLClassPath ucp = SharedSecrets.getJavaNetAccess().getURLClassPath(this);
//通过类信息获取appClassLoader的方法
public static ClassLoader getAppClassLoader(final ClassLoader var0) throws IOException {
//获取类路劲
final String var1 = System.getProperty("java.class.path");
//通过地址获取对象流
final File[] var2 = var1 == null ? new File[0] : Launcher.getClassPath(var1);
//返回类信息
return (ClassLoader)AccessController.doPrivileged(new PrivilegedAction<Launcher.AppClassLoader>() {
public Launcher.AppClassLoader run() {
URL[] var1x = var1 == null ? new URL[0] : Launcher.pathToURLs(var2);
return new Launcher.AppClassLoader(var1x, var0);
}
});
}
//构构方法(重写classLoader的方法)
AppClassLoader(URL[] var1, ClassLoader var2) {
//调用父类获取
super(var1, var2, Launcher.factory);
//初始化jdk信息并放到缓存中
this.ucp.initLookupCache(this);
}
//核心类 加载类对象的方法,也是双亲委派实现的方法
public Class<?> loadClass(String var1, boolean var2) throws ClassNotFoundException {
//做相关的校验 比如包名称为能为空等
int var3 = var1.lastIndexOf(46);
if (var3 != -1) {
SecurityManager var4 = System.getSecurityManager();
if (var4 != null) {
var4.checkPackageAccess(var1.substring(0, var3));
}
}
//判断地址是否符合
if (this.ucp.knownToNotExist(var1)) {
//通过查询当前类加载器是否已加载过
Class var5 = this.findLoadedClass(var1);
//判断是否加载了
if (var5 != null) {
if (var2) {
//链接指定的类装入加载器,然后直接返回
this.resolveClass(var5);
}
return var5;
} else {
//抛出异常加载
throw new ClassNotFoundException(var1);
}
} else {
//通过父类进入加载(下面看)
return super.loadClass(var1, var2);
}
}
//获取指定代码的权限
protected PermissionCollection getPermissions(CodeSource var1) {
PermissionCollection var2 = super.getPermissions(var1);
var2.add(new RuntimePermission("exitVM"));
return var2;
}
//判断是否已上锁 (有可能其它对象正在加载)
private void appendToClassPathForInstrumentation(String var1) {
assert Thread.holdsLock(this);
super.addURL(Launcher.getFileURL(new File(var1)));
}
//获取上下文信息
private static AccessControlContext getContext(File[] var0) throws MalformedURLException {
PathPermissions var1 = new PathPermissions(var0);
ProtectionDomain var2 = new ProtectionDomain(new CodeSource(var1.getCodeBase(), (Certificate[])null), var1);
AccessControlContext var3 = new AccessControlContext(new ProtectionDomain[]{var2});
return var3;
}
//初始化类加载器
static {
ClassLoader.registerAsParallelCapable();
}
}
可以发现上面的双亲委派是先在AppClassLoader进行加载,如果加载不成功,则由父类加载,而父类是URLClassLoader 中再去调用extClassLoader,所以很多面试的时候直接上来就问是不是继承,这里不是继承后,从源码也可以看出来。(坑来的)
位置:java.lang.ClassLoader#loadClass(java.lang.String, boolean)
//类加载方法
protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
//同步锁
synchronized (getClassLoadingLock(name)) {
// 通过当前类检查是否加载过
Class<?> c = findLoadedClass(name);
//如果没有加载过
if (c == null) {
long t0 = System.nanoTime();
try {
//父类容器不为空
if (parent != null) {
//通过父类加载
c = parent.loadClass(name, false);
} else {
//通过顶级父类(引导类加载器)进行加载
c = findBootstrapClassOrNull(name);
}
} catch (ClassNotFoundException e) {
// ClassNotFoundException thrown if class not found
// from the non-null parent class loader
}
//如果对象还是为空
if (c == null) {
// If still not found, then invoke findClass in order
// to find the class.
long t1 = System.nanoTime();
//当前类进行加载(也就是说,如果父类或者顶级父类都没办法加载就自已加载)
//注意这里的加载是通过URLClassLoader加载的
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();
}
}
//如果传进来是true 通过链接指定类(一般不会执行,都是传false)
if (resolve) {
resolveClass(c);
}
return c;
}
}
最后
通过学习launcher的核心代码可以发现,我们常说的双亲委派实现方式其实不是直接继承,而是引用或者说调用,其次,通过学习可以发现其实代码量不多(指java)很多本地方式其实是c代码或c++,其实通过学习是如何初始化和双亲委派的实现逻辑后续去学习如何打破双亲委派可以更深刻理解,当然本文没有读整个文件,有部分可能描述有误,其实有发现同学可以指出或交流。