java自定义类加载器代码示例

如果要使用自定义类加载器加载class文件,就需要继承java.lang.ClassLoader类。

ClassLoader有几个重要的方法:

protectedClassLoader(ClassLoaderparent):使用指定的、用于委托操作的父类加载器创建新的类加载器。

protectedfinalClass<?>defineClass(Stringname,byte[]b,intoff,intlen):将一个byte数组转换为Class类的实例。

protectedClass<?>findClass(Stringname):使用指定的二进制名称查找类。

publicClass<?>loadClass(Stringname):使用指定的二进制名称来加载类。

protectedfinalClass<?>findLoadedClass(Stringname):如果Java虚拟机已将此加载器记录为具有给定二进制名称的某个类的启动加载器,则返回该二进制名称的类。否则,返回null。

publicfinalClassLoadergetParent():返回委托的父类加载器。

protectedfinalvoidresolveClass(Class<?>c):链接指定的类。

如果要遵循双亲委派模型,则重写findClass(Stringname)方法;如果不想遵循双亲委派模型,则直接重写loadClass(Stringname)方法。

自定义遵循双亲委派模型的类加载器

ParentsDelegateClassLoader.java

package com.zzj.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
/**
 * 双亲委派类加载器,重写findClass(name)方法
 *
 * @author Administrator
 *
 */
public class ParentsDelegateClassLoader extends ClassLoader {
	private static final String EXT = ".class";
	private String path;
	public ParentsDelegateClassLoader() {
		path = this.getResource("").getPath();
	}
	public ParentsDelegateClassLoader(String path) {
		this.path = path;
	}
	@Override
	  protected Class<?> findClass(String name) throws ClassNotFoundException {
		byte[] b = null;
		try {
			b = loadClassFile(name);
		}
		catch (IOException e) {
			e.printStackTrace();
		}
		return this.defineClass(name, b, 0, b.length);
	}
	private byte[] loadClassFile(String name) throws IOException {
		String classFile = getClassFile(name);
		System.out.println("即将加载class文件" + classFile);
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		InputStream input = new FileInputStream(classFile);
		int count;
		byte[] temp = new byte[1024];
		while ((count = input.read(temp)) > -1) {
			out.write(temp, 0, count);
		}
		out.close();
		input.close();
		return out.toByteArray();
	}
	private String getClassFile(String name) {
		String pathName = name.replace(".", File.separator);
		if (path.endsWith("/") || path.endsWith("\\")) {
			return path + pathName + EXT;
		}
		return path + File.separator + pathName + EXT;
	}
}

现在类路径classpath下和F:\\ClassloaderTest\\bin目录下都有一个类文件com\zzj\classloader\User.class,包名为com.zzj.classloader,使用类加载器ParentsDelegateClassLoader加载F:\\ClassloaderTest\\bin下的类。

package com.zzj.classloader;
public class App {
	private static final String path = "F:\\ClassloaderTest\\bin";
	private static final String classname = "com.zzj.classloader.User";
	public static void main(String[] args) throws Exception {
		ParentsDelegateClassLoader classLoader = new ParentsDelegateClassLoader(path);
		Class<?> clazz = classLoader.loadClass(classname);
		System.out.println(clazz);
		System.out.println(clazz.getClassLoader());
	}
}

输出:

class com.zzj.classloader.User
sun.misc.Launcher$AppClassLoader@19821f 

User类的加载器是系统类加载器AppClassLoader,而不是我们自己定义的类加载。实际上被加载不是F:\\ClassloaderTest\\bin下的类,而是classpath下的类。这就是双亲委派模型:当ParentsDelegateClassLoader加载器接收到加载请求后,会先委托给父类加载器,如果父类加载器加载成功,则返回一个Class对象。如果加载失败,才会让接收到加载请求的类加载器加载。

把classpath下的User类删掉测试运行:

即将加载class文件F:\ClassloaderTest\bin\com\zzj\classloader\User.class
class com.zzj.classloader.User
com.zzj.classloader.ParentsDelegateClassLoader@61de33 

此时User类的加载为ParentsDelegateClassLoader。

这一点可以从ClassLoader的源码中得到验证:

public Class<?> loadClass(String name) throws ClassNotFoundException {
  return loadClass(name, false);
  } 

调用了重载方法:

protected synchronized Class<?> loadClass(String name, boolean resolve)
  throws ClassNotFoundException
  {
  // 先判断该类是否已被当前的类加载器加载
  Class c = findLoadedClass(name);
  if (c == null) {
    try {
    if (parent != null) {// 如果存在父类加载器,则委派给父类加载
      c = parent.loadClass(name, false);
    } else {// 如果父类加载为空,则其父类加载器为引导类加载器
      c = findBootstrapClass0(name);
    }
    } catch (ClassNotFoundException e) {
      // 如果父类加载器加载失败,则自己加载,调用的就是findClass方法!
      c = findClass(name);
    }
  }
  if (resolve) {
    resolveClass(c);
  }
  return c;
  } 

可见,如果想要破坏双亲委派模型,可以直接重写loadClass(Stringname)方法。

自定义不遵循双亲委派模型的类加载器

NotParentsDelegateClassLoader.java

package com.zzj.classloader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
/**
 * 非双亲委派类加载器,重写loadClass(name)方法
 *
 * @author Administrator
 *
 */
public class NotParentsDelegateClassLoader extends ClassLoader {
	private static final String EXT = ".class";
	private String path;
	public NotParentsDelegateClassLoader() {
		path = this.getResource("").getPath();
	}
	public NotParentsDelegateClassLoader(String path) {
		this.path = path;
	}
	@Override
	  public Class<?> loadClass(String name) throws ClassNotFoundException {
		byte[] b = null;
		try {
			b = loadClassFile(name);
		}
		catch (FileNotFoundException e) {
			System.err.println("加载器" + this.getClass().getName()
			          + "没有找到class文件" + name + ",将委派给父类加载器!");
			// 委派给父类加载器
			return getClass().getClassLoader().loadClass(name);
		}
		catch (IOException e) {
			System.err.println("加载器" + this.getClass().getName() + "加载class文件"
			          + name + "失败,将委派给父类加载器!");
			// 委派给父类加载器
			return getClass().getClassLoader().loadClass(name);
		}
		// 检查该类是否被当前类加载器加载过(只检查当前类加载器,不会检查父类加载器)
		Class<?> clazz = findLoadedClass(name);
		if (clazz != null) {
			System.out.println("类" + name + "已被加载过!");
			return clazz;
		} else {
			System.out.println("类" + name + "尚未被加载!");
		}
		return this.defineClass(name, b, 0, b.length);
	}
	private byte[] loadClassFile(String name) throws IOException,
	      FileNotFoundException {
		String classFile = getClassFile(name);
		System.out.println("即将加载class文件" + classFile);
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		InputStream input = new FileInputStream(classFile);
		int count;
		byte[] temp = new byte[1024];
		while ((count = input.read(temp)) > -1) {
			out.write(temp, 0, count);
		}
		out.close();
		input.close();
		return out.toByteArray();
	}
	private String getClassFile(String name) {
		String pathName = name.replace(".", File.separator);
		if (path.endsWith("/") || path.endsWith("\\")) {
			return path + pathName + EXT;
		}
		return path + File.separator + pathName + EXT;
	}
}

现在类路径classpath下有一个类文件com\zzj\classloader\User.class,包名为com.zzj.classloader,使用类加载器NotParentsDelegateClassLoader加载User类。

package com.zzj.classloader;
public class App2 {
	private static final String className = "com.zzj.classloader.User";
	public static void main(String[] args) throws Exception {
		NotParentsDelegateClassLoader classLoader = new NotParentsDelegateClassLoader();
		Class<?> clazz = classLoader.loadClass(className);
		System.out.println(clazz);
		System.out.println(clazz.getClassLoader());
	}
}

输出:

即将加载class文件/E:/Myeclipse/zzjtest/WebRoot/WEB-INF/classes/com\zzj\classloader\User.class
类com.zzj.classloader.User尚未被加载!
即将加载class文件/E:/Myeclipse/zzjtest/WebRoot/WEB-INF/classes/java\lang\Object.class
class com.zzj.classloader.User
com.zzj.classloader.NotParentsDelegateClassLoader@61de33
加载器com.zzj.classloader.NotParentsDelegateClassLoader没有找到class文件java.lang.Object,将委派给父类加载器! 

此时User类的加载器是NotParentsDelegateClassLoader,没有先委托给父类,只有加载失败才会委托给父类加载器,正好跟双亲委派模型是反的。

当然,即使加载失败,也可以不委托给父类加载器,而指定其他的类加载器,从而可以构建更加复杂的网状模型的类加载机制

总结

以上就是本文关于java自定义类加载器代码示例的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

(0)

相关推荐

  • java基础学习笔记之类加载器

    类加载器 java类加载器就是在运行时在JVM中动态地加载所需的类,java类加载器基于三个机制:委托,可见,单一. 把classpath下的那些.class文件加载进内存,处理后成为字节码,这些工作是类加载器做的. 委托机制指的是将加载类的请求传递给父加载器,如果父加载器找不到或者不能加载这个类,那么再加载他. 可见性机制指的是父加载器加载的类都能被子加载器看见,但是子加载器加载的类父加载器是看不见的. 单一性机制指的是一个类只能被同一种加载器加载一次. 默认类加载器 系统默认三个类加载器:

  • java 详解类加载器的双亲委派及打破双亲委派

    java 详解类加载器的双亲委派及打破双亲委派 一般的场景中使用Java默认的类加载器即可,但有时为了达到某种目的又不得不实现自己的类加载器,例如为了达到类库的互相隔离,例如为了达到热部署重加载功能.这时就需要自己定义类加载器,每个类加载器加载各自的类库资源,以此达到资源隔离效果.在对资源的加载上可以沿用双亲委派机制,也可以打破双亲委派机制. 一.沿用双亲委派机制自定义类加载器很简单,只需继承ClassLoader类并重写findClass方法即可.如下例子: ①先定义一个待加载的类Test,它

  • java类加载器和类反射使用示例

    一.一个命令对应一个进程. 当我们启动一个Java程序,即启动一个main方法时,都将启动一个Java虚拟机进程,不管这个进程有多么复杂.而不同的JVM进程之间是不会相互影响的.这也就是为什么说,Java程序只有一个入口--main方法,让虚拟机调用.而两个mian方法,对应的是2个JVM进程,启动的是两个不同的类加载器,操作的实际上是不同的类.故而不会互相影响. 二.类加载. 当我们使用一个类,如果这个类还未加载到内存中,系统会通过加载.连接.初始化对类进行初始化. 1.类加载:指的是将类的c

  • JAVA提高第七篇 类加载器解析

    今天我们学习类加载器,关于类加载器其实和JVM有很大关系,在这里这篇文章只是简单的介绍下类加载器,后面学习到JVM的时候还会详细讲到类加载器,本文分为下面几个小节讲解: 一.认识类加载器 1.什么是类加载器? 所谓的类加载器可以从其作用来理解,其功能就是将classpath目录下.class文件,加载到内存中来进行一些处理,处理完的结果就是一些字节码.那是谁把这些class类加载到内存中来的呢?就是类加载器. 2.JVM中默认的类加载器有哪些? java虚拟机中可以安装多个类加载器,系统默认三个

  • java 类加载与自定义类加载器详解

    类加载 所有类加载器,都是ClassLoader的子类. 类加载器永远以.class运行的目录为准. 读取classpath根目录下的文件有以下几种方式: 1 在Java项目中可以通过以下方式获取classspath下的文件: public void abc(){ //每一种读取方法,使用某个类获取Appclassloader ClassLoader cl = ReadFile.class.getClassLoader(); URL url = cl.getResource("a.txt&quo

  • Java中的类加载器_动力节点Java学院整理

    从java的动态性到类加载机制 Java是一种动态语言.那么怎样理解这个"动态"呢?或者说一门语言具备了什么特性,才能称之为动态语言呢?对于java,我是这样理解的. JVM(java虚拟机)执行的不是本地机器码指令,而是执行一种称之为字节码的指令(存在于class文件中).这就要求虚拟机在真正执行字节码之前,先把相关的class文件加载到内存中.虚拟机不是一次性加载所有需要的class文件,因为它在执行的时候根本不会知道以后会用到哪些class文件.它是每用到一个类,就会在运行时&q

  • classloader类加载器_基于java类的加载方式详解

    基础概念 Classloader 类加载器,用来加载 Java 类到 Java 虚拟机中.与普通程序不同的是.Java程序(class文件)并不是本地的可执行程序.当运行Java程序时,首先运行JVM(Java虚拟机),然后再把Java class加载到JVM里头运行,负责加载Java class的这部分就叫做Class Loader. JVM本身包含了一个ClassLoader称为Bootstrap ClassLoader,和JVM一样,BootstrapClassLoader是用本地代码实现

  • 深入解析Java中的Class Loader类加载器

    类加载的过程 类加载器的主要工作就是把类文件加载到JVM中.如下图所示,其过程分为三步: 1.加载:定位要加载的类文件,并将其字节流装载到JVM中: 2.链接:给要加载的类分配最基本的内存结构保存其信息,比如属性,方法以及引用的类.在该阶段,该类还处于不可用状态: (1)验证:对加载的字节流进行验证,比如格式上的,安全方面的: (2)内存分配:为该类准备内存空间来表示其属性,方法以及引用的类: (3)解析:加载该类所引用的其它类,比如父类,实现的接口等. 3.初始化:对类变量进行赋值. 类加载器

  • java自定义类加载器代码示例

    如果要使用自定义类加载器加载class文件,就需要继承java.lang.ClassLoader类. ClassLoader有几个重要的方法: protectedClassLoader(ClassLoaderparent):使用指定的.用于委托操作的父类加载器创建新的类加载器. protectedfinalClass<?>defineClass(Stringname,byte[]b,intoff,intlen):将一个byte数组转换为Class类的实例. protectedClass<

  • java自定义类加载器如何实现类隔离

    目录 自定义类加载器 准备 通过URLClassLoader来实现[推荐] 通过继承ClassLoader实现 网上java自定义类加载器很多容易找到,但是都是加载的单个类,如果被加载的类,有引用了其他类怎么办呢?接下来看一下如何来处理这种情况 有时候一个项目中可能会引用不同版本的第三方依赖,比如笔者在升级hbase系统时,代理层就同时用到了1.X和2.X版本的hbase-client的jar包. 当时是使用的阿里的SOFAArk来实现的. 它的本质就是是哟个类加载来实现的,接下来就通过一个小例

  • 浅谈Java自定义类加载器及JVM自带的类加载器之间的交互关系

    JVM自带的类加载器: 其关系如下: 其中,类加载器在加载类的时候是使用了所谓的"父委托"机制.其中,除了根类加载器以外,其他的类加载器都有且只有一个父类加载器. 关于父委托机制的说明: 当生成 一个自定义的类加载器实例时,如果没有指定它的父加载器,那么系统类加载器将成为该类加载器的父类加载器 下面,自定义类加载器.自定义的类加载器必须继承java.lang.ClassLoader类 import java.io.*; public class MyClassLoader extend

  • Java语言中的自定义类加载器实例解析

    本文研究的主要是Java语言中的自定义类加载器实例解析的相关内容,具体如下. 自己写的类加载器 需要注意的是:如果想要对这个实例进行测试的话,首先需要在c盘建立一个c://myjava的目录.然后将相应的java文件放在这个目录中.并将产生的.clas文件放在c://myjava/com/lg.test目录下,否则是找不到的.这是要注意的.. class FileClassLoader : package com.lg.test; import java.io.ByteArrayOutputSt

  • Java基础之自定义类加载器

    一.类加载器关系 自定义类加载器 创建一个类继承ClassLoader类,同时重写findClass方法,用于判断当前类的class文件是否已被加载 二.基于本地class文件的自定义类加载器 本地class文件路径 自定义类加载器: //创建自定义加载器类继承ClassLoader类 public class MyClassLoader extends ClassLoader{ // 包路径 private String Path; // 构造方法,用于初始化Path属性 public MyC

  • Java实现的自定义类加载器示例

    本文实例讲述了Java实现的自定义类加载器.分享给大家供大家参考,具体如下: 一 点睛 1 ClassLoader类有如下两个关键方法: loadClass(String name, boolean resolve):该方法为ClassLoader的入口点,根据指定的二进制名称来加载类,系统就是调用ClassLoader的该方法来获取指定类对应的Class对象. findClass(String name):根据二进制名称来查找类. 如果需要实现自定义的ClassLoader,可以通过重写以上两

  • Java基于自定义类加载器实现热部署过程解析

    热部署: 热部署就是在不重启应用的情况下,当类的定义即字节码文件修改后,能够替换该Class创建的对象.一般情况下,类的加载都是由系统自带的类加载器完成,且对于同一个全限定名的java类,只能被加载一次,而且无法被卸载.可以使用自定义的 ClassLoader 替换系统的加载器,创建一个新的 ClassLoader,再用它加载 Class,得到的 Class 对象就是新的(因为不是同一个类加载器),再用该 Class 对象创建一个实例,从而实现动态更新.如:修改 JSP 文件即生效,就是利用自定

  • java类加载机制、类加载器、自定义类加载器的案例

    类加载机制 java类从被加载到JVM到卸载出JVM,整个生命周期包括:加载(Loading).验证(Verification).准备(Preparation).解析(Resolution).初始化(Initialization).使用(using).和卸载(Unloading)七个阶段. 其中验证.准备和解析三个部分统称为连接(Linking). 1.加载 加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Clas

  • Java通过自定义类加载器实现类隔离

    目录 前言 类隔离是什么 使用场景 解决方案 重写findClass 重写loadClass 总结 前言 由于微服务的快速迭代.持续集成等特性,越来越多的团队更倾向于它.但是也体现出了一些问题,比如在基础设施建设过程中,需要把通用功能下沉,把现有大而全的基础设施按领域拆分,考虑需要兼容现有生产服务,会产生不同的依赖版本,有时不注意就可以引发问题.比如本文遇到的依赖包版本冲突问题,以及如何利用类隔离技术解决的分析. 类隔离是什么 类隔离是一种通过类加载器实现加载所需类的实现方式,使得不同版本类间隔

随机推荐