JAVA中的Launcher类解析
作者:feiyingHiei
一、ClassLoader
sun.misc.Launcher类是java的入口,在启动java应用的时候会首先创建Launcher类,创建Launcher类的时候回准备应用程序运行中需要的类加载器。
Launcher作为JAVA应用的入口,根据双亲委派模型,Laucher是由JVM创建的,它类加载器应该是BootStrapClassLoader, 这是一个C++编写的类加载器,是java应用体系中最顶层的类加载器,负责加载JVM需要的一些类库(<JAVA_HOME>/lib)。可以通过一个简单的代码验证一下我们的想法。
public class App { public static void main(String[] args) { ClassLoader classLoader = Launcher.class.getClassLoader(); } }
这里的classLoader是null,说明Launcher确实是BootstrapClassLoader加载的,那么我们就会非常好奇,ExtClassLoader是什么时候创建的呢?翻看一下Launcher的构造器的代码。
public Launcher() { sun.misc.Launcher.ExtClassLoader extClassLoader; try { extClassLoader = sun.misc.Launcher.ExtClassLoader.getExtClassLoader(); } catch (IOException var10) { throw new InternalError("Could not create extension class loader", var10); } try { this.loader = sun.misc.Launcher.AppClassLoader.getAppClassLoader(extClassLoader); } catch (IOException var9) { throw new InternalError("Could not create application class loader", var9); } Thread.currentThread().setContextClassLoader(this.loader); }
Launcher在创建的时候,第一件事情就是获取ExtClassLoader, ExtClassLoader在JVM中是一个单例, 创建过程也是通过获取环境变量来获取ext加载的目录,生成一个ExtClassLoader,ExtClassLoader是URLClassLoader的子类。
public static Launcher.ExtClassLoader getExtClassLoader() throws IOException { if (instance == null) { Class clazz = Launcher.ExtClassLoader.class; synchronized(Launcher.ExtClassLoader.class) { if (instance == null) { instance = createExtClassLoader(); } } } return instance; }
在获取ExtClassLoader之后,以此作为父类加载器创建一个 sun.misc.Launcher.AppClassLoader, AppClassLoader的加载路径是java.class.path标记的路径,相同的,AppClassLoader也是URLClassLoader的子类。最终会将当前线程的上下文类加载器设置为AppClassLoader。 通过上述分析,当我们需要获取当前应用程序的AppClassLoader或者ExtClassLoader的时候,可以直接使用Launcher来访问。
public class App { public static void main(String[] args) { ClassLoader appClassLoader = Launcher.getLauncher().getClassLoader(); ClassLoader extClassLoader = appClassLoader.getParent(); } }
二、Thread上下文ClassLoader的思考
java程序并不是一个单独的可执行文件,而是由一组.class文件组成。在应用程序启动的时候,并不是所有的类都会被加载,一个类被加载的时候会使用引用这个类的那个类的加载器来加载,也就是xxx.class.getClassLoader()。但是在日常使用的时候,还有一种是通过Thread的上限文获取类加载器。
在java中为什么需要上下文类加载器呢,这个就是一个非常有意思的问题。 我们都知道java类加载的双亲委派模型,在加载一个类的时候,会优先委派给父类加载器,这样保证不会出现类被重复加载,也保证了java一些基础类可以稳定的存在,不会被用户自定义类顶替掉。
双亲委派模型并不是完美的,在一些场景下会出现一些比较难解决的问题,举个例子,在使用SPI的时候,ServiceLoader是通过BootStrap类加载器加载的,在执行到加载用户编写的扩展类的时候,如果使用当前类的类加载器,是肯定无法加载到用户编写的类的,这个时候就无法继续执行了,所以这个时候就需要使用Thread的上下文类加载器,查看源码的时候我们就发现,在用户不主动传递ClassLoader的时候,会获取当前上下文类加载器,这样应用程序才能正常的执行。
public static <S> ServiceLoader<S> load(Class<S> service) { ClassLoader cl = Thread.currentThread().getContextClassLoader(); return ServiceLoader.load(service, cl); }
到此这篇关于JAVA中的Launcher类解析的文章就介绍到这了,更多相关JAVA的Launcher内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!