通过自定义类加载器进行动态编译与动态实现接口
2010-05-01 00:49
603 查看
两种动态加载类的方法
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);本文出自 “ni掌柜的IT专栏” 博客,请务必保留此出处http://nileader.blog.51cto.com/1381108/307975
相关文章推荐
- 通过代理接口在内存中动态生成代理类源代码并编译实现的真正动态代理
- 通过自定义类加载器进行动态编译与动态实现接口
- Windows下通过MinGW进行WxWidgets的动态编译与静态编译
- Java 计算器,通过动态生成并编译Java类来进行
- 利用C#动态编译功能实现像Javascript中的Eval的功能来将一段字符串进行数学运算
- java中通过接口实现自定义事件,动态设定行为
- @SpringContext通过实现ApplicationContextAware接口动态获取bean
- 通过反射中的 TypeBuilder 来动态实现 INotifyPropertyChanged 接口
- asp.net通过实现IHttpHandler接口方法给图片添加水印图片(动态添加,不破坏原图片,但是耗资源)
- 通过实现ApplicationContextAware接口动态获取bean
- LINQ 通过动态生成lambda表达式,实现根据指定属性名称对序列进行排序
- .Net Core 通过依赖注入和动态加载程序集实现宿主程序和接口实现类库完全解构
- 基于Ado.net Entity Framework 通过系统运行时动态编译实现数据表字段的增删
- 组合查询(通过Criteria接口实现动态构造查询条件)
- 通过动态获取资源ID,执行接口回调-----来实现日夜模式切换
- 实现进度条程序. 通过makefile进行编译
- 通过实现 Filter 接口进行项目全局编码控制
- Android 通过WebService进行网络编程,使用工具类轻松实现
- 通过IViewObject接口,取浏览器的图象,实现SNAP
- 安卓与IOS通过hessian接口实现用友系统移动手机订单3JDBC跨数据库库事务处理