自定义ClassLoader

来源:互联网 发布:java多线程同步和死锁 编辑:程序博客网 时间:2024/05/11 23:39

最近在做一个工作,把两个文件合并,Android中的两个R.java文件合并。看到这个需求时,想到两种方法: 1)逐行读文件,根据关键字区分哪些是内部类,哪些是内部类的成员变量;2)通过将.java文件编译并加载,通过反射得到内部类,和成员变量。第二种方法更直接一些,本文记录下编译并加载.java文件的方法和问题。

目的:编译并加载磁盘上任意位置的.java文件。

实现MyClassloader,继承自ClassLoader,重写findClass方法

package com.demo.RMerger;import java.io.ByteArrayOutputStream;import java.io.File;import java.io.FileInputStream;import java.io.IOException;public class MyClassloader extends ClassLoader {private String mPre;public MyClassloader(String pre) {mPre = pre;}// 读取一个文件的内容private byte[] getBytes(String filename) throws IOException {FileInputStream fi = null;fi = new FileInputStream(filename);ByteArrayOutputStream baos = new ByteArrayOutputStream();byte[] buffer = new byte[8192];int read;while ((read = fi.read(buffer)) > 0)baos.write(buffer, 0, read);byte[] classBytes = baos.toByteArray();return classBytes;}// 定义编译指定Java文件的方法public boolean compile(String javaFile) throws IOException {System.out.println("CompileClassLoader:正在编译" + javaFile + "...");// 调用系统的javac命令Process p = Runtime.getRuntime().exec("javac " + javaFile);try {p.waitFor();} catch (InterruptedException e) {e.printStackTrace();}// 获取javac线程的退出值int ret = p.exitValue();// 返回编译是否成功return ret == 0;}// 重写ClassLoader的findClass方法protected Class<?> findClass(String name) throws ClassNotFoundException {Class clazz = null;// 将包路径中的点替换成斜线String fileStub = name.replace(".", File.separator);String javaFilename = mPre + fileStub + ".java";String classFilename = mPre + fileStub + ".class";File javaFile = new File(javaFilename);File classFile = new File(classFilename);if (javaFile.exists() && (!classFile.exists()|| javaFile.lastModified() > classFile.lastModified())) {try {// 如果编译失败,或者该Class文件不存在if (!compile(javaFilename) || !classFile.exists()) { // 这一步compile重新编译了一次throw new ClassNotFoundException("ClassNotFoundException:" +javaFilename);}} catch (IOException e) {e.printStackTrace();}}// 如果Class文件存在,系统负责将该文件转换成Class对象if (classFile.exists()) {try {// 将Class文件的二进制数据读入数组byte[] raw = getBytes(classFilename);// 调用ClassLoader的defineClass方法将二进制数据成Class对象clazz = defineClass(name, raw, 0, raw.length);} catch (IOException e) {e.printStackTrace();}}// 如果clazz为null,则表明加载失败,抛出异常if (clazz == null) {throw new ClassNotFoundException(name);}return clazz;}}
调用方法:

MyClassloader myClassloader = new MyClassloader(System.getProperty("user.dir") + File.separatorChar + pre+ File.separatorChar);
Class<?> R = myClassloader.loadClass(name.replace(File.separatorChar, '.'));


  注意调用loadClass时候需要用包名+类名;比如com.android.R。

System.getProperty("user.dir")表示当前工程的路径。


0 0
原创粉丝点击