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. day17 14.dao模式介绍

    Web的三层架构,不是MVC,Web层,Service层,DAO层. 之前玩的JSP Servlet JavaBean那是MVC模式,那玩意只是表现层的东西. 转账汇款的例子. 说了这么多有啥用啊,一 ...

  2. Codeforces 1142D(dp)

    题目传送 先给出设计dp的结论: dp[i][j]:以第i个位置.以rankj的数拓展出去的方案数.意会一下,我实在想不好语言-- 其中所谓rankj=真·rank%11 找到拓展的规律,转移也就顺理 ...

  3. noi.ac day1t3 Sort

    传送门 分析 快排的原理是以任意一个数为标准,然后把所有小于它的数换到它的左边,所有大于它的数换到它的右边.我们就使用快排的思路,分治整个区间.对于每个区间以排好序的这个数列的中间位置的值为标准,然后 ...

  4. java全栈day12----final static 匿名对象 内部类 包的声明与访问

    final关键字概念 继承的出现提高了代码的复用性,并方便开发.但随之也有问题,有些类在描述完之后,不想被继承, 或者有些类中的部分方法功能是固定的,不想让子类重写.可是当子类继承了这些特殊类之后, ...

  5. Makefile的使用

    Makefile 使用 一.实验说明 课程说明 在先前的课程中,我们已经学习了 gcc 和 gdb 的使用.本节课程中,我们将介绍 Makefile 的使用.Makefile带来的好处就是--&quo ...

  6. springcloud +spring security多种验证方式之第三方token生成自己的token通过校验和自己的表单验证大体流程

    步骤: 1.继承 WebSecurityConfigurerAdapter.class,其中使用两个过滤器,一个spring scurity自带的UsernamePasswordAuthenticat ...

  7. 企业建立私有云的N个理由

    简而言之,私有云意味着高效.安全.可控(技术路线是否可控另说了),也意味着高成本.有限弹性.(政绩工程不在讨论范围) 全面回答这个问题,其实包括两个部分:为什么要上云计算,以及为什么要采用私有化部署模 ...

  8. Python lib库docker-py和docker的区别

    1)两者的安装方式 pip install docker A Python library for the Docker Engine API pip install docker-py A Pyth ...

  9. CentOS6.5上Zabbix3.0的RPM安装【三】-安装并添加Agent

    七.Download and install Zabbix Agent Zabbix Agent is required to install on all remote systems needs  ...

  10. CHNS类

    NS类集合介绍 1.常用部分 NSDictionary NSString NSArray 数组 NSTimer 定时器 NSRange 范围 NSNotification 2.网络相关 NSURLCo ...