classloader 学习
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 学习的更多相关文章
- Java ClassLoader 学习理解
/** * <html> * <body> * <P> Copyright 1994 JsonInternational</p> * <p> ...
- Spring源码学习之:ClassLoader学习(4)
转载:http://www.codeceo.com/article/java-classloader.html 一:什么是ClassLoader?===>大家都知道,当我们写好一个Java程序之 ...
- Spring源码学习之:ClassLoader学习(3)
ClassLoader主要对类的请求提供服务,当JVM需要某类时,它根据名称向ClassLoader要求这个类,然后由ClassLoader返回 这个类的class对象. 1.1 几个相关概念Clas ...
- Spring源码学习之:ClassLoader学习(2)
转载:http://longdick.iteye.com/blog/332580 大家都知道一个java应用项目可以打包成一个jar,当然你必须指定一个拥有main函数的main class作为你这个 ...
- Spring源码学习之:ClassLoader学习(1)
转载:http://longdick.iteye.com/blog/442213 java应用环境中不同的class分别由不同的ClassLoader负责加载. 一个jvm中默认的classloade ...
- ClassLoader 学习笔记
概述 在经过编译后.java文件会生成对应的.class文件,但需要执行的时候,虚拟机首先会从class文件中读取必要的信息,而这个过程则成为类加载.类加载时类的生命周期的一部分,也是它的初始步骤. ...
- Spring源码学习之:ClassLoader学习(5)-自测
[一]测试目的(ClassLoader的作用) 1:测试涉及三个jar包,nonbankcard-configure-0.0.1-SNAPSHOT.jar,nonbankcard-persist-0. ...
- Java ClassLoader 学习笔记
参考 Java类加载器(ClassLoader)
- 反射(学习整理)----Class类和加载器ClassLoader类的整理
1.学习反射的时整理的笔记!Class类和ClassLoader类的简单介绍 反射机制中的Class Class内部到底有什么呢?看下图! 代码: Class cls=Person.class; .C ...
随机推荐
- jquery获取元素在文档中的位置信息以及滚动条位置(转)
jquery获取元素在文档中的位置信息以及滚动条位置 http://blog.csdn.net/qq_34095777/article/details/78750886 原文链接 原创 201 ...
- python 爬虫 下载图片
import os#导入操作系统模块from urllib.request import urlretrieve#下载url对应的文件from urllib.request import urlope ...
- nodejs读文件
1.异步读取文件:var fs= require('fs'); // 从文件系统中读取请求的文件内容 fs.readFile(pathname.substr(1), function (err, da ...
- 说说excel
今天遇到一个实际问题. 我有一组数据: 0.0.0.1 activate.adobe.com 0.0.0.1 practivate.adobe.com 0.0.0.1 ereg.adobe.com 0 ...
- Sql Server 2008 Management studio安装教程
Sql Server 2008 Management studio安装教程 以下介绍Visual Studio 2010已安装后,sql server 2008 management stud ...
- ChineseLunisolarCalendar 农历日期
#region 农历日期 static ChineseLunisolarCalendar cCalendar = new ChineseLunisolarCalendar(); /// <sum ...
- C/C++使用心得:enum与int的相互转换
如何正确理解enum类型? 例如: enum Color { red, white, blue}; Color x; 我们应说x是Color类型的,而不应将x理解成enumeration类型,更不应将 ...
- Linq 和 Lambda 查询中按照多个值进行分组GroupBy
创建要查询的对象: class Employee { public int ID { get;set; } public string FName { get; set; } public int A ...
- C#:数据库通用访问类 SqlHelper
using System; using System.Collections.Generic; using System.Data; using System.Data.SqlClient; usin ...
- 浅谈《守望先锋》中的 ECS 构架
https://blog.codingnow.com/2017/06/overwatch_ecs.html 今天读了一篇 <守望先锋>架构设计与网络同步 .这是根据 GDC 2017 上的 ...