| 
                  
                      
                            | 很多时候人们会使用一些自定义的ClassLoader ,而不是使用系统的Class Loader。大多数时候人们这样做的原因是,他们在编译时无法预知运行时会需要那些Class。特别是在那些appserver中,比如tomcat,Avalon-phonix,Jboss中。或是程序提供一些plug-in的功能,用户可以在程序编译好之后再添加自己的功能,比如ant, jxta-shell等。定制一个ClassLoader很简单,一般只需要理解很少的几个方法就可以完成。
 一个最简单的自定义的ClassLoader从ClassLoader类继承而来。这里我们要做一个可以在运行时指定路径,加载这个路径下的class的ClassLoader。
 通常我们使用ClassLoader.loadClass(String):Class方法,通过给出一个类名,就会得到一个相应的Class实例。因此只要小小的改动这个方法,就可以实现我们的愿望了。
 源码:
 
 
                            
                            protected synchronized Class loadClass(String name, boolean resolve)    throws ClassNotFoundException    {     
                                // 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 = findBootstrapClass0(name);            }         }catch(ClassNotFoundException e){ 
                                        // If still not found, then call findClass in order 
                                        // to find the class.             
                                       c = findClass(name);         }     }     if (resolve) { 
                                  resolveClass(c);     }     return c; 
                            }  Source from ClassLoader.java
 
 First,check JavaAPI doc:上面指出了缺省的loadClass方法所做的几个步骤。
 1.    调用findLoadedClass(String):Class 检查一下这个class是否已经被加载过了,由于JVM 规范规定ClassLoader可以cache它所加载的Class,因此如果一个class已经被加载过的话,直接从cache中获取即可。
 2.    调用它的parent 的loadClass()方法,如果parent为空,这使用JVM内部的class loader(即著名的bootstrap classloader)。
 3.    如果上面两步都没有找到,调用findClass(String)方法来查找并加载这个class。
 后面还有一句话,在Java 1.2版本以后,鼓励用户通过继承findClass(String)方法实现自己的class loader而不是继承loadClass(String)方法。
 既然如此,那么我们就先这么做:)
 
 
                            
                            public class AnotherClassLoader extends ClassLoader { 
                                private String baseDir;private static final Logger LOG =  
                                     Logger.getLogger(AnotherClassLoader.class);     
                                public AnotherClassLoader (ClassLoader parent, String baseDir) { 
                                       super(parent); 
                                       this.baseDir = baseDir; 
                                }     protected Class findClass(String name) 
                                        throws ClassNotFoundException { 
                                    LOG.debug("findClass " + name); 
                                    byte[] bytes = loadClassBytes(name); 
                                    Class theClass = defineClass(name, bytes, 0, bytes.length);//A 
                                    if (theClass == null) 
                                        throw new ClassFormatError(); 
                                    return theClass; 
                                }     private byte[] loadClassBytes(String className) throws 
                                    ClassNotFoundException { 
                                    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) { 
                                    StringBuffer sb = new StringBuffer(baseDir); 
                                    name = name.replace('.', File.separatorChar) + ".class"; 
                                    sb.append(File.separator + name); 
                                    return sb.toString(); 
                                } }  [i]Ps:这里使用了一些JDK1.4的nio的代码:)[/i]
 很简单的代码,关键的地方就在A处,我们使用了defineClass方法,目的在于把从class文件中得到的二进制数组转换为相应的Class实例。defineClass是一个native的方法,它替我们识别class文件格式,分析读取相应的数据结构,并生成一个class实例。
 
 还没完呢,我们只是找到了发布在某个目录下的class,还有资源呢。我们有时会用Class.getResource():URL来获取相应的资源文件。如果仅仅使用上面的ClassLoader是找不到这个资源的,相应的返回值为null。
 
 同样我们看一下原来的ClassLoader内部的结构。
 
 
                            
                            public java.net.URL getResource(String name) { 
                                    name = resolveName(name);         ClassLoader cl = getClassLoader0();//这里 
                                    if (cl==null) { 
                                        // A system class. 
                                        return ClassLoader.getSystemResource(name); 
                                    }         return cl.getResource(name);} 
                             
 原来是使用加载这个class的那个classLoader获取得资源。
 
 
 
                            
                            public URL getResource(String name) { 
                                URL url; 
                                if (parent != null) { 
                                    url = parent.getResource(name);     } else { 
                                    url = getBootstrapResource(name);     }     if (url == null) { 
                                    url = findResource(name);//这里 
                                }     return url; 
                            }  
 
 
 这样看来只要继承findResource(String)方法就可以了。修改以下我们的代码:
 
 
 
                            
                            //新增的一个findResource方法 
                            protected URL findResource(String name) { 
                                    LOG.debug("findResource " + name); 
                                    try { 
                                        URL url = super.findResource(name); 
                                        if (url != null) 
                                            return url; 
                                        url = new URL("file:///" + converName(name)); 
                                        //简化处理,所有资源从文件系统中获取 
                                        return url; 
                                    } catch (MalformedURLException mue) { 
                                        LOG.error("findResource", mue); 
                                        return null; 
                                    } } private String converName(String name) { 
                                    StringBuffer sb = new StringBuffer(baseDir); 
                                    name = name.replace('.', File.separatorChar); 
                                    sb.append(File.separator + name); 
                                    return sb.toString(); 
                            }  
 好了,到这里一个简单的自定义的ClassLoader就做好了,你可以添加其他的调料(比如安全检查,修改class文件等),以满足你自己的口味:)
 
 ----------------
 I am not guru,but nonsenser
 
 
 |  |