通过自定义类加载器进行动态编译与动态实现接口
2014-12-25 17:16
274 查看
两种动态加载类的方法
1. 使用java.lang.Class.forName(fullClassName ).
2. 使用自定义类加载器.
这里看看如何通过自定义加载器来进行java类加载.
通过默认使用的类加载器来加载类,一般都是通过如下方法:
看一下loadClass方法的实现:
这是一个采用双亲委派机制的实现,最主要的就是一个findLoadedClass方法,其它就是递归找父亲了..
所以要进行自定义扩展,就要从这个方法入手.
findLoadClass的默认实现如下:
这里我们看到了一个最让我们感兴趣的方法: defineClass
protected final Class<?> defineClass(String name, byte[] b, int off, int len);
这个方法将一个字节数据转换成一个Class并初始化.
当然如果不能转换的话,异常: ClassFormatError.
所以我们就有了一个自由加载文件系统中任意合法类文件的思路了.
写一个类继承ClassLoader-—将文件系统中的*.class文件以byte[]的方式传入---交给defineClass处理. 这样的做法似乎破坏了Java的双亲委派
所以类定义如下:
现在,就可以使用修改后的类加载器来进行类的加载了.
加载完后就可以Class、Field和Method的一些方法来进行接口的“实现”.
方法很多.
这里所谓的”实现”是: 就是根据程序运行过程中的一些需求,进行具体方法的写. 如:
动态编译
动态编译的过程是:
生成一个临时的*.java文件, 使用com.sun.tools.javac.Main来模拟命令行中的java文件编译.
生成好之后,就可以用同样的方式进行实现类的加载,通过如下方式进行
1. 使用java.lang.Class.forName(fullClassName ).
2. 使用自定义类加载器.
这里看看如何通过自定义加载器来进行java类加载.
通过默认使用的类加载器来加载类,一般都是通过如下方法:
Class obj = aLoader.loadClass(customFullClassName, true);
看一下loadClass方法的实现:
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { Class c = findLoadedClass(name); …………… ……… }
这是一个采用双亲委派机制的实现,最主要的就是一个findLoadedClass方法,其它就是递归找父亲了..
所以要进行自定义扩展,就要从这个方法入手.
findLoadClass的默认实现如下:
这里我们看到了一个最让我们感兴趣的方法: defineClass
protected final Class<?> defineClass(String name, byte[] b, int off, int len);
这个方法将一个字节数据转换成一个Class并初始化.
当然如果不能转换的话,异常: ClassFormatError.
所以我们就有了一个自由加载文件系统中任意合法类文件的思路了.
写一个类继承ClassLoader-—将文件系统中的*.class文件以byte[]的方式传入---交给defineClass处理. 这样的做法似乎破坏了Java的双亲委派
所以类定义如下:
/** * 一个自定义的类加载器. * @author nileader */ public class CustomClassLoader extends ClassLoader { public static final String classPath = System.getProperty("user.dir") + "\\bin\\"; /** * 自定义的一个加载方法,这样的做法似乎破坏了Java的双亲委派 * @param classFullName * @return */ protected Class customLoadClass(String classFullName){ String filePath = classFullName2FileName(classFullName, classPath); byte[] data = loadClassFromFS(filePath ); //调用defineClass将一个字节数据转换成一个类并进行初始化工作. return defineClass(classFullName, data, 0, data.length); } /** * 从文件系统中加载类文件,生成一个byte[]. * @param name 文件路径 * @return 类文件的字节码数组 */ private byte[] loadClassFromFS(String filePath) { FileInputStream fis = null; byte[] byteSource = null; try { fis = new FileInputStream(new File(filePath ) ); ByteArrayOutputStream tempSource = new ByteArrayOutputStream(); int readChar= 0; while ((readChar = fis.read()) != -1) { tempSource.write(readChar ); } byteSource = tempSource.toByteArray(); } catch (IOException e) { //IO出错 } return byteSource; } /** * 将一个完整类名转换为一个当前工程classpath为基础的文件路径. * @param classFullName 一个完整类名 * @param classPath 当前工程类路径 * @return */ public String classFullName2FileName(String classFullName, String classPath){ classFullName = classFullName.replaceAll("[.]", "\\\\" ); return classPath + classFullName + ".class"; }
现在,就可以使用修改后的类加载器来进行类的加载了.
CustomClassLoader customClassLoader = new CustomClassLoader(); Class obj = customClassLoader.customLoadClass("com.test.DemoInterface" );
加载完后就可以Class、Field和Method的一些方法来进行接口的“实现”.
方法很多.
这里所谓的”实现”是: 就是根据程序运行过程中的一些需求,进行具体方法的写. 如:
动态编译
动态编译的过程是:
生成一个临时的*.java文件, 使用com.sun.tools.javac.Main来模拟命令行中的java文件编译.
/** * 将Java源程序生成一个类文件. * * @param sourceCode * 要编译的Java源程序 * @throws Exception */ public static boolean compile(String sourceCode, String className) { File file = null; // 生成一个临时的*.java文件. try { file = File.createTempFile("JavaRuntime", ".java", new File( targetPath)); } catch (IOException e) { // 发生IO异常 } String fileName = null; if (null != file) { // 取得类名 fileName = file.getName(); file.deleteOnExit(); } // 将代码输出到java文件中去. PrintWriter out = null; try { out = new PrintWriter(new FileOutputStream(file)); } catch (FileNotFoundException e) { // 发生 FileNotFoundException 异常. } if (null != out) { out.write(sourceCode); out.flush(); out.close(); } /** 开始编译 */ if (fileName != null) { Main.compile(new String[] { "-d", System.getProperty("user.dir") + "\\bin\\", targetPath + fileName }); } return true; }
生成好之后,就可以用同样的方式进行实现类的加载,通过如下方式进行
Class c1 = customClassLoader.customLoadClass(implementsFullName); Constructor con = c1.getDeclaredConstructor(); con.setAccessible(true ); Object o = con.newInstance(); // 调用封装bean的方法 Method method = c1.getMethod("print"); method.setAccessible(true); method.invoke(o);本文出自 “专栏:Paxos与ZooKeeper” 博客,请务必保留此出处http://nileader.blog.51cto.com/1381108/307975
相关文章推荐
- 通过自定义类加载器进行动态编译与动态实现接口
- 通过代理接口在内存中动态生成代理类源代码并编译实现的真正动态代理
- 利用C#动态编译功能实现像Javascript中的Eval的功能来将一段字符串进行数学运算
- Java技巧——实现Comparator接口来进行字符串逆向排序
- asp.net根据条件动态生成GridView,并动态绑定列,且可对其进行编辑的实现
- C++ 接口与实现分离技术---如何将文件间的编译关系降至最低
- 在java中利用动态编译实现eval
- asp.net根据条件动态生成GridView,并动态绑定列,且可对其进行编辑的实现
- 在java中利用动态编译实现eval
- C#动态编译的实现
- 使用CODEDOM动态实现代码的生成,编译
- InvocationHandler接口实现动态代理
- asp.net根据条件动态生成GridView,并动态绑定列,且可对其进行编辑的实现
- 将数据库中的.net原码进行动态编译及调用~
- 用动态代理实现用AOP对数据库进行操作
- .NET : 动态生成工作流定义文件并且进行编译
- 基于MATLAB 进行混合编程的接口技术实现
- 使用CODEDOM动态实现代码的生成,编译
- 利用动态创建自动化接口实现VB的函数指针调用
- 用于创建实现 System.ComponentModel.INotifyPropertyChanged 接口的动态类型,并添加各个 public 属性的定义