ClassLoader类加载机制

类加载流程

Class1,Class2-> MyClassLoader1, MyClassLoader2, MyThreadClassLoader1,MyThreadClassLoader2 -> AppClassLoader -> ExtClassLoader -> BootStrap

 

每个类加载器方法流程

loadClass-> findClass -> defineClass(C++)

 

名称

描述

实现

Bootstrap

JVM内核,加载Jre/lib/rt.jar

C++,BootStrap

Extension

加载Jre/lib/ext/*.jar

Java,ExtClassLoader

Application

加载classpath指定位置,系统或应用类加载

Java,AppClassLoader

Custom

自定义类的加载器,加载自定义位置class

Java

Custom Thread

自定义线程类加载器

Java

 

Launcher$ExtClassLoader和Launcher$AppClassLoader都是URLClassLoader的子类。

父类委托机制:一个类需要被加载的时候,JVM先会调用他的父类加载器进行加载(父类加载器,不一定他们之间是继承的关系,有可能仅仅是包装的关系)。如果父类加载器加载不了,再使用其子类进行加载。如果所有父类加载器都无法加载,再通过用户自定义的findClass方法进行加载。

JVM中类用全名和ClassLoader的实例作为唯一标识(包名,类名,ClassLoader实例名).


注:不要重写loadClass方法,如果重写了loadClass方法,就会导致jvm使用MyClassLoader来加载Object、String等等一些类。


<例子>

Animal.java

package com.test;

public class Animal {

	public void say(){
        System.out.println("hello world 2~~~");
    }
	
}

ClassLoaderTest02.java

package com.test;

import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class ClassLoaderTest02 extends ClassLoader {

	private String path; // 加载类的路径
	private final String fileType = ".class"; // .class文件扩展名

	public ClassLoaderTest02(){
	}
	public ClassLoaderTest02(ClassLoader parent){
		super(parent);
	}
	public void setPath(String path) {
		this.path = path;
	}

	/**
	 * 读取class文件作为二进制流放入到byte数组中去
	 * 
	 * @param name
	 * @return
	 */
	private byte[] loadClassData(String name) {
		System.out.println("loadClassData~~~");
		InputStream in = null;
		byte[] data = null;
		ByteArrayOutputStream baos = null;

		try {
			name = name.replace(".", "\\");
			in = new BufferedInputStream(new FileInputStream(new File(path
					+ name + fileType)));
			baos = new ByteArrayOutputStream();
			int ch = 0;
			while (-1 != (ch = in.read())) {
				baos.write(ch);
			}
			data = baos.toByteArray();
		} catch (Exception e) {

			e.printStackTrace();
		} finally {
			try {
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				try {
					baos.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
		return data;
	}

	/**
	 * 定义class对应的命名空间
	 */
	@Override
	protected Class<?> findClass(String name) throws ClassNotFoundException {
		System.out.println("findClass~~~");
		byte[] data = this.loadClassData(name);
		return this.defineClass(name, data, 0, data.length);
	}

	public static void main(String[] args) throws Exception {
		ClassLoaderTest02 loader1 = new ClassLoaderTest02();
		ClassLoaderTest02 loader2 = new ClassLoaderTest02(loader1);
		// test01(loader1);  // getParent~~~:com.test.AppClassLoader
		// test01(loader2);  // getParent~~~:com.test.ClassLoaderTest02
		// test02(loader1);  // 自定義, loadClass > findClass > defineClass
		// test03(loader1);  // 自定義, findClass > defineClass
		// test04(loader1);  // 加载不同版本同名jar包
		test05(loader1);  // web环境
	}

	/**
	 * 正常使用AppClassLoader
	 */
	public static void test01(ClassLoaderTest02 loader) throws Exception {
		loader.setPath("D://workspace//java//javaTest" + "//bin//");
		System.out.println("classLoader~~~:" + 
			ClassLoaderTest02.class.getClassLoader().getClass().getName());
		System.out.println("getParent~~~:" + 
			loader.getParent().getClass().getName());
		Class<?> clazz = loader.loadClass("com.test.Animal"); // AppClassLoader
		System.out.println("clazz~~~:"
				+ clazz.getClassLoader().getClass().getName());
		// System.out.println("Animal~~~:" + Animal.class.getClassLoader());
		// Animal animal=(Animal) clazz.newInstance();
		// animal.say();
		Object animalObj = clazz.newInstance();
		Method animalSay = clazz.getMethod("say");
		animalSay.invoke(animalObj);
	}
	
	/**
	 * 使用自定義ClassLoader
	 * 注:(1)需要先屏蔽classpath下的Animal.class(如重命名);(2)copy .class到其它目錄(如D:\temp1)
	 */
	public static void test02(ClassLoaderTest02 loader) throws Exception {
		loader.setPath("D://temp1//");
		System.out.println("classLoader~~~:" + 
			ClassLoaderTest02.class.getClassLoader().getClass().getName());
		System.out.println("getParent~~~:" + 
			loader.getParent().getClass().getName());
		Class<?> clazz = loader.loadClass("com.test.Animal"); // ClassLoaderTest02
		System.out.println("clazz~~~:" + 
			clazz.getClassLoader().getClass().getName());
		Object animalObj = clazz.newInstance(); // classloader不一致, 通过reflect调用
		Method animalSay = clazz.getMethod("say");
		animalSay.invoke(animalObj);
	}
	
	/**
	 * 使用自定義ClassLoader
	 * 注:(1)不需要屏蔽classpath下的Animal.class;(2)copy .class到其它目錄(如D:\temp1)
	 */
	public static void test03(ClassLoaderTest02 loader) throws Exception {
		loader.setPath("D://temp1//");
		System.out.println("classLoader~~~:" + 
			ClassLoaderTest02.class.getClassLoader().getClass().getName());
		System.out.println("getParent~~~:" + 
			loader.getParent().getClass().getName());
		Class<?> clazz = loader.findClass("com.test.Animal"); // 直接调用
		System.out.println("clazz~~~:" + 
			clazz.getClassLoader().getClass().getName());
		Object animalObj = clazz.newInstance(); // classloader不一致, 通过reflect调用
		Method animalSay = clazz.getMethod("say");
		animalSay.invoke(animalObj);
	}
	
	/**
	 * 使用自定義ClassLoader,加载不同版本同名jar包
	 * 注:(1)需要先屏蔽classpath下的Animal.class(如重命名);(2)copy .class到其它目錄(如\jar)
	 */
	public static void test04(ClassLoaderTest02 loader) throws Exception {
		URL url1 = new URL("file:D://workspace//jar//Animal-0.0.1.jar");  // 输出为"hello world 1!"
		URL url2 = new URL("file:D://workspace//jar//Animal-0.0.2.jar");  // 输出为"hello world 2~~~"
		URLClassLoader myClassLoader1  =   new  URLClassLoader( new  URL[]   { url1 } );
		URLClassLoader myClassLoader2  =   new  URLClassLoader( new  URL[]   { url2 } );
		System.out.println("myClassLoader1~~~" + myClassLoader1.getClass());
		Class<?> clazz1  =  myClassLoader1.loadClass("com.test.Animal");
		Class<?> clazz2  =  myClassLoader2.loadClass("com.test.Animal");
		Object animalObj1 = clazz1.newInstance();
		Object animalObj2 = clazz2.newInstance();
		Method animalSay1 = clazz1.getMethod("say");
		Method animalSay2 = clazz2.getMethod("say");
		animalSay1.invoke(animalObj1);
		animalSay2.invoke(animalObj2);
	}
	
	/**
	 * web环境
	 * JVM中类用全名和ClassLoader的实例作为唯一标识
	 * 参考:CSDN -> "Peter_K的专栏" -> "ClassLoader解决jar包冲突问题"
	 */
	public static void test05(ClassLoaderTest02 loader) throws Exception {
		URL url1 = new URL("file:D://workspace//jar//Animal-0.0.1.jar");
		System.out.println("ContextClassLoader~~~" + Thread.currentThread().getContextClassLoader());
		System.out.println("ContextClassLoader-getParent~~~" + 
			Thread.currentThread().getContextClassLoader().getParent()); 
		URLClassLoader myClassLoader1  =   new  URLClassLoader( new  URL[]   { url1 } , 
			Thread.currentThread().getContextClassLoader());  // 当前线程类加载器
		System.out.println("myClassLoader1~~~" + myClassLoader1.getClass());
		Class<?> clazz1  =  myClassLoader1.loadClass("com.test.Animal");
		Object animalObj1 = clazz1.newInstance();
		Method animalSay1 = clazz1.getMethod("say");
		animalSay1.invoke(animalObj1);
	}
	
	/*
	 * 更多参考:
	 * 1)ITEye -> "classloader 加载同名类问题"
	 * 2)http://blog.csdn.net/is_zhoufeng/article/details/26602689
	 */
}

文件位置






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值