ClassLoader作用

classloader这个写业务代码的童鞋们,应该很少用到,但是写框架的应该很熟悉。这个类负责Java底层的类的加载和查找,简单滴说Java 的所有类都是由它负责将class文件加载到JVM

这篇文章主要是讲解,ClassLoader是如何查找和加载类的

常用的ClassLoader

   ①:BootStrapClassloader  负责加载jre下面的核心类

②:ExtClassLoader      负责加载jre/ext下面的类

③:AppClassloader      负责加载classpath下面的类或者jar,这个classpath很广,包括(我们平常能接触到也就是这个AppClassload)

①:jre/lib下面的部分包

②:web项目的classpath路径下的所有的类和资源

③:web项目依赖的第三方jar包

④:自定义classloader

   如果有兴趣的话,你们可以分别打印出每个classloader管理的资源,代码如下

 
 public static  void print(){
  URL[] bootStrapUrls = Launcher.getBootstrapClassPath().getURLs();
  System.out.println("-----------boot-----------------");
  for(URL url : bootStrapUrls)
  System.out.println(url);
   System.out.println("-----------boot-----------------");    URLClassLoader extClassLoader = (URLClassLoader)Thread.currentThread().getContextClassLoader().getParent();
   URL[] extUrls = extClassLoader.getURLs();
  System.out.println("-----------ext-----------------");
  for(URL url : extUrls)
  System.out.println(url);
   System.out.println("-----------ext-----------------");   URLClassLoader appClassLoad = (URLClassLoader)Thread.currentThread().getContextClassLoader();
  URL[] appClassLoadUrls = appClassLoad.getURLs();
  System.out.println("-----------appClassloader-----------------");
  for(URL url : appClassLoadUrls)
System.out.println(url);
  System.out.println("-----------appClassloader-----------------");   String var1 = System.getProperty("java.class.path");
  System.out.println("classpath:"+var1);
  }


ClassLoader如何发现资源   

    
①:A.class.getResourceAsStream(resource)
②:A.class.getClassLoader().getResourceAsStream("")
③:Class.forName("").newInstance();
      上面的代码大家熟悉不,我相信很多人都用过,但是用的时候都很迷茫,每次用的时候一旦出错,都不知道为啥出错,怎么修改(内心都是why? why? why?)到底是为啥呀 ???下面我们就聊聊这个Classloader的原理

这段是基础介绍,如果大家知道可以跳过
 一,各个Classloader的关系直接上图(双亲委托机制)


            查找一个.class文件时,从上图可以看到,

①:查找资源的时候appClassloader会将查找任务先委托给自己的上级ExtClassloader

②:ExtClassloader会将查找任务先委托给上级BootStrapClassloader

③:只有在上级查找不到情况下,自己再负责查找资源并加载

经典加载代码 Thread.currentThread().getContextClassLoader().loadClass("");


protected Class<?> loadClass(String name, boolean resolve)
throws ClassNotFoundException
{
synchronized (getClassLoadingLock(name)) {
// First, check if the class has already been loaded
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();
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();
}
}
if (resolve) {
resolveClass(c);
}
return c;
}
}

 
这段是基础介绍,如果大家知道可以跳过

问题
    问题1:每个classloader 管理的资源存储在什么地方
    问题2:项目依赖的jar包本身还要依赖第三jar包,此时classloader又该如何处理
示例
 main方法执行后,JVM是如何加载main方法所在的类
首先大家要知道JVM是不会傻傻的在启动时候,直接将程序中所有的类和资源都加载进内存,JVM秉承的设计原则就是,用时再加载,不用的时候,你就歇着吧(JVM作为BOSS 很抠的哈),那么JVM是怎么加载main方法所在的类尼
①:程序启动的时候,JVM负责先装载所有的classLoader(这一部分,大家不用深究,具体JVM怎么下指令的,这个东西是底层做的,我们也看不懂),我们只需要知道JVM帮忙实例化了sun.misc.Launcher这个类,在这个构造函数中
,装载所有的Classloader(主意:BootstrapClassloader不归它管,你可以理解BootstrapClassloader为JVM直辖(就像直辖市一样))
public Launcher() {
Launcher.ExtClassLoader var1;
try {
//加载ExtClassload
var1 = Launcher.ExtClassLoader.getExtClassLoader();
} catch (IOException var10) {
throw new InternalError("Could not create extension class loader", var10);
} try {
//加载AppClassLoad
this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);
} catch (IOException var9) {
throw new InternalError("Could not create application class loader", var9);
} Thread.currentThread().setContextClassLoader(this.loader);
String var2 = System.getProperty("java.security.manager");
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);
} System.setSecurityManager(var3);
} }

②:加载main方法所在的类如:A,调用的部分流程如下:最终调用Classloader.loadClass这个方法加载A类,这个类(典型的双亲委托模式实例)

③:带着问题分析一下ClassLoader.loadClass 这个类

从上文得知findClass() 这个方法是具体负责加载资源,但是你点击进去会发现这是一个为空实现,看看这个类的继承关系,AppClassLoader父类为URLClassLoader,我们到URLclassLoader上看看

其实到这里,我们已经可以看到,其实是类查找和加载UCP 这个对象操作,UCP(是URLClasspath这个类的实例)

     ps:这里的涉及到缓存的代码,暂时不要看(如:getLookUpCache()),基本上都无法名中(反正我debug的时候,没啥用)

 

重点来啦,要看懂上述这段代码,就不的不提一下上面讲的那个类URLClassPath 和URLClassPath.loader这个内部类

path:初始化类加载器的时候,设置类记载器管理的默认资源列表(比如appclassLoader 这个类加载器,这个path中主要存储的是项目依赖的jar包路径(不包括此ar包依赖的第三方jar),jre/lib下的部分jar)

urls:其实初始化完成以后应该和path一样,区别在于urls还会解决这个问题(问题2:项目依赖的jar包本身还要依赖第三jar包,此时classloader又该如何处理),

也就是说,urls初始化完成以后,还会去加载jar包依赖的第三方jar包,怎么加载的(下面分析)(这是一个栈的数据结构,先进后出)

loaders:这个属性有点强啦,是资源抽象(URLClassPath.loader)的列表,提供一个非常关键的方法  Resource getResource(String var1, boolean var2) ,负责匹配资源(或者说在这个资源中能不能找到这个要加载的类)

URLClassPath.loader  有2个具体的实现类,每个都重写了getResource这个方法

lamp 这个其实作用不是很大,做一个缓存的作用,如果URLClassPath.loader实例化过啦,直接从缓存中取出,不用重新实例化啦


那么我们现在再来分析一下,getLoader这个方法


private synchronized URLClassPath.Loader getLoader(int var1) {
if(this.closed) {
return null;
} else {
//这里不用if用while,原因是loader存在重复加载,此时缓存已经有啦(证明这个loader是无法找到这个资源),
//直接获取下一个loader即可(项目依赖的jar包很多,存在多个jar包在依赖其他的同一个jar包很正常,
//如:每个jar包都需要日志相关的jar)
while(this.loaders.size() < var1 + 1) {
Stack var3 = this.urls;
URL var2;
synchronized(this.urls) {
//urls都空即当前这个classloader无法加载这个类
if(this.urls.empty()) {
return null;
} var2 = (URL)this.urls.pop();
} String var9 = URLUtil.urlNoFragString(var2);
if(!this.lmap.containsKey(var9)) {
URLClassPath.Loader var4;
try {
var4 = this.getLoader(var2);
//这个地方主要是解决问题2(有兴趣可以看看它的实现类,JarLoader,简单滴讲就是读取MANIFEST.MF中的classpath这个字段),问题2:项目依赖的jar包本身还要依赖第三jar包,此时classloader又该如何处理
URL[] var5 = var4.getClassPath();
if(var5 != null) {
this.push(var5);
}
} catch (IOException var6) {
continue;
} catch (SecurityException var7) {
if(DEBUG) {
System.err.println("Failed to access " + var2 + ", " + var7);
}
continue;
} this.validateLookupCache(this.loaders.size(), var9);
this.loaders.add(var4);
this.lmap.put(var9, var4);
}
} if(DEBUG_LOOKUP_CACHE) {
System.out.println("NOCACHE: Loading from : " + var1);
}
return (URLClassPath.Loader)this.loaders.get(var1);
}
}



给大家画了一张简单时序图


简单总结
整个classloader加载思想就是:将classloader能够管理的资源交给URLClasspath,将查找这个脏活交给这个loader内部类,通过调用getResource()来确定是否有能力加载
												

聊聊 ClassLoader 是如何查找资源的的更多相关文章

  1. Spring源码学习之:ClassLoader学习(1)

    转载:http://longdick.iteye.com/blog/442213 java应用环境中不同的class分别由不同的ClassLoader负责加载. 一个jvm中默认的classloade ...

  2. JVM的ClassLoader过程分析

    本文来自网络:深入分析Java ClassLoader原理 http://my.oschina.net/zhengjian/blog/133836 一. JVM的ClassLoader过程以及装载原理 ...

  3. 图解classloader加载class的流程及自定义ClassLoader

    图解classloader加载class的流程及自定义ClassLoader 博客分类: JVM JavaJVM虚拟机EXTSUN /** *  转载请注明作者longdick    http://l ...

  4. ClassLoader 机制

    JAVA启动后,是经过JVM各级ClassLoader来加载各个类到内存.为了更加了解加载过程,我通过分析和写了一个简单的ClassLoader来粗浅的分析它的原理. JVM的ClassLoader分 ...

  5. 理解ClassLoader基本原理

    当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构:        bootstrap classloader                 |        exte ...

  6. classloader加载过程

    /** *  转载请注明作者longdick    http://longdick.iteye.com * */ java应用环境中不同的class分别由不同的ClassLoader负责加载. 一个j ...

  7. 理解Java ClassLoader机制

    当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构: bootstrap classloader                |       extension cla ...

  8. classloader.getresources() 介绍

    ◆普通情况下,我们都使用相对路径来获取资源,这种灵活性比較大. 比方当前类为com/bbebfe/Test.class 而图像资源比方sample.gif应该放置在com/bbebfe/sample. ...

  9. Java ClassLoader深入讲解(转)

    当JVM(Java虚拟机)启动时,会形成由三个类加载器组成的初始类加载器层次结构: bootstrap classloader                |       extension cla ...

随机推荐

  1. 一、ETL实践之数据可视化架构

    开篇心声: 不管是学习新知识,还是遇到各种难题,总能在技术论坛找到经验帖子.一直享受大家提供的帮助,而自己没有任何输出,实在过意不去.我相信技术是经验的交流,思维的碰撞. 这是我一次写技术分享文章,我 ...

  2. C语言之 Switch和?:运算符的反汇编

    Switch条件语句 通过上面一篇了解了条件语句的使用,接下来就直接进行反汇编学习 #include <stdio.h> void print() { int b = 1; switch ...

  3. Python+Post请求中涉及到多个参数data方法的应用

    进行post请求,Python提供了httplib.urllib2,同时也可以引用requests模块的一些方法.前几天做持续集成,运用requests写了一个post请求.代码如下: import ...

  4. CVE-2017-6090&msf的基本使用(一)

    渗透环境的搭建 phpcollab的下载:phpCollab-v2.5.1.zip 解压到www目录,给www目录权限,因为这个漏洞需要写的权限 chmod 777 wwww 基本环境 配置 mysq ...

  5. STM32入门系列-GPIO结构

    已经了解了STM32 GPIO的基本概念及引脚分类.现在来看下STM32 GPIO内部的结构是怎样的.IO端口位的基本结构如下图所示. 从图中可以看出GPIO内部结构还是比较复杂的,只要将这张GPIO ...

  6. Python3网络学习案例一:Ping详解

    1. 使用Ping做什么 ping用于确定本地主机是否能与另一台主机成功交换(发送与接收)数据包,再根据返回的信息,就可以推断TCP/IP参数是否设置正确,以及运行是否正常.网络是否通畅等. 2. 效 ...

  7. Socket创建简单服务器和客户端程序

    使用Socket编程创建简单服务器和客户端 要知道的 Socket-AddressFamily, SocketType, ProtocolType https://blog.csdn.net/weix ...

  8. 网络编程NIO:BIO和NIO

    BIO BIO(Blocking I/O),同步阻塞,实现模式为一个连接一个线程,即当有客户端连接时,服务器端需为其单独分配一个线程,如果该连接不做任何操作就会造成不必要的线程开销.BIO是传统的Ja ...

  9. 用Matlab对导出的数据进行可视化

    我这里是MapReduce导出的数据,MapReduce导出的数据中,Key和Value之间用制表符分隔的,可以直接作为表格型数据进行操作,复制一下导出的数据 1. 首先在Matlab工作区创建一个元 ...

  10. SpringBoot入门最简单的一个项目示例

    使用IDEA创建一个SpringBoot项目 1.1 打开IDEA,文件-New-Project 1.2下一步,选择版本8(根据自己安装的JDK版本来选择) 1.3 下一步后点击Web,勾选Sprin ...