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 ...
随机推荐
- webview加载url出现空白页面,有些页面没问题
用webview加载url出现空白页,测试后把百度,Github之类的url传进去都没问题,后来发现是因为布局的原因,因为webview对不同的网站兼容性没有那么强,特别是现在出现的各种前端布局,没法 ...
- js面试题知识点全解(一原型和原型链)
1.如何准确判断一个变量是数组类型2.写一个原型链继承的例子3.描述new一个对象的过程4.zepto(或其他框架)源码中如何使用原型链知识点:1.构造函数2.构造函数-扩展3.原型规则和示例4.原型 ...
- ngx-bootstrap使用01 安装ngx-bootstrap和bootstrap及其使用、外部样式引入
1 版本说明 2 新建一个angular项目 ng new 项目名 --stayle=scss 代码解释:创建一个样式文件格式为SCSS的angular项目 技巧01:由于我angular-cli的版 ...
- Ubuntu 添加用户到sudoers
ubuntu上的用户有时候需要用到管理员权限,可以通过修改 /etc/sudoers 文件内容添加用户权限. 操作方式 1. 首先以root进入系统打开文件 sudo vim /etc/sudoers ...
- 39、count_rpkm_fpkm_TPM
参考:https://f1000research.com/articles/4-1521/v1 https://www.biostars.org/p/171766/ http://www.rna-se ...
- 51NOD1052 最大M字段和
传送门 分析 一眼看去我们自然会想到dp[i][j][k]表示区间[i,j]中选k个子段的最大值.然后我们考虑降去一维.我们设dp[i][j]表示考虑了前i个数,在选了a[i]的情况下共有j个子段的最 ...
- Git 之 初使用
什么是Git? Git 是一个开源的分布式版本控制软件,用以有效.高速的处理从很小到非常大的项目版本管理. Git 最初是由Linus Torvalds设计开发的,用于管理Linux内核开发.Git ...
- 存储过程自动更新ID
DECLARE @i int --更新题序编号 UPDATE UserAnswer SET @i=@i+,TestOrder=@i WHERE UserScoreID=' //根据ID 累加更新
- SQL CTE 递归 查询省,市,区
IF OBJECT_ID('tb') IS NOT NULL DROP TABLE tb ) , pid ) , name )) ' , null , '广东省') ' , '广州市') ' , '深 ...
- linux下redis的安装与django-redis使用方法
Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库.缓存和消息中间件. Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set( ...