ClassLoader

ClassLoader本身是一个Abstract Class,我们可以扩展ClassLoader的实现特定的load需求。
java应用环境中不同的class分别由不同的ClassLoader负责加载。
一个jvm中默认的classloader有Bootstrap ClassLoader、Extension ClassLoader、App ClassLoader,分别各司其职:

Bootstrap ClassLoader     负责加载java基础类,主要是 %JRE_HOME/lib/ 目录下的rt.jar、resources.jar、charsets.jar和class等

Extension ClassLoader     负责加载java扩展类,主要是 %JRE_HOME/lib/ext 目录下的jar和class
App ClassLoader           负责加载当前java应用的classpath中的所有类。 


加载机制:

出于代码安全性考虑,ClassLoader采用的是双亲委托的加载模式,否则用户会出现用户自定义的ClassLoader加载的类会覆盖jvm加载的类。图解:


classloader 加载类用的是全盘负责委托机制。所谓全盘负责,即是当一个classloader加载一个Class的时候,这个Class所依赖的和引用的所有 Class也由这个classloader负责载入,除非是显式的使用另外一个classloader载入。
所以,当我们自定义的classloader加载成功了com.company.MyClass以后,MyClass里所有依赖的class都由这个classLoader来加载完成。


正常的双亲委派模型中,下层的类加载器可以使用上层父加载器加载的对象,但是上层父类的加载器不可以使用子类加载的对象。
而有些时候程序的确需要上层调用下层,这时候就需要ContextClassLoader 线程上下文加载器来处理。使用线程上下文类加载器, 可以在执行线程中, 抛弃双亲委派加载链模式, 使用线程上下文里的类加载器加载类。

以JNDI为例,它的核心是由JRE核心类(rt.jar)实现的。但这些核心JNDI类必须能加载由第三方厂商提供的JNDI实现。
解决办法就是让核心JNDI类使用线程上下文类加载器,从而有效的打通类加载器层次结构,逆着代理机制的方向使用类加载器。

线程上下文从根本解决了一般应用不能违背双亲委派模式的问题,使java类加载体系显得更灵活.使用线程上下文加载类。线程将会从它的父线程中继承上下文类加载器。如果你在整个应用中不做任何设置,所有线程将以系统类加载器作为它们自己的上下文加载器。
也要注意, 保证多根需要通信的线程间的类加载器应该是同一个,防止因为不同的类加载器, 导致类型转换异常(ClassCastException)。
如果多个JVM线程通过共享数据通信时这将造成一个非常混乱的类加载图景,除非他们都使用同一个上下文加载器实例。

同时ClassLoader也充当了namespace的角色,不同的ClassLoader加载的同一个class也是不同的,相互之间做类型转换是要抛 castException的。
注意ClassLoader对findClass的定义包含synchronized关键字,保证在多线程环境下,多个线程不会在读入同一个Class,以免造成互锁的情况。

ClassLoader加载定义实例

ClassLoader定义的用于加载类的方法,一般我们只需要实现findClass:
findClass(String name)   
loadClass(String name)   
protected final Class defineClass(String name, byte[] b, int off, int len) throws ClassFormatError  

每一个JVM环境中,都有一个默认的ClassLoader,我们可以通过
ClassLoader.getSystemClassLoader()
方法获得默认的ClassLoader(AppClassLoader): 

我们可以通过loadClass方法来加载名为className的类: 

Class clazz = ClassLoader.loadClass("MyClass");  

然后调用Class的newInstance创建这个类的实例:
MyClass myClass = (MyClass) clazz.newInstance(); 

不同的 classLoader 之间怎么相互通信,调用彼此的服务实现呢?如果可以尽量是在同一个classloader下加载要用到的类,使用反射的方式来调用类的实例是一种方式。

虽然我们用不同的ClassLoader加载的同一个Class,不能相互Cast,但是我们可以坚持全部代码使用反射方式进行调用。


附例:

工程A里面定义Product接口,和接口实现ProductImpl;

工程B里面定义SimpleClassLoader,和主函数测试入口类。

package com.study.classloader;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;

public class SimpleClassLoader extends ClassLoader {
	String[] dirs;

	public SimpleClassLoader(String path) {
		dirs = path.split(System.getProperty("path.separator"));
		String[] _dirs = dirs.clone();
		for (String dir : _dirs) {
			extendClasspath(dir);
		}
	}

	public void extendClasspath(String path) {
		String[] segments = path.split("/");
		String[] exDirs = new String[segments.length];
		for (int i = 0; i < (segments.length); i++) {
			exDirs[i] = popd(segments, i);
		}

		String[] newDirs = new String[dirs.length + exDirs.length];
		System.arraycopy(dirs, 0, newDirs, 0, dirs.length);
		System.arraycopy(exDirs, 0, newDirs, dirs.length, exDirs.length);
		dirs = newDirs;
	}

	private String popd(String[] pathSegments, int level) {
		StringBuffer path = new StringBuffer();
		for (int i = 0; i < level; i++) {
			path.append(pathSegments[i]).append("/");
		}
		return path.toString();
	}

	public String[] getDirs() {
		return dirs;
	}

	public synchronized Class<?> findClass(String name) throws ClassNotFoundException {

		for (String dir : dirs) {
			byte[] buf = getClassData(dir, name);
			if (buf != null) {
				System.out.println("Loaded '" + name + "' from: " + dir);
				return defineClass(name, buf, 0, buf.length);
			}
		}
		throw new ClassNotFoundException();
	}

	protected byte[] getClassData(String directory, String name) {
		String[] tokens = name.split("\\.");
		String classFile = directory + "/" + tokens[tokens.length - 1]
				+ ".class";
		File f = (new File(classFile));
		int classSize = (new Long(f.length())).intValue();
		byte[] buf = new byte[classSize];
		try {
			FileInputStream filein = new FileInputStream(classFile);
			classSize = filein.read(buf);
			filein.close();
		} catch (FileNotFoundException e) {
			return null;
		} catch (IOException e) {
			return null;
		}catch (Exception e) {
			return null;
		}
		return buf;
	}

}


package com.study.classloader;

import java.lang.reflect.Method;

public class ClassLoaderDemo {
	public static void main(String args[]){  
        ClassLoader cl = new SimpleClassLoader("D:/work/workspace_bak/Test/bin/com/study/classloader/impl");
        ClassLoader cl_1 = new SimpleClassLoader("D:/work/workspace_bak/Test/bin/com/study/classloader/impl");  
        
        try {
			Class clazz = cl.loadClass("com.study.classloader.impl.ProductImpl");
			Class clazz_1 = cl_1.loadClass("com.study.classloader.impl.ProductImpl"); 
			
			// 查看各自己使用的ClassLoader  
			System.out.println(clazz.getClassLoader());  
			System.out.println(clazz_1.getClassLoader());  
			  
			// 看看JVM是否认为clazz和clazz_1是同一个Class  
			System.out.println("clazz & clazz_1 is the same Class ? "+(clazz == clazz_1));  
			
			//用反射的方式去执行方法
			Object obj = clazz.newInstance();
			Method[] mthds = clazz.getDeclaredMethods();
			for (Method mthd : mthds) {
				String methodName = mthd.getName();
				//excute method
				String arg = "iphone5S";
				mthd.invoke(obj, arg);
				System.out.println("mthd.name=" + methodName);
				
			}
			
			System.out.println("obj.class=" + obj.getClass().getName());
			System.out.println("obj.class=" + clazz.getClassLoader().toString());
			System.out.println("obj.class="+clazz.getClassLoader().getParent().toString());
			
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}  
    }  
}


实例加载实现jar里面的main

package com.study.classloader;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.jar.Attributes;

public class JarClassLoaderDemoRun {
	/**
	 * 根据URL找到该Jar的Main方法类名.
	 * 
	 * @param url
	 *            Jar的Url:Demo.jar & HelloDemo.jar
	 * @return String
	 * @throws IOException
	 */
	public static String getMainClassName(URL url) throws IOException {
		JarURLConnection uc = (JarURLConnection) url.openConnection();
		Attributes attr = uc.getMainAttributes();
		return attr != null ? attr.getValue(Attributes.Name.MAIN_CLASS) : null;
	}

	/**
	 * 通过反射执行类Main方法.
	 * 
	 * @param classLoader
	 *            自定义的ClassLoader
	 * @param name
	 *            Jar的Main方法名
	 * @param args
	 *            Main方法参数
	 * @throws ClassNotFoundException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 */
	public static void invokeClass(ClassLoader classLoader, String name,
			String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException {
		Class c = classLoader.loadClass(name);
		Method m = c.getMethod("main", new Class[] { args.getClass() });
		m.setAccessible(true);
		int mods = m.getModifiers();
		if (m.getReturnType() != void.class || !Modifier.isStatic(mods) || !Modifier.isPublic(mods)) {
			throw new NoSuchMethodException("main");
		}
		try {
			m.invoke(null, new Object[] { new String[] {} });
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		try {
			System.out.println("ClassLoaderRun ClassLoader:" + JarClassLoaderDemoRun.class.getClassLoader());
			ClassLoader cl = Thread.currentThread().getContextClassLoader();
			URL[] urls = new URL[] { new URL("file:/C:/Demo.jar"),
						 			 new URL("file:/D:/study/code/HelloDemo.jar") };
			// 自定义ClassLoader,通过指定URL加载Jar
			URLClassLoader classLoader = new URLClassLoader(urls, cl);
			Thread.currentThread().setContextClassLoader(classLoader);
			// 程序入口取得Jar中Main方法类名
			String mainClass = getMainClassName(new URL("jar:file:/D:/study/code/HelloDemo.jar!/"));
			invokeClass(classLoader, mainClass, new String[] {});
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}
}


refer: http://weli.iteye.com/blog/1682625



  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值