java类加载器-----用户自定义类加载器实现
java类加载器主要分为如下几种:
- jvm提供的类加载器
- 根类加载器:底层实现,主要加载java核心类库(如:java.lang.*)
- 扩展类加载器:使用java代码实现,主要加载如:jre/lib/ext/ 下的扩展类库。(父类加载器为根类加载器)
- 系统类加载器(应用类加载器):使用java代码实现,加载classpath目录下的类。(父类加载器为扩展类加载器)
- 用户自定义类加载器:去继承ClassLoader类实现自定义类加载器。
类加载器负责将java字节码文件加载到虚拟机内存中也就是类的生命周期的装载过程。(如下为类的生命周期)
下面是用户自定义类加载器的实现过程及代码:
实现一个用户自定义类加载器需要去继承ClassLoader类并重写findClass方法,代码如下
package com.space; import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream; public class MyClassLoader extends ClassLoader { private String path="/home/luciel/"; //默认加载路径 private String name; //类加载器名称 private final String filetype=".class"; //文件类型 public MyClassLoader(String name) {
// TODO Auto-generated constructor stub
super();
this.name=name;
} public MyClassLoader(ClassLoader parent,String name){
super(parent);
this.name=name;
} @Override
public Class<?> findClass(String name) throws ClassNotFoundException {
// TODO Auto-generated method stub
byte[] b=loadClassData(name);
return defineClass(name, b, 0, b.length);
} private byte[] loadClassData(String name) {
byte[] data=null;
InputStream in=null;
name=name.replace('.', '/');
ByteArrayOutputStream out=new ByteArrayOutputStream();
try {
in=new FileInputStream(new File(path+name+filetype));
int len=0;
while(-1!=(len=in.read())){
out.write(len);
}
data=out.toByteArray();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
in.close();
out.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return data;
} public String getPath() {
return path;
} public void setPath(String path) {
this.path = path;
} @Override
public String toString() {
// TODO Auto-generated method stub
return this.name;
} }
public MyClassLoader(String name) {
// TODO Auto-generated constructor stub
super();
this.name=name;
}
这个构造方法中去调用ClassLoader无参构造方法从ClassLoader源码中可以得出:调用此构造方法会让系统类加载器成为该类加载器的父加载器。(注意:此处父类加载器不一定是继承关系,只是一种包装关系)。
在重写findClass方法时参照java API中实现一个网络类加载器的例子,API例子如下:
class NetworkClassLoader extends ClassLoader {
String host;
int port; public Class findClass(String name) {
byte[] b = loadClassData(name);
return defineClass(name, b, 0, b.length);
} private byte[] loadClassData(String name) {
// load the class data from the connection
. . .
}
}
下面是测试类以及main方法类的测试代码:
package com.space; public class Color {
public Color() {
// TODO Auto-generated constructor stub
System.out.println("Color is loaded by "+this.getClass().getClassLoader()); }
}
package com.space; public class Red extends Color{ public Red() {
// TODO Auto-generated constructor stub
System.out.println("Red is loaded by "+this.getClass().getClassLoader()); } }
package com.space; public class TestMyClassLoader { public static void main(String[] args) throws Exception { MyClassLoader loader1=new MyClassLoader("loader1"); loader1.setPath("/home/luciel/test1/"); MyClassLoader loader2=new MyClassLoader(loader1, "loader2"); loader2.setPath("/home/luciel/test2/"); MyClassLoader loader3=new MyClassLoader(null, "loader3"); loader3.setPath("/home/luciel/test3/"); loadClassByMyClassLoader("com.space.Red",loader2); loadClassByMyClassLoader("com.space.Red",loader3);
} private static void loadClassByMyClassLoader(String name,ClassLoader loader) throws Exception{ Class<?> c=loader.loadClass(name);
Object obj=c.newInstance();
} }
按照main方法中给三个类加载器传入的路径创建相应的环境并将com.space.Red、com.space.Color的class类拷贝到
/home/luciel/test1/和/home/luciel/test2/以及/home/luciel/test3/目录中去将com.space.TestMyClassLoader类和com.space.MyClassLoader拷贝
/home/luciel/main/ 中去并在该目录下执行
最终运行结果如下显示:
[root@localhost main]# java com.space.TestMyClassLoader
Color is loaded by loader1
Red is loaded by loader1
Color is loaded by loader3
Red is loaded by loader3
loadClassByMyClassLoader("com.space.Red",loader2);
如测试代码中 我们调用了loader2去加载Red类但Red类却打印出由loader1加载,这是由于类加载器秉承的是父委托机制loader2在创建时包装了loader1为其父类加载器,而loader1创建时由于调用的是没有传入父类加载器的构造方法,因此它的父加载器为系统类加载器。因此几个加载器的关系如下:
由于loader1的路径下有Red类class文件所以loader1可以加载,因此载Red类构造方法中打印的类加载器为loader1.
我门看似只去加载了Red类但运行结果却将Color父类加载了,而且Color类的加载在Red类之前,那是由于Red类主动使用 了Color类,因此在初始化Red类之前必须先初始化Color类,要初始化就必须先加载,所以先打印出了Color类的输出信息。(关于类的主动使用大家如果不清楚可以查查,一共有6种)
loadClassByMyClassLoader("com.space.Red",loader3);
再分析第二个测试代码,由于loader3创建时传入的父类加载器为 null,看下面关于ClassLoader类源码部分代码或查看java API
/**
* Returns the parent class loader for delegation. Some implementations may
* use <tt>null</tt> to represent the bootstrap class loader. This method
* will return <tt>null</tt> in such implementations if this class loader's
* parent is the bootstrap class loader.
*
* <p> If a security manager is present, and the invoker's class loader is
* not <tt>null</tt> and is not an ancestor of this class loader, then this
* method invokes the security manager's {@link
* SecurityManager#checkPermission(java.security.Permission)
* <tt>checkPermission</tt>} method with a {@link
* RuntimePermission#RuntimePermission(String)
* <tt>RuntimePermission("getClassLoader")</tt>} permission to verify
* access to the parent class loader is permitted. If not, a
* <tt>SecurityException</tt> will be thrown. </p>
*
* @return The parent <tt>ClassLoader</tt>
*
* @throws SecurityException
* If a security manager exists and its <tt>checkPermission</tt>
* method doesn't allow access to this class loader's parent class
* loader.
*
* @since 1.2
*/
@CallerSensitive
public final ClassLoader getParent() {
if (parent == null)
return null;
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkClassLoaderPermission(parent, Reflection.getCallerClass());
}
return parent;
}
getParent方法的说明如下部分文字:
* Returns the parent class loader for delegation. Some implementations may
* use <tt>null</tt> to represent the bootstrap class loader. This method
* will return <tt>null</tt> in such implementations if this class loader's
* parent is the bootstrap class loader.
意思是说我们可以使用null表示 the bootstrap class loader(根类加载器)
那么loader3的父类加载器就是 根类加载器 ,而根类加载器只会去加载那些系统核心类库,显然我们的Red和Color类不属于此范围,而就只能让loader3加载,loader3的加载路径下有这两个类对应的字节码可以成功加载,所以大引出Red和Color类的类加载器为loader3
以上是自己最近在学习jvm时有关类加载器的相关知识总结。
java类加载器-----用户自定义类加载器实现的更多相关文章
- Java魔法堂:类加载器入了个门
一.前言 <Java魔法堂:类加载机制入了个门>中提及整个类加载流程中只有加载阶段作为码农的我们可以入手干预,其余均由JVM处理.本文将记录加载阶段的核心组件——类加载器的相关信息,以便日 ...
- Java虚拟机10:类加载器
类与类加载器 虚拟机设计团队把类加载阶段张的"通过一个类的全限定名来获取此类的二进制字节流"这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这 ...
- java类加载器-Tomcat类加载器
在上文中,已经介绍了系统类加载器以及类加载器的相关机制,还自定制类加载器的方式.接下来就以tomcat6为例看看tomat是如何使用自定制类加载器的.(本介绍是基于tomcat6.0.41,不同版本可 ...
- java类加载器-系统类加载器
系统类加载器 系统类加载器可能都耳详能熟,但是为了完整点,还是先简单的说说系统的类加载器吧. public class Test { public static void main(String[] ...
- JAVA提高七:类加载器
今天我们学习类加载器,关于类加载器其实和JVM有很大关系,在这里这篇文章只是简单的介绍下类加载器,后面学习到JVM的时候还会详细讲到类加载器,本文分为下面几个小节讲解: 一.认识类加载器 1.什么是类 ...
- Java自定义类加载和ClassPath类加载器
1 自定义类加载器: 实现规则: 自定义类加载器,需要重写findClass,然后通过调用loadClass进行类加载(loadClass通过递归实现类的双亲委派加载) package com.dax ...
- Java虚拟机14:类加载器
类与类加载器 虚拟机设计团队把类加载阶段张的"通过一个类的全限定名来获取此类的二进制字节流"这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类.实现这 ...
- 进阶Java编程(11)ClassLoader类加载器【待完成】
1,ClassLoader类加载器简介 在Java里面提供一个系统的环境变量:ClassPath,这个属性的作用主要是在JVM进程启动的时候进行类加载路径的定义,在JVM里面可以根据类加载器而后进行指 ...
- 深入理解java:1.1. 类加载器
从java的动态性到类加载机制 我们知道,Java是一种动态语言. 那么怎样理解这个“动态”呢? 或者说一门语言具备了什么特性,才能称之为动态语言呢? 对于java,我是这样理解的. 我们都知道J ...
随机推荐
- python cookbook第三版学习笔记十七:委托属性
我们想在访问实例的属性时能够将其委托到一个内部持有的对象上,这经常用到代理机制上 class A: def spam(self,x): print("class_A: ...
- 简易bootloader重定位问题
单板选择NandFlash启动,则硬件上电后,系统会自己主动将NandFlash中的前4K内容复制到STEPSTONE即4K SRAM中.然后从SRAM中的0X0地址启动. 基于mini ...
- ABAP下载服务器文件到本机
转自http://blog.sina.com.cn/s/blog_701594f40100l8ml.html ABAP:下载服务器文件到本机 对服务器的文件进行读写操作,SAP提供了OPEN DATA ...
- JavaWeb:前端开发基础
JavaWeb:前端开发基础 内联元素和块级元素 说明: 联元素和块级元素都是html中的范畴,块元素和内联元素的主要差异是块元素是从新的一行开始.而内联元素一般显示在一行上.但是可以通过css的di ...
- pandas to_datetime()
>>> import pandas as pd >>> i = pd.date_range() >>> df = pd.DataFrame(dic ...
- Data Structure Stack: Reverse a stack using recursion
http://www.geeksforgeeks.org/reverse-a-stack-using-recursion/ #include <iostream> #include < ...
- ubuntu配置jdk环境
简单记录如下: 1. 下载JDK并解压后,复制到想要放置的目录,本文以“/usr/lib/jdk”为例: 2. 编辑配置文件,可以是“/etc/profile”或者“~/.bashrc”. 输入命令“ ...
- spring项目命名
groupId 一般分为多个段,最简单的分两段,第一段为域,第二段为公司名称.域又分为org.com.cn等等许多, 举个apache公司的tomcat项目例子:这个项目的groupId是org.ap ...
- 解决COMODO Internet Security更新慢或失败的问题
方法一: COMODO 默认更新服务器只有一个:http://download.comodo.com/ 我电信网络更新速度非常慢: 实在没有办法了,找到了几个更新服务器: http://eu1.dow ...
- EntityFramework 学习 一 Spatial Data type support in Entity Framework 5.0
MS SQl Server引进两种特殊的数据类型geography and geometry public partial class Course { public Course() { this. ...