转自---> http://liudeh-009.iteye.com/blog/1463388
有时候,根据自己项目的需求,需要重新实现属于自己的类加载器,以满足项目的灵活性和扩展性,下面我们就来实现自己的类加载器.
实现自己的类加载器必须首先继承一个父类加载器.
编写一个类加载器会涉及到以下几个方法:
1. findClass(String name) 根据类的路径查找类,必须重写的方法
2.defineClass(String name, byte[] b, int off, int len) 由父类实现,直接调用
3.loadClass(String name)首先调用父类的findClass方法找,找不到则调用自身重写的findClass方法找,也不需要我们实现.loadClass(String name)默认会调用loadClass(name,false)方法,表示只加载,不发生连接操作.JDK的ClassLoader类的loadClass(String name,boolean resolve)方法的实现如下:
public abstract class ClassLoader { protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { synchronized (getClassLoadingLock(name)) { // First, check if the class has already been loaded Class c = findLoadedClass(name); if (c == null) { try { if (parent != null) { c = parent.loadClass(name, false); } else { c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) { // ClassNotFoundException thrown if class not found // from the non-null parent class loader } if (c == null) { // If still not found, then invoke findClass in order // to find the class. c = findClass(name); } } if (resolve) { resolveClass(c); } return c; } } protected Class<?> findClass(String name) throws ClassNotFoundException { throw new ClassNotFoundException(name); } }
可以看出ClassLoader采用了模版模式,在父类加载器加载不到想要的类时,采用自己实现的方法.
自己写的类加载器及测试代码如下:
- <span style="font-size: small;">public class MyClassLoader extends ClassLoader {
- private String path = "c:/bin/";
- @Override
- protected Class<?> findClass(String name) throws ClassNotFoundException {
- String namePath = name.replace(".", "/");
- String classPath = path + namePath + ".class";
- InputStream is = null;
- ByteArrayOutputStream os = null;
- try {
- is = new FileInputStream(new File(classPath));
- os = new ByteArrayOutputStream();
- int b = 0;
- while ((b = is.read()) != -1) {
- os.write(b);
- }
- byte[] bytes = os.toByteArray();
- os.flush();
- return defineClass(name, bytes, 0, bytes.length);
- } catch (Exception e) {
- e.printStackTrace();
- }finally{
- try {
- if(os!=null)
- os.close();
- if(is!=null)
- is.close();
- } catch (IOException e1) {
- os=null;
- is = null;
- }
- }
- return null;
- }
- @SuppressWarnings("unchecked")
- public static void main(String[] args) {
- MyClassLoader myLoader = new MyClassLoader();
- try {
- Class myClass = myLoader.loadClass("com.ldh.loader.HelloWorld");
- Object obj = myClass.newInstance();
- Method method = myClass.getMethod("say", null);
- method.invoke(obj, null);
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }</span>
HelloWolrd类如下:
- <span style="font-size: small;">public class HelloWorld {
- public void say(){
- System.out.println("hello,world");
- }
- }
- </span>
我把HelloWorld类放在c:/bin/目录下.
最后谈一下loadClass()和forName()的区别.
从上可以看出调用loadClass(name),相当于调用loadClass(name,false),表示只加载类,不连接初始化类,调用newInstance()才真正完成连接初始化操作.
Class.forName("xxxx")等同于Class.forName("xxxx",true,loader).true,表示载入实例的同时也载入静态初始化区块;false,表示只会加载该类别,但不会调用其静态初始化区块,只有等到整个程序第一次实例化某个类时,静态初始化区块才会被调用
在大多情况下loadClass()和forName()可以互用, 可以把ClassLoader.loadClass()看成是更底层的操作.在某些必须初始化类的场合,比如加载JDBC驱动,只能使用forName()方法了
从上可以看出,实现自己的类加载器相当简单,只要继承一个父类加载器,重写findClass方法就可以了.