Mybatisソース学習のリソースロード(六)

35796 ワード

クラス・ローダの概要
Java仮想マシンのクラスローダ(ClassLoader)は、ファイルシステム、ネットワーク、または他のソースからのクラスファイルのロードを担当します.Java仮想マシンのクラスローダは、親委任モードがデフォルトで使用されます.図に示すように、Bootstrap ClassLoader、Extension ClassLoader、System ClassLoaderの3つのデフォルトで使用されています.各クラスローダは、クラスファイルをどの場所からロードするかを決定します.
Bootstrap ClassLoader:JDKに付属のrt.jarパッケージのクラスファイルをロードします.これはすべてのクラスローダの親ローダです.
Extension ClassLoader:Javaの拡張クラスライブラリをロードし、jre/lib/extディレクトリまたはjava.ext.dirsシステム属性で指定されたディレクトリからクラスをロードします.
System ClassLoader:Extension ClassLoaderのサブローダであるclasspath環境変数からクラスファイルをロードします.
親委任モードに従って、クラスファイルをロードすると、サブローダはまずロード要求を親ローダに委任します.親ローダは、クラスがロードされたかどうかを検出し、ロードされた場合はロードプロセスが終了します.ロードされていない場合は、Bootstrap ClassLoaderまでリクエストがアップロードされます.要求のアップロード中にクラスがロードされたことが検出されない場合は、Bootstrap ClassLoaderから対応するパスからクラスファイルのロードを試み、ロードに失敗した場合は、ロード要求を開始するサブローダビットまでサブローダでロードを試み続けます.
システムが3種類のローダを提供するほか、java.lang.ClassLoaderクラスを継承することによって、Tomcat、JBossなどの特定のニーズを満たすために、独自のクラスローダを実現することもできます.
ClassLoaderWrapper
Mybatis IOパッケージで提供されるClassLoaderWrapperは、複数のClassLoaderオブジェクトを含むClassLoaderのパッケージです.複数のクラス・ローダーの使用順序を調整することで、ClassLoaderWrapperはシステムに返されて正しいクラス・ローダーが使用されていることを確認できます.ClassLoaderWrapperを使用するのは、ClassLoaderオブジェクトを使用するのと同じです.ClassLoaderWrapperは、パッケージされたClassLoaderオブジェクトを指定した順序で順次検出し、そこから使用可能な最初のClassLoaderを選択して関連機能を完了します.
ClassLoaderWrapperの主な機能は、getResourceAsURL()メソッド、classForName()メソッド、getResourceAsStream()メソッドの3つに分類されます.この3つのメソッドには、最終的にはStringとClassLoader[]のパラメータのリロードが呼び出されます.

/**
 *            ,       
 *
 * @author kaifeng
 * @author Clinton Begin
 */
public class ClassLoaderWrapper {

    //      
    ClassLoader defaultClassLoader;
    //      
    ClassLoader systemClassLoader;

    /**
     *       
     */
    ClassLoaderWrapper() {
        try {
            //         
            systemClassLoader = ClassLoader.getSystemClassLoader();
        } catch (SecurityException ignored) {
            // AccessControlException on Google App Engine
        }
    }

    //region getResourceAsURL     

    /**
     *              URL
     *
     * @param resource       
     * @return   URL null
     */
    public URL getResourceAsURL(String resource) {
        return getResourceAsURL(resource, getClassLoaders(null));
    }

    /**
     * Get a resource from the classpath, starting with a specific class loader
     *
     * @param resource    -       
     * @param classLoader -        
     * @return   URL null
     */
    public URL getResourceAsURL(String resource, ClassLoader classLoader) {
        return getResourceAsURL(resource, getClassLoaders(classLoader));
    }
    //endregion

    //region getResourceAsStream     

    /**
     *          
     *
     * @param resource -       
     * @return         null
     */
    public InputStream getResourceAsStream(String resource) {
        return getResourceAsStream(resource, getClassLoaders(null));
    }

    /**
     *                
     *
     * @param resource    -       
     * @param classLoader -        
     * @return         null
     */
    public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
        return getResourceAsStream(resource, getClassLoaders(classLoader));
    }
    //endregion

    //region classForName     

    /**
     *          
     *
     * @param name -        
     * @return -         
     * @throws ClassNotFoundException        
     */
    public Class> classForName(String name) throws ClassNotFoundException {
        return classForName(name, getClassLoaders(null));
    }

    /**
     *        ,                
     *
     * @param name        -        
     * @param classLoader -            
     * @return -         
     * @throws ClassNotFoundException        
     */
    public Class> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
        return classForName(name, getClassLoaders(classLoader));
    }
    //endregion

    /**
     *          ,              
     *
     * @param resource    -       
     * @param classLoader -       
     * @return         null
     */
    InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
        for (ClassLoader cl : classLoader) {
            if (null != cl) {

                //                
                InputStream returnValue = cl.getResourceAsStream(resource);

                //         ,           “/”,      
                if (null == returnValue) {
                    returnValue = cl.getResourceAsStream("/" + resource);
                }
                //        
                if (null != returnValue) {
                    return returnValue;
                }
            }
        }
        return null;
    }

    /**
     *          ,              
     *
     * @param resource    -        
     * @param classLoader -            
     * @return         null
     */
    URL getResourceAsURL(String resource, ClassLoader[] classLoader) {

        URL url;

        for (ClassLoader cl : classLoader) {

            if (null != cl) {

                //                
                url = cl.getResource(resource);

                //         ,           “/”,      
                if (null == url) {
                    url = cl.getResource("/" + resource);
                }

                //        
                if (null != url) {
                    return url;
                }

            }

        }

        //      null
        return null;

    }

    /**
     *        ,               
     *
     * @param name        -       
     * @param classLoader -          
     * @return the class
     * @throws ClassNotFoundException -        
     */
    Class> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {

        for (ClassLoader cl : classLoader) {

            if (null != cl) {

                try {

                    Class> c = Class.forName(name, true, cl);

                    if (null != c) {
                        return c;
                    }

                } catch (ClassNotFoundException e) {
                    // we'll ignore this until all classloaders fail to locate the class
                }

            }

        }

        throw new ClassNotFoundException("Cannot find class: " + name);

    }

    /**
     *         ,            
     * 

* classLoader * defaultClassLoader * Thread.currentThread().getContextClassLoader() * getClass().getClassLoader() * systemClassLoader *

* * @param classLoader */
ClassLoader[] getClassLoaders(ClassLoader classLoader) { return new ClassLoader[]{ classLoader, defaultClassLoader, Thread.currentThread().getContextClassLoader(), getClass().getClassLoader(), systemClassLoader}; } }

Resourcesは、ClassLoaderWrapperタイプの静的フィールドがカプセル化された複数の静的メソッドを提供するツールクラスであり、Resourcesが提供するこれらの静的ツールは、ClassLoaderWrapperオブジェクトを呼び出す対応するメソッドによって実現される.
ResolverUtil
ResolverUtilは、指定された条件に基づいて指定されたパッケージの下のクラスを検索することができ、使用される条件はTestインタフェースによって表される.ResolverUtilでclassLoaderフィールド(ClassLoaderタイプ)を使用して現在使用されているクラスローダが記録されています.デフォルトでは、現在のスレッドコンテキストにバインドされているClassLoaderが使用されています.setClassLoader()メソッドでクラスローダの使用を変更できます.
MyBatisは、図のようにIsaとAnnotatedWithの2つの一般的なTestインタフェース実装を提供しています.Isaはクラスが指定したクラスまたはインタフェースを継承しているかどうかを検出するために使用され、AnnotatedWithはクラスが指定した注釈を追加しているかどうかを検出するために使用されます.
ResolverUtil.findImplementations()メソッドとResolverUtil.findAnnotated()メソッドはいずれもResolverUtil.find()メソッドに依存して実現され、findImplementations()メソッドはIsaオブジェクトを検出条件として作成し、findAnnotated()メソッドはAnnotatedWithオブジェクトを検出条件として作成します.
/**
 * ResolverUtil                 ,        Test    。
 *
 * @author kaifeng
 * @author Tim Fennell
 */
public class ResolverUtil<T> {
    /**
     *              
     */
    private static final Log log = LogFactory.getLog(ResolverUtil.class);

    /**
     *        ,       ,          ResolverUtil      。
     */
    public interface Test {
        /**
         *            ,   True,    false。
         *
         * @param type      
         */
        boolean matches(Class> type);
    }

    /**
     *                  ,
     */
    public static class IsA implements Test {
        private Class> parent;

        /**
         *               
         */
        public IsA(Class> parentType) {
            this.parent = parentType;
        }

        /**
         *                   true
         */
        @Override
        public boolean matches(Class> type) {
            return type != null && parent.isAssignableFrom(type);
        }

        @Override
        public String toString() {
            return "is assignable to " + parent.getSimpleName();
        }
    }

    /**
     *                
     */
    public static class AnnotatedWith implements Test {
        private Class extends Annotation> annotation;

        /**
         *           
         */
        public AnnotatedWith(Class extends Annotation> annotation) {
            this.annotation = annotation;
        }

        /**
         *                    ,   true
         */
        @Override
        public boolean matches(Class> type) {
            return type != null && type.isAnnotationPresent(annotation);
        }

        @Override
        public String toString() {
            return "annotated with @" + annotation.getSimpleName();
        }
    }

    /**
     * The set of matches being accumulated.
     */
    private Setextends T>> matches = new HashSetextends T>>();

    /**
     *     ,    Thread.currentThread().getContextClassLoader() will be used.
     */
    private ClassLoader classloader;

    /**
     *         
     *
     * @return       
     */
    public Setextends T>> getClasses() {
        return matches;
    }

    /**
     *       ,               
     *
     * @return           
     */
    public ClassLoader getClassLoader() {
        return classloader == null ? Thread.currentThread().getContextClassLoader() : classloader;
    }

    /**
     *       
     *
     * @param classloader        
     */
    public void setClassLoader(ClassLoader classloader) {
        this.classloader = classloader;
    }

    /**
     *                  
     *
     * @param parent            
     * @param packageNames        
     */
    public ResolverUtil findImplementations(Class> parent, String... packageNames) {
        if (packageNames == null) {
            return this;
        }

        Test test = new IsA(parent);
        for (String pkg : packageNames) {
            find(test, pkg);
        }

        return this;
    }

    /**
     *                 
     *
     * @param annotation          
     * @param packageNames        
     */
    public ResolverUtil findAnnotated(Class extends Annotation> annotation, String... packageNames) {
        if (packageNames == null) {
            return this;
        }

        Test test = new AnnotatedWith(annotation);
        for (String pkg : packageNames) {
            find(test, pkg);
        }

        return this;
    }

    /**
     *              ,        
     *
     * @param test                
     * @param packageName    e.g. {@code net.sourceforge.stripes}
     */
    public ResolverUtil find(Test test, String packageName) {
        //          
        String path = getPackagePath(packageName);

        try {
            List children = VFS.getInstance().list(path);
            for (String child : children) {
                if (child.endsWith(".class")) {
                    //       test  
                    addIfMatching(test, child);
                }
            }
        } catch (IOException ioe) {
            log.error("Could not read package: " + packageName, ioe);
        }

        return this;
    }

    /**
     *           
     *
     * @param packageName   
     */
    protected String getPackagePath(String packageName) {
        return packageName == null ? null : packageName.replace('.', '/');
    }

    /**
     *           test  
     *
     * @param test     
     * @param fqn           ,        
     */
    @SuppressWarnings("unchecked")
    protected void addIfMatching(Test test, String fqn) {
        try {
            String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
            ClassLoader loader = getClassLoader();
            if (log.isDebugEnabled()) {
                log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
            }

            Class> type = loader.loadClass(externalName);
            if (test.matches(type)) {
                matches.add((Class) type);
            }
        } catch (Throwable t) {
            log.warn("Could not examine class '" + fqn + "'" + " due to a " +
                    t.getClass().getName() + " with message: " + t.getMessage());
        }
    }
}

ResolverUtilの使い方
// pkg1,pkg2      ActionBean 
ResolverUtil resolver = new ResolverUtil();

// pkg1,pkg2      ActionBean  
resolver.findImplementation(ActionBean.class, pkg1, pkg2);
resolver.find(new CustomTest(), pkg1);// pkg1       CustomTest  
resolver.find(new CustomTest(), pkg2);// pkg2       CustomTest  
//         
Collection beans = resolver.getClasses();

VFS
VFSは、指定されたパスの下にあるリソースを検索する仮想ファイルシステム(Virtual File System)を表します.VFSは抽象クラスであり、MyBatisでは、図に示すように、JBoss 6 VFSとDefaultVFSの2つのVFSの実装が提供されている.ユーザーは、カスタムVFS実装クラスを提供することもできます.
VFSにはlist(URL,String)とisValid()の2つの抽象メソッドが定義されており、isValid()は現在のVFSオブジェクトが現在の環境で有効かどうかを検出し、list(URL,String)メソッドは指定したリソース名のリストを検索し、ResolverUtil.find()メソッドがクラスファイルを検索するとlist()メソッドのリロードメソッドが呼び出され、このリロードは最終的にlist(list)メソッドを呼び出す(URL、String)このリロード.
/**
 * VFS        (Virtual File System),             。
 * VFS      ,MyBatis    JBoss6VFS   DefaultVFS  VFS   。
 *
 * @author kaifeng
 * @author Ben Gunter
 */
public abstract class VFS {
    private static final Log log = LogFactory.getLog(VFS.class);

    /**
     *     VFS   
     */
    public static final Class>[] IMPLEMENTATIONS = {JBoss6VFS.class, DefaultVFS.class};

    /**
     *         VFS   ,   {@link #addImplClass(Class)}.          USER_IMPLEMENTATIONS   
     */
    public static final List> USER_IMPLEMENTATIONS = new ArrayList>();

    /**
     *     ,      VFS
     */
    private static class VFSHolder {
        static final VFS INSTANCE = createVFS();

        @SuppressWarnings("unchecked")
        static VFS createVFS() {
            //           VFS   ,       
            List> impls = new ArrayList>();
            impls.addAll(USER_IMPLEMENTATIONS);
            impls.addAll(Arrays.asList((Class extends VFS>[]) IMPLEMENTATIONS));

            //      ,     VFS             ,               
            VFS vfs = null;
            for (int i = 0; vfs == null || !vfs.isValid(); i++) {
                Class extends VFS> impl = impls.get(i);
                try {
                    vfs = impl.newInstance();
                    if (vfs == null || !vfs.isValid()) {
                        if (log.isDebugEnabled()) {
                            log.debug("VFS implementation " + impl.getName() +
                                    " is not valid in this environment.");
                        }
                    }
                } catch (InstantiationException e) {
                    log.error("Failed to instantiate " + impl, e);
                    return null;
                } catch (IllegalAccessException e) {
                    log.error("Failed to instantiate " + impl, e);
                    return null;
                }
            }

            if (log.isDebugEnabled()) {
                log.debug("Using VFS adapter " + vfs.getClass().getName());
            }

            return vfs;
        }
    }

    /**
     *       VFS     
     */
    public static VFS getInstance() {
        return VFSHolder.INSTANCE;
    }

    /**
     *       VFS        USER_IMPLEMENTATIONS
     *
     * @param clazz    VFS   
     */
    public static void addImplClass(Class extends VFS> clazz) {
        if (clazz != null) {
            USER_IMPLEMENTATIONS.add(clazz);
        }
    }

    /**
     *        
     */
    protected static Class> getClass(String className) {
        try {
            return Thread.currentThread().getContextClassLoader().loadClass(className);
//      return ReflectUtil.findClass(className);
        } catch (ClassNotFoundException e) {
            if (log.isDebugEnabled()) {
                log.debug("Class not found: " + className);
            }
            return null;
        }
    }

    /**
     *       ,              
     *
     * @param clazz                 
     * @param methodName        
     * @param parameterTypes     
     */
    protected static Method getMethod(Class> clazz, String methodName, Class>... parameterTypes) {
        if (clazz == null) {
            return null;
        }
        try {
            return clazz.getMethod(methodName, parameterTypes);
        } catch (SecurityException e) {
            log.error("Security exception looking for method " + clazz.getName() + "." + methodName + ".  Cause: " + e);
            return null;
        } catch (NoSuchMethodException e) {
            log.error("Method not found " + clazz.getName() + "." + methodName + "." + methodName + ".  Cause: " + e);
            return null;
        }
    }

    /**
     *                 
     *
     * @param method            
     * @param object          
     * @param parameters     
     * @return           
     * @throws IOException      If I/O errors occur
     * @throws RuntimeException If anything else goes wrong
     */
    @SuppressWarnings("unchecked")
    protected static  T invoke(Method method, Object object, Object... parameters)
            throws IOException, RuntimeException {
        try {
            return (T) method.invoke(object, parameters);
        } catch (IllegalArgumentException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            if (e.getTargetException() instanceof IOException) {
                throw (IOException) e.getTargetException();
            } else {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     *        ,                        URL
     *
     * @param path        
     * @return      URL   {@link ClassLoader#getResources(String)}.
     * @throws IOException If I/O errors occur
     */
    protected static List getResources(String path) throws IOException {
        return Collections.list(Thread.currentThread().getContextClassLoader().getResources(path));
    }

    /**
     *     VFS            
     */
    public abstract boolean isValid();

    /**
     *            ,
     *
     * @param url       url  
     * @param forPath URL        
     * @return          
     * @throws IOException If I/O errors occur
     */
    protected abstract List list(URL url, String forPath) throws IOException;

    /**
     *                       。
     *
     * @param path     
     * @return            
     * @throws IOException If I/O errors occur
     */
    public List list(String path) throws IOException {
        List names = new ArrayList();
        for (URL url : getResources(path)) {
            names.addAll(list(url, path));
        }
        return names;
    }
}