由于最近的一个项目需要使用到java的反射机制,所以最近也是好好看了一下java的ClassLoader,目前也只完成了一个可以读取某一个文件夹中的所有类的Loader.
我们都知道,自定义的ClassLoader都是需要重写几个方法的,比如:findResource.findClass.但是很少有文章详细的所以下ClassLoader的调用顺序.先说一下代码吧:
public class MyClassLoader extends ClassLoader { private String BaseDir = new String(); Logger logger = Logger.getLogger("MyClassLoader"); /** * @param name 形如"java.lang.String"的字符串 * {@inheritDoc} */ @Override protected Class<?> findClass(String name) throws ClassNotFoundException { logger.log(Level.INFO, "findClass"); byte[] bytes = loadClassBytes(name); Class theClass = defineClass(name, bytes, 0, bytes.length); if (theClass == null) throw new ClassFormatError(); return theClass; } private byte[] loadClassBytes(String className) throws ClassNotFoundException { logger.log(Level.INFO, "loadClassBytes"); try { String classFile = getClassFile(className); FileInputStream fis = new FileInputStream(classFile); FileChannel fileC = fis.getChannel(); ByteArrayOutputStream baos = new ByteArrayOutputStream(); WritableByteChannel outC = Channels.newChannel(baos); ByteBuffer buffer = ByteBuffer.allocateDirect(1024); while (true) { int i = fileC.read(buffer); if (i == 0 || i == -1) { break; } buffer.flip(); outC.write(buffer); buffer.clear(); } fis.close(); return baos.toByteArray(); } catch (IOException fnfe) { throw new ClassNotFoundException(className); } } private String getClassFile(String name) { logger.log(Level.INFO, "getClassFile"); StringBuffer sb = new StringBuffer(getBaseDir()); name = name.replace('.', File.separatorChar) + ".class"; sb.append(File.separator + name); return sb.toString(); } private String getBaseDir() { return BaseDir; } public void setBaseDir(String basedir){ BaseDir = basedir; } // 新增的一个findResource方法 @Override protected URL findResource(String name) { logger.log(Level.INFO, "findResource"); try { URL url = super.findResource(name); if (url != null) return url; url = new URL("file:///" + converName(name)); // 简化处理,所有资源从文件系统中获取 return url; } catch (MalformedURLException mue) { throw new RuntimeException(mue); } } private String converName(String name) { logger.log(Level.INFO, "converName"); StringBuffer sb = new StringBuffer(getBaseDir()); name = name.replace('.', File.separatorChar); sb.append(File.separator + name); return sb.toString(); } }
这就是全部的代码,然后我们在main函数里调用load函数,会出现以下内容:
我们可以看到,Loader函数的执行顺序是: findClass–>loadClassBytes–>getClassFile.所以,如果我们需要自定义我们自己的ClassLoader只需要按着顺序进行修改就行了.
至于main函数吧,就是这样的:
MyClassLoader classLoader = new MyClassLoader(); classLoader.setBaseDir("Classes/"); Class<?> class1 = classLoader.loadClass("test"); for (Method method : class1.getMethods()) { System.out.println(method.getName()); } Method method = class1.getMethod("sayHello", null); method.invoke(class1.newInstance(), null);
我把所有的class文件放在Classes文件夹之下,看上去是这样的:
这是test.class的代码:
public class test{ public static void main(String[] args){ System.out.println("Hello World!"); } public void sayHello(){ System.out.println("Hello World!"); } }
于是这样,我们得到了好的结果: