classloader就是把类文件加载到jvm中供虚拟机使用,先看一个magic小例子:

首先,我定义一个alex/vicky包,然后在这个包内定义一个接口:

public interfaceIService {

voidservice();

}

然后使用这个接口定义一个实现类:

public classService extendsIService{

@Override

publicvoidservice(){
System.out.println("Alex");

}

}

然后把这个类定义删掉,把这个类生成的class文件放到c:/alex/vicky文件夹下.

再做一个实现类:

public classMain {

public static voidmain(String[] args) throwsException {

URLu = newURL("file:c:/");

URLClassLoaderucl = newURLClassLoader(newURL[] { u });

Classc = ucl.loadClass("alex.vicky.Service");

IServiceobj = (IService) c.newInstance();

obj.service();

}

}

可以看到,我们通过URL方式,远程加载了类文件.

一旦一个类被载入JVM中,同一个类就不会被再次载入了(切记,同一个类)。在Java中,一个类用其完全匹配类名(fullyqualified classname)作为标识,这里指的完全匹配类名包括包名和类名。但在JVM中一个类用其全名和一个加载类ClassLoader的实例作为唯一标识。

java的几种ClassLoader:

在java中,我们可以取得这么以下三个ClassLoader类:

一. ClassLoader基本概念

1.ClassLoader分类

类装载器是用来把类(class)装载进JVM的。

JVM规范定义了两种类型的类装载器:启动内装载器(bootstrap)和用户自定义装载器(user-definedclass loader)。

JVM在运行时会产生三个ClassLoader:BootstrapClassLoader、ExtensionClassLoader和AppClassLoader。Bootstrap是用C++编写的,我们在Java中看不到它,是Null,是JVM自带的类装载器,用来装载核心类库,如java.lang.*等。

AppClassLoader的Parent是ExtClassLoader,而ExtClassLoader的Parent为BootstrapClassLoader。

Java提供了抽象类ClassLoader,所有用户自定义类装载器都实例化自ClassLoader的子类。系统类装载器可以通过ClassLoader.getSystemClassLoader()方法得到。

系统为什么要分别指定这么多的ClassLoader类呢?

答案在于因为java是动态加载类的,这样的话,可以节省内存,用到什么加载什么,就是这个道理,然而系统在运行的时候并不知道我们这个应用与需要加载些什么类,那么,就采用这种逐级加载的方式

(1)首先加载核心API,让系统最基本的运行起来

(2)加载扩展类

(3)加载用户自定义的类

publicstatic void main(String[] args) {

System.out.println(System.getProperty("sun.boot.class.path"));

System.out.println(System.getProperty("java.ext.dirs"));

System.out.println(System.getProperty("java.class.path"));

}

程序结果为:

E:\Myeclipse6.0\jre\lib\rt.jar;E:\Myeclipse 6.0\jre\lib\i18n.jar;E:\Myeclipse6.0\jre\lib\sunrsasign.jar;E:\MyEclipse6.0\jre\lib\JSse.jar;E:\MyEclipse 6.0\jre\lib\jce.jar;E:\MyEclipse6.0\jre\lib\charsets.jar;E:\MyEclipse 6.0\jre\classes

E:\MyEclipse6.0\jre\lib\ext

E:\workspace\ClassLoaderDemo\bin

在上面的结果中,你可以清晰看见三个ClassLoader分别加载类的路径;也知道为什么我们在编写程序的时候,要把用到的jar包放在工程的classpath下面啦,也知道我们为什么可以不加载java.lang.*包啦!其中java.lang.*就在rt.jar包中;

ClassLoader的加载机制

现在我们设计这种一下Demo:

packagejava.net;

publicclass URL {

privateString path;

publicURL(String path) {

this.path= path;

}

publicString toString() {

returnthis.path + " new Path";

}

}

packagejava.net;

importjava.net.*;

publicclass TheSameClsDemo {

publicstatic void main(String[] args) {

URLurl = new URL("http://www.baidu.com");

System.out.println(url.toString());

}

}

在这种情况下,系统会提示我们出现异常,因为我们有两个相同的类,一个是真正的URL,一个是我在上面实现的伪类;出现异常是正常的,因为你想想,如果我们在执行一个applet的时候,程序自己实现了一个String的类覆盖了我们虚拟机上面的真正的String类,那么在这个String里面,不怀好意的人可以任意的实现一些功能;这就造成极不安全的隐患;所以java采用了一种名为“双亲委托”的加载模式;

以下是jdk源代码:

protectedsynchronized Class<?> loadClass(String name, boolean resolve)

throwsClassNotFoundException

{

//First, check if the class has already been loaded

Classc = findLoadedClass(name);

if(c == null) {

try{

if(parent != null) {

c= parent.loadClass(name, false);

}else {

c= findBootstrapClass0(name);

}

}catch (ClassNotFoundException e) {

//If still not found, then invoke findClass in order to find theclass.

c= findClass(name);

}

}

if(resolve) {

resolveClass(c);

}

returnc;

}

在上面的代码中,我们可以清晰的看见,我们调用一个ClassLoader加载程序的时候,这个ClassLoader会先调用设置好的parentClassLoader来加载这个类,如果parent是null的话,则默认为BootClassLoader类,只有在parent没有找的情况下,自己才会加载,这就避免我们重写一些系统类,来破坏系统的安全;

类与它所依赖的类的classloader机制:

如果一个类是由某个classloader加载,那么这个类依赖的类(非显式的通过某个classloader加载)必须也由该classloader或其父classloader加载,无视子classloader

通过thread.getContextClassloader:

thread.getContextClassloader默认返回AppClassLoader,除非你显式setContextClassloader

来看一下jdbc中如何使用classloader:

一般我们写一个jdbc程序都会这样:

Class.forName("com.mysql.jdbc.Driver");

Stringurl ="jdbc:mysql://127.0.0.1/test?useUnicode=true&characterEncoding=utf-8";

Stringuser = "root";

Stringpsw = "yanyan";

Connectioncon = DriverManager.getConnection(url,user, psw);

为什么需要第一句话?

其实第一句话可以用下面这句话替代:

com.mysql.jdbc.Driverdriver = new com.mysql.jdbc.Driver();

其他都不用变化,有人会问,driver对象从来没有用到.对,它的效果就是在调用DriverManager的getConnection方法之前,保证相应的Driver类已经被加载到jvm中,并且完成了类的初始化工作就行了.注意了,如果我们进行如下操作,程序是不能正常运行的,因为这样仅仅使Driver类被装载到jvm中,却没有进行相应的初始化工作。

com.mysql.jdbc.Driverdriver = null;

//or:

ClassLoadercl = new ClassLoader();

cl.loadClass("com.mysql.jdbc.Driver");

我们都知道JDBC是使用Bridge模式进行设计的,DriverManager就是其中的Abstraction,java.sql.Driver是Implementor,com.mysql.jdbc.Driver是Implementor的一个具体实现(请参考GOF的Bridge模式的描述)。大家注意了,前一个Driver是一个接口,后者却是一个类,它实现了前面的Driver接口。

Bridge模式中,Abstraction(DriverManager)是要拥有一个Implementor(Driver)的引用的,但是我们在使用过程中,并没有将Driver对象注册到DriverManager中去啊,这是怎么回事呢?jdk文档对Driver的描述中有这么一句:

Whena Driver class is loaded, it should create an instance of itself andregister it with the DriverManager

哦,原来是com.mysql.jdbc.Driver在装载完后自动帮我们完成了这一步骤。源代码是这样的:

packagecom.mysql.jdbc

publicclass Driver extends NonRegisteringDriver implements java.sql.Driver{

static{

try{

java.sql.DriverManager.registerDriver(newDriver());

}catch (SQLException E) {

thrownew RuntimeException("Can't register driver!");

}

}

publicDriver() throws SQLException {

//Required for Class.forName().newInstance()

}

}

再看一下DriverManager.getConnection(url,user, psw);方法:

ClassLoadercallerCL = DriverManager.getCallerClassLoader();

getConnection(url,info, callerCL)

==>

if(callerCL== null){

callerCL= Thread.currentThread().getContextClassLoader();

}

上面的意思是:代码意思是如果DriverManager类的类加载器为空的话,就使用当前线程的类加载器。仔细想想,DriverManager在rt.jar包中,它是由JDK的启动类加载器加载的,而启动类加载器是C编写的,所以取得的都是空,再者,使用当前线程类加载器的话,那么交由程序编写者来保证能够加载驱动类。而不至于驱动器类无法加载。非常高明的手段~!

Class的这种设计引入了一个有趣的模式:

某个框架制定某个API,而这些api的实现是有其他供应商来提供,为了能让框架类(处于较高层次的classloader)使用api的实现(处于较低层次的classloader)

通过thread.getContextClassloader是来传递classloader(有时候需要thread.setContextClassloader设置好api实现的classloader),用此classloader.getResources找出所有的api实现的具体类名,再用classloader加载之,此时框架都不需要知道api的实现类的类名就能加载之,程序显示了良好的动态性和可扩展性。

classloader 学习的更多相关文章

  1. Java ClassLoader 学习理解

    /** * <html> * <body> * <P> Copyright 1994 JsonInternational</p> * <p> ...

  2. Spring源码学习之:ClassLoader学习(4)

    转载:http://www.codeceo.com/article/java-classloader.html 一:什么是ClassLoader?===>大家都知道,当我们写好一个Java程序之 ...

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

    ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回 这个类的class对象. 1.1 几个相关概念Clas ...

  4. Spring源码学习之:ClassLoader学习(2)

    转载:http://longdick.iteye.com/blog/332580 大家都知道一个java应用项目可以打包成一个jar,当然你必须指定一个拥有main函数的main class作为你这个 ...

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

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

  6. ClassLoader 学习笔记

    概述 在经过编译后.java文件会生成对应的.class文件,但需要执行的时候,虚拟机首先会从class文件中读取必要的信息,而这个过程则成为类加载.类加载时类的生命周期的一部分,也是它的初始步骤. ...

  7. Spring源码学习之:ClassLoader学习(5)-自测

    [一]测试目的(ClassLoader的作用) 1:测试涉及三个jar包,nonbankcard-configure-0.0.1-SNAPSHOT.jar,nonbankcard-persist-0. ...

  8. Java ClassLoader 学习笔记

    参考 Java类加载器(ClassLoader)

  9. 反射(学习整理)----Class类和加载器ClassLoader类的整理

    1.学习反射的时整理的笔记!Class类和ClassLoader类的简单介绍 反射机制中的Class Class内部到底有什么呢?看下图! 代码: Class cls=Person.class; .C ...

随机推荐

  1. webview加载url出现空白页面,有些页面没问题

    用webview加载url出现空白页,测试后把百度,Github之类的url传进去都没问题,后来发现是因为布局的原因,因为webview对不同的网站兼容性没有那么强,特别是现在出现的各种前端布局,没法 ...

  2. js面试题知识点全解(一原型和原型链)

    1.如何准确判断一个变量是数组类型2.写一个原型链继承的例子3.描述new一个对象的过程4.zepto(或其他框架)源码中如何使用原型链知识点:1.构造函数2.构造函数-扩展3.原型规则和示例4.原型 ...

  3. ngx-bootstrap使用01 安装ngx-bootstrap和bootstrap及其使用、外部样式引入

    1 版本说明 2 新建一个angular项目 ng new 项目名 --stayle=scss 代码解释:创建一个样式文件格式为SCSS的angular项目 技巧01:由于我angular-cli的版 ...

  4. Ubuntu 添加用户到sudoers

    ubuntu上的用户有时候需要用到管理员权限,可以通过修改 /etc/sudoers 文件内容添加用户权限. 操作方式 1. 首先以root进入系统打开文件 sudo vim /etc/sudoers ...

  5. 39、count_rpkm_fpkm_TPM

    参考:https://f1000research.com/articles/4-1521/v1 https://www.biostars.org/p/171766/ http://www.rna-se ...

  6. 51NOD1052 最大M字段和

    传送门 分析 一眼看去我们自然会想到dp[i][j][k]表示区间[i,j]中选k个子段的最大值.然后我们考虑降去一维.我们设dp[i][j]表示考虑了前i个数,在选了a[i]的情况下共有j个子段的最 ...

  7. Git 之 初使用

    什么是Git? Git 是一个开源的分布式版本控制软件,用以有效.高速的处理从很小到非常大的项目版本管理. Git 最初是由Linus Torvalds设计开发的,用于管理Linux内核开发.Git ...

  8. 存储过程自动更新ID

    DECLARE @i int --更新题序编号 UPDATE UserAnswer SET @i=@i+,TestOrder=@i WHERE UserScoreID=' //根据ID 累加更新

  9. SQL CTE 递归 查询省,市,区

    IF OBJECT_ID('tb') IS NOT NULL DROP TABLE tb ) , pid ) , name )) ' , null , '广东省') ' , '广州市') ' , '深 ...

  10. linux下redis的安装与django-redis使用方法

    Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set( ...