`
DLevin
  • 浏览: 36766 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

扩展ClassLoader

阅读更多

在上文Java中的ClassLoader中,已经对ClassLoader做了介绍。在那里也提到过了部分关于ClassLoader的扩展,那么下面我将简单的实现一些自定义的ClassLoader

 

ClassLoader中提供了三个方法用于子类扩展其行为:

findResource

findResources

findClass

从它们的名字中已经能知道它们的行为了,因而就不做过多的解释了。

需要注意的是这三个方法都只要实现在自己的搜索路径中实现资源和字节码的查找即可,关于ClassLoader的代理模型已经在基类中的getResourcegetResourcesloadClass实现了,并且loadClass函数也实现已加载类的缓存机制,所以子类的findClass也不需要实现该机制。因此在子类中一般不需要也不建议重写getResourcegetResourcesloadClass等方法,除非自定义的ClassLoader在加载类时所使用的算法不同于默认的代理模型。

 

一般findClass可以如下方式实现:

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

         //多数情况下name为类名

         String resName = convertNameToResName(name);

         URL resURL = getResource(resName);

         if(resURL != null) {

                   byte[] classData = getClassDataFrom(resURL);

                   return defineClass(name, 0, classData, ClassData.length);

         }

         throw new ClassNotFoundException();

}

 

两个简单的自定义ClassLoader实现

 

假设有这样一个需求,当前已经有一个Employee接口,它的实现根据用户的配置不同而不同。这个需求可以简单的通过不同的配置映射不同的实现类来实现。假设又有一个变态的需求说Employee的实现类不在CLASSPATH的搜索路径中,这时就可以借助扩展ClassLoader的方式来实现。

在一个工程中定义了Employee接口:

public interface Employee {
	public String getFirstName();
	public void setFirstName(String firstName);
	public String getLastName();
	public void setLastName(String lastName);
	public String getFullName();
	
	public void printFullName();
}
有一个实现类如下:
public class EmployeeImpl implements Employee {
	private String firstName;
	private String lastName;
	@Override
	public String getFirstName() {
		return firstName;
	}
	@Override
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	@Override
	public String getLastName() {
		return lastName;
	}
	@Override
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	@Override
	public String getFullName() {
		return firstName + " " + lastName;
	}
	@Override
	public void printFullName() {
		System.out.println(getFullName());
	}
}

 

 

1.       LocalFileClassLoader

若该实现类生成的.class文件存放在”E:\\CodeRepository\\Java\\TomcatReading\\DomainImpl \\bin”

此时,我们可以如下实现LocalFileClassLoader类:

 

public class LocalFileClassLoader extends ClassLoader {
	private String location;
	
	public LocalFileClassLoader(String location, ClassLoader parent) {
		super(parent);
		this.location = location;
	}
	
	public LocalFileClassLoader(String location) {
		this(location, LocalFileClassLoader.class.getClassLoader());
	}
	
	public String getLocation() {
		return location;
	}
	
	@Override
	public Class<?> findClass(String name) 
	throws ClassNotFoundException {
		String separatorStr = new String(new char[] {File.separatorChar });
		if(!location.endsWith(separatorStr)) {
			location = location + separatorStr;
		}
		String filename = location + name.replace('.', File.separatorChar) + ".class";
		byte[] classData = null;
		try {
			classData = loadClassData(filename);
		} catch(FileNotFoundException e) {
			throw new ClassNotFoundException("Cannot find " + name + " at " + location);
		} catch(IOException e) {
			throw new ClassNotFoundException("Read class data error");
		}
		
		if(null == classData) {
			throw new ClassNotFoundException("Unknown reason");
		}
		
		return super.defineClass(null, classData, 0, classData.length);
	}
	
	private byte[] loadClassData(String filename) 
	throws FileNotFoundException, IOException {
		File inputFile = new File(filename);
		byte[] classData = new byte[(int)inputFile.length()];
		FileInputStream inputStream = new FileInputStream(filename);
		DataInputStream dataInput = new DataInputStream(inputStream);
		dataInput.readFully(classData);
		dataInput.close();
		return classData;
	}
}

 

该类只实现了findClass方法,并没有实现findResource方法。测试代码如下:

 

 

 

public class LocalFileClassLoaderTest {
	public static void main(String[] args) {
		LocalFileClassLoader classLoader = new LocalFileClassLoader("E:\\CodeRepository\\Java\\TomcatReading\\DomainImpl\\bin");
		try {
			Class<?> employeeCls = classLoader.loadClass("org.levin.domain.impl.EmployeeImpl");
			Employee employee = (Employee)employeeCls.newInstance();
			employee.setFirstName("Levin");
			employee.setLastName("Ding");
			employee.printFullName();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			System.err.println(e.getMessage());
		} catch(Exception e) {
			System.err.println(e.getMessage());
		}
	}
}

 

1.       LocalJarClassLoader

若该实现类生成的jar文件为"E:\\CodeRepository\\Java\\TomcatReading\\Temp\\ DomainImpl.jar"

此时,我们可以如下实现LocalJarClassLoader类:

 

public class LocalJarFileClassLoader extends ClassLoader {
	private final static LocalJarFileClassLoader instance = new LocalJarFileClassLoader();
	private final static int BUFFER_SIZE = 8192;
	private final static byte[] buffer = new byte[BUFFER_SIZE];
	
	private String jarFilename;
	
	protected LocalJarFileClassLoader() {
		this(null);
	}
	
	protected LocalJarFileClassLoader(String jarFilename) {
		this(jarFilename, LocalJarFileClassLoader.class.getClassLoader());
	}
	
	protected LocalJarFileClassLoader(String jarFilename, ClassLoader parent) {
		super(parent);
		this.jarFilename = jarFilename;
	}
	
	protected void setJarFilename(String jarFilename) {
		this.jarFilename = jarFilename;
	}
	
	protected static LocalJarFileClassLoader newInstance(String jarFilename) {
		instance.setJarFilename(jarFilename);
		return instance;
	}
	
	public static Class<?> loadClassFrom(String name, String jarFilename) 
	throws ClassNotFoundException {
		if(!jarFilename.toLowerCase().endsWith(".jar")) {
			throw new IllegalArgumentException(jarFilename + " is not a jar file");
		}
		
		return newInstance(jarFilename).loadClass(name);
	}
	
	@Override
	public Class<?> findClass(String name) 
	throws ClassNotFoundException {
		byte[] classData = loadClassData(name);
		return super.defineClass(null, classData, 0, classData.length);
	}
	
	private byte[] loadClassData(String name)
	throws ClassNotFoundException {
		JarInputStream jarInputStream = null;
		try {
			jarInputStream = new JarInputStream(new FileInputStream(jarFilename));
		} catch (FileNotFoundException e) {
			throw new ClassNotFoundException(jarFilename + " doesn't exist", e);
		} catch (IOException e) {
			throw new ClassNotFoundException("Reading " + jarFilename + " error", e);
		} 
		
		byte[] classData = null;
		String location = name.replace('.', '/') + ".class";
		while(true) {
			ZipEntry entry = null;
			try {
				entry = jarInputStream.getNextEntry();
				if(entry == null) break;
				
				String entryName = entry.getName();
				if(entryName.toLowerCase().endsWith(".class") && 
						entryName.equals(location)) {
					classData = readClassData(jarInputStream);
				}
				
			} catch(IOException e) {
				System.err.println(e.getMessage());
			}
		}
		
		if(classData == null) {
			throw new ClassNotFoundException(name + " cannot be found");
		}
		
		return classData;
	}
	
	private byte[] readClassData(JarInputStream jarInputStream)
	throws IOException {
		ByteArrayOutputStream classData = new ByteArrayOutputStream();
		int readSize = 0;
		while(jarInputStream.available() != 0) {
			// 注:有些时候,即使jarInputStream有超过BUFFER_SIZE的数据
			//     也有可能读到的数据少于BUFFER_SIZE
			readSize = jarInputStream.read(buffer, 0, BUFFER_SIZE);
			// jarInputStream.available()好像不起作用,因而需要加入一下代码
			if(readSize < 0) break;
			classData.write(buffer, 0, readSize);
		}
		return classData.toByteArray();
	}
}

 

该类也只是实现了findClass方法而没有实现findResource方法。测试代码如下:

public class LocalJarFileClassLoaderTest {

	public static void main(String[] args) {
		try {
			Class<?> employeeCls = LocalJarFileClassLoader.loadClassFrom(
					"org.levin.domain.impl.EmployeeImpl", 					"E:\\CodeRepository\\Java\\TomcatReading\\Temp\\DomainImpl.jar");
			Employee employee = (Employee)employeeCls.newInstance();
			employee.setFirstName("Levin");
			employee.setLastName("Ding");
			employee.printFullName();
		} catch (ClassNotFoundException e) {
			System.err.println(e.getMessage());
		} catch (Exception e) {
			e.printStackTrace();
			System.err.println(e.getMessage());
		}
	}

}

 

事实上,以上的需求用把字节代码存放在数据库的中的方式实现会更好,因为把字节码存放在数据库中有一个好处就是可以隐藏自己的实现代码而不用担心被反编译,这也是我之前在某篇文章中看到的保护自己知识产权的方法之一,可惜数据库的实现代码我还没写好。另外一个原因是以上两个自定义ClassLoader提供的功能,Java内部已经有一个功能更加强大的类可以提供了:URLClassLoader。实现以上功能的代码如下:

public class URLClassLoaderTest {
	public static void main(String[] args) {
		File jarFile = new File("E:\\CodeRepository\\Java\\TomcatReading\\Temp\\DomainImpl.jar");
		File clsFile = new File("E:\\CodeRepository\\Java\\TomcatReading\\DomainImpl\\bin");
		try {
			testURLClassLoader(jarFile.toURI().toURL());
			testURLClassLoader(clsFile.toURI().toURL());
		} catch (Exception e) {
			System.err.println(e.getMessage());
		}
	}
	
	private static void testURLClassLoader(URL url) throws Exception {
		URLClassLoader classLoader = new URLClassLoader(new URL[] { url });
		Class<?> employeeCls = classLoader.loadClass("org.levin.domain.impl.EmployeeImpl");
		Employee employee = (Employee)employeeCls.newInstance();
		employee.setFirstName("Levin");
		employee.setLastName("Ding");
		employee.printFullName();
	}
}

  

 

查看URLClassLoader的实现代码,会发现它正是实现了findClassfindResourcefindResources等方法,另外它也提供了两个newInstance的静态工厂方法以创建相应的URLClassLoader

 

分享到:
评论

相关推荐

    classloader体系结构(含hotswap)

    重温java之classloader体系结构(含hotswap) 启动类加载器 扩展类加载器 系统类加载器

    xjar-maven-plugin:XJar-Maven-Plugin是对XJar的一个Maven插件封装,实现可通过Maven命令或绑定在Maven的生命周期之中执行,以更便捷的方式集成XJar

    XJar是基于对JAR包内部资源的加密以及扩展ClassLoader来构建的一套程序加密启动,动态解密运行的方案,避免初始化或反向编译,支持Spring Boot JAR安全加密运行,同时支持其原生JAR。更多文档请点击: 环境依赖 ...

    Java类加载器(ClassLoader)1

    如果户创建的JAR放在此录下,也会动由扩展类加载器加载.应程序类加载器(系统类加载器,Application ClassLoader)java语编写,由sun.

    类加载器,classload

    关于类加载器的 上课ppt -java虚拟机自带的加载器 根类加载器(Bootstrap) c++写的看不到扩展类加载器(extension) 系统类加载器(System) AppClassLoad 用户自定义的类加载器 Java.lang.ClassLoader的子类

    Laravel框架中扩展函数、扩展自定义类的方法

    一、扩展自己的类 在app/ 下建立目录 libraries\class  然后myTest.php 类名格式 驼峰 myTest 复制代码 代码如下: &lt;?php class myTest { public function test() { return ‘1asdasd111’; } } 在 app/start/...

    java 类加载器 双亲委派 根加载器、扩展类加载器、系统类加载器

    类加载器分为根加载器(bootstrap classloader)、扩展类加载器(ext classloader)、系统类加载器(system classloader)、自定义类加载器(通常继承java.net.URLClassLoader,重写findClass()),它们的关系通常...

    Android中动态载入自定义类

    一些开发框架可以通过运行时自定义类加载机制来设计出更加可扩展的框架 通过该方式实现更加强壮的程序注册机制,防止被人破解 载入自定义类的过程 获取需要载入的自定义类的dex文件,可以是设备本地的文件或者互联网...

    AOP ClassLoader Suite-开源

    一组Java类加载器,使用“加载时反射”(在类加载时修改字节码)来扩展现有的Java类。 它使用了出色的BCEL库(http://bcel.sf.net)。

    Java类加载机制与反射-PPT

    Java的类加载机制:加载,连接,初始化。JAVA类加载器: Bootstrap ClassLoader : 根类加载器, Extension ClassLoader: 扩展类加载器, System ClassLoader : 系统类加载器, Java反射

    14.类加载器1

    (1) 启动类加载器(BootStrap classLoader):这个类加载器负责将存在 (2) 扩展类加载器(Extension ClassLoader):

    JVM-类加载器与双亲委托

    2.Extension ClassLoader:扩展加载器 3.System ClassLoader:系统加载器 以下我们将对这3种加载器进行分析: 1.Bootstrap ClassLoader 根加载器 Bootstrap ClassLoader(根加载器)是由C++编写的加载器,负责加载 /lib...

    【JVM】类加载器与双亲委派模型

    扩展类加载器(Extension ClassLoader) 应用类加载器(Application ClassLoader) 启动类加载器 内嵌在JVM内核中的加载器,由C++语言编写(因此也不会继承ClassLoader),是类加载器层次中最顶层的加载器。用于...

    day020-继承加强和设计模式代码和笔记.rar

    1、ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); //获取当前线程类加载器 2、InputStream is = classLoader.getResourceAsStream("文件路径/文件名");//通过当前线程的类...

    sbp:基于pf4j的Spring Boot插件框架

    我们需要一个具有灵活性和可扩展性的现代框架,以快速为复杂的业务场景提供解决方案。 并非所有项目都需要像Spring Cloud一样在开始阶段就考虑扩展。 使用sbp ,我们可以仅在Spring Boot中考虑微服务架构,而无需...

    GroovyAction:通过 groovy 编写动作

    其实具体的工作量并不大,主要就是支持脚本语言groovy编写的action,扩充了ObjectFactory,增加了groovy的classloader,同时扩展了配置项,分为生产环境和运行环境,在生产环境中,不把class进行缓存,可以动态展现...

    Java虚拟机和Java程序的生命周期?

    提供了访问该类在方法去内的数据结构的接口。...Java虚拟机自带的类加载器包括:启动类加载器,扩展类加载器,系统类加载器三种。 用户自定义的类加载器是ClassLoader类的实例,通过它来定制类的加载方式。

    飞翔的小鸟java源码-java-cheat-sheet:JAVA的所有基本概念

    #####ClassLoader 查找并加载 Java 类! 3种 系统 - 从 CLASSPATH 加载所有应用程序类 扩展 - 从扩展目录加载所有类 Bootstrap - 加载所有 Java 核心文件 #####第一个Java程序 public class Main { public static ...

    大数据面试题.pdf

    ArrayList 和 Vector 是采⽤数组⽅式存储数据的,是根据索引来访问元素的,都可以 根据需要⾃动扩展内部数据长度,以便增加和插⼊元素,都允许直接序号索引元素,但 是插⼊数据要涉及到数组元素移动等内存操作,所以...

    开源bbs源码java-interview-note:面试题总结

    ClassLoader有什么用? ==和equals的区别? hashCode方法的作用? Object类中有哪些方法?列举3个以上。 NIO是什么?适用于何种场景? HashMap数据结构、扩展策略,Hash冲突攻击如何防范,如何实现线程安全的HashMap...

    笔记 — JVM内存结构

    JVM = 类加载器(classloader) + 执行引擎(execution engine) + 运行时数据区域(runtime data area)。 下面我们从每个区域的用途,涉及的问题等方面来简单的说一说JVM的内存结构。 方法区 作用:用于存放已被加载的...

Global site tag (gtag.js) - Google Analytics