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. jquery获取元素在文档中的位置信息以及滚动条位置(转)

    jquery获取元素在文档中的位置信息以及滚动条位置 http://blog.csdn.net/qq_34095777/article/details/78750886     原文链接 原创 201 ...

  2. python 爬虫 下载图片

    import os#导入操作系统模块from urllib.request import urlretrieve#下载url对应的文件from urllib.request import urlope ...

  3. nodejs读文件

    1.异步读取文件:var fs= require('fs'); // 从文件系统中读取请求的文件内容 fs.readFile(pathname.substr(1), function (err, da ...

  4. 说说excel

    今天遇到一个实际问题. 我有一组数据: 0.0.0.1 activate.adobe.com 0.0.0.1 practivate.adobe.com 0.0.0.1 ereg.adobe.com 0 ...

  5. Sql Server 2008 Management studio安装教程

    Sql Server 2008 Management studio安装教程     以下介绍Visual Studio 2010已安装后,sql server 2008 management stud ...

  6. ChineseLunisolarCalendar 农历日期

    #region 农历日期 static ChineseLunisolarCalendar cCalendar = new ChineseLunisolarCalendar(); /// <sum ...

  7. C/C++使用心得:enum与int的相互转换

    如何正确理解enum类型? 例如: enum Color { red, white, blue}; Color x; 我们应说x是Color类型的,而不应将x理解成enumeration类型,更不应将 ...

  8. Linq 和 Lambda 查询中按照多个值进行分组GroupBy

    创建要查询的对象: class Employee { public int ID { get;set; } public string FName { get; set; } public int A ...

  9. C#:数据库通用访问类 SqlHelper

    using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; usin ...

  10. 浅谈《守望先锋》中的 ECS 构架

    https://blog.codingnow.com/2017/06/overwatch_ecs.html 今天读了一篇 <守望先锋>架构设计与网络同步 .这是根据 GDC 2017 上的 ...