两种动态加载类的方法

1.       使用java.lang.Class.forName(fullClassName ).

2.       使用自定义类加载器.

这里看看如何通过自定义加载器来进行java类加载.

 

通过默认使用的类加载器来加载类,一般都是通过如下方法:

 

 
  
  1. Class obj = aLoader.loadClass(customFullClassName, true); 

 

看一下loadClass方法的实现:

 

 
  
  1. protected synchronized Class<?> loadClass(String name, boolean resolve) 
  2.     throws ClassNotFoundException 
  3.     { 
  4.     Class c = findLoadedClass(name); 
  5.     …………… 
  6. ……… 

这是一个采用双亲委派机制的实现,最主要的就是一个findLoadedClass方法,其它就是递归找父亲了..

所以要进行自定义扩展,就要从这个方法入手.

findLoadClass的默认实现如下:

这里我们看到了一个最让我们感兴趣的方法defineClass

protected final Class<?> defineClass(String name, byte[] b, int off, int len);

这个方法将一个字节数据转换成一个Class并初始化.

当然如果不能转换的话,异常ClassFormatError.

 

所以我们就有了一个自由加载文件系统中任意合法类文件的思路了.

写一个类继承ClassLoader-—将文件系统中的*.class文件以byte[]的方式传入---交给defineClass处理. 这样的做法似乎破坏了Java的双亲委派

所以类定义如下:

 

 
  
  1. /** 
  2.  * 一个自定义的类加载器. 
  3.  * @author nileader 
  4.  */ 
  5. public class CustomClassLoader extends ClassLoader { 
  6.  
  7.     public static final String classPath = System.getProperty("user.dir") + "\\bin\\"
  8.  
  9.     /** 
  10.      * 自定义的一个加载方法,这样的做法似乎破坏了Java的双亲委派 
  11.      * @param classFullName 
  12.      * @return 
  13.      */ 
  14.     protected Class customLoadClass(String classFullName){ 
  15.         String filePath = classFullName2FileName(classFullName, classPath); 
  16.         byte[] data = loadClassFromFS(filePath ); 
  17.         //调用defineClass将一个字节数据转换成一个类并进行初始化工作. 
  18.         return defineClass(classFullName, data, 0, data.length); 
  19.     } 
  20.  
  21.     /** 
  22.      * 从文件系统中加载类文件,生成一个byte[]. 
  23.      * @param name   文件路径 
  24.      * @return   类文件的字节码数组 
  25.      */ 
  26.     private byte[] loadClassFromFS(String filePath) { 
  27.         FileInputStream fis = null
  28.         byte[] byteSource = null
  29.         try { 
  30.             fis = new FileInputStream(new File(filePath ) ); 
  31.             ByteArrayOutputStream tempSource = new ByteArrayOutputStream(); 
  32.             int readChar= 0
  33.             while ((readChar = fis.read()) != -1) { 
  34.                 tempSource.write(readChar ); 
  35.             } 
  36.             byteSource = tempSource.toByteArray(); 
  37.         } catch (IOException e) { 
  38.             //IO出错 
  39.         } 
  40.         return byteSource; 
  41.     } 
  42.      
  43.     /** 
  44.      * 将一个完整类名转换为一个当前工程classpath为基础的文件路径. 
  45.      * @param classFullName     一个完整类名 
  46.      * @param classPath         当前工程类路径 
  47.      * @return 
  48.      */ 
  49.     public String classFullName2FileName(String classFullName, String classPath){ 
  50.         classFullName = classFullName.replaceAll("[.]""\\\\" ); 
  51.         return classPath  + classFullName + ".class"
  52.     } 

 

现在,就可以使用修改后的类加载器来进行类的加载了.

 
  
  1. CustomClassLoader customClassLoader = new CustomClassLoader(); 
  2.  
  3. Class obj = customClassLoader.customLoadClass("com.test.DemoInterface" ); 

加载完后就可以ClassFieldMethod的一些方法来进行接口的“实现”.

方法很多.

这里所谓的实现就是根据程序运行过程中的一些需求,进行具体方法的写:

动态编译

动态编译的过程是:

生成一个临时的*.java文件使用com.sun.tools.javac.Main来模拟命令行中的java文件编译.

 

 

 

 
  
  1. /** 
  2.      * 将Java源程序生成一个类文件. 
  3.      *  
  4.      * @param sourceCode 
  5.      *            要编译的Java源程序 
  6.      * @throws Exception 
  7.      */ 
  8.     public static boolean compile(String sourceCode, String className) { 
  9.         File file = null
  10.         // 生成一个临时的*.java文件. 
  11.         try { 
  12.             file = File.createTempFile("JavaRuntime"".java"new File( 
  13.                     targetPath)); 
  14.         } catch (IOException e) { 
  15.             // 发生IO异常 
  16.         } 
  17.         String fileName = null
  18.         if (null != file) { 
  19.             // 取得类名 
  20.             fileName = file.getName(); 
  21.             file.deleteOnExit(); 
  22.         } 
  23.  
  24.         // 将代码输出到java文件中去. 
  25.         PrintWriter out = null
  26.         try { 
  27.             out = new PrintWriter(new FileOutputStream(file)); 
  28.         } catch (FileNotFoundException e) { 
  29.             // 发生 FileNotFoundException 异常. 
  30.         } 
  31.         if (null != out) { 
  32.             out.write(sourceCode); 
  33.             out.flush(); 
  34.             out.close(); 
  35.         } 
  36.  
  37.         /** 开始编译 */ 
  38.         if (fileName != null) { 
  39.             Main.compile(new String[] { "-d"
  40.                     System.getProperty("user.dir") + "\\bin\\"
  41.                     targetPath + fileName }); 
  42.         } 
  43.  
  44.         return true
  45.  
  46.     } 

生成好之后,就可以用同样的方式进行实现类的加载,通过如下方式进行

 
  
  1. Class c1 = customClassLoader.customLoadClass(implementsFullName); 
  2.         Constructor con = c1.getDeclaredConstructor(); 
  3.         con.setAccessible(true ); 
  4.         Object o = con.newInstance(); 
  5.         // 调用封装bean的方法 
  6.         Method method = c1.getMethod("print"); 
  7.         method.setAccessible(true); 
  8.         method.invoke(o);