zoukankan      html  css  js  c++  java
  • Mybatis源码学习之资源加载(六)

    image

    类加载器简介

    Java虚拟机中的类加载器(ClassLoader)负责加载来自文件系统、网络或其他来源的类文件。Java虚拟机中的类加载器默认使用的是双亲委派模式,如图所示,其中有三种默认使用的类加载器,分别是Bootstrap ClassLoader、Extension ClassLoader和System ClassLoader(也被称为Application ClassLoader),每种类加载器都已经确定从哪个位置加载类文件。

    image

    Bootstrap ClassLoader: 加载JDK自带的rt.jar包中的中的类文件,它是所有类加载器的父加载器

    Extension ClassLoader: 负责加载Java的扩展类库,从jre/lib/ext目录下或者java.ext.dirs系统属性指定的目录下加载类。

    System ClassLoader: 负责从classpath环境变量中加载类文件,它是Extension ClassLoader的子加载器。

    根据双亲委派模式,在加载类文件时,子加载器首先会将加载请求委托给它的父加载器。父加载器会检测自己是否已经加载过该类,如果已加载则加载过程结束;如果未加载则请求继续向上传递,直到Bootstrap ClassLoader。如果在请求向上委托的过程中,始终未检测到该类已加载,则从Bootstrap ClassLoader开始尝试从其对应路径中加载该类文件,如果加载失败则由子加载器继续尝试加载,直至发起加载请求的子加载器位为止。

    除了系统提供三种类加载器,也可以通过继承 java.lang.ClassLoader类的方式实现自己的类加载器,以满足一些特殊的需求,例如Tomcat、JBoss等都涉及了自定义类加载器的使用。

    ClassLoaderWrapper

    在Mybatis IO包中提供的ClassLoaderWrapper是一个ClassLoader的包装器,其中包含了多个ClassLoader对象。通过调整多个类加载器的使用顺序,ClassLoaderWrapper可以确保返回给系统使用的是正确的类加载器。使用ClassLoaderWrapper就如同使用一个ClassLoader对象,ClassLoaderWrapper会按照指定的顺序依次检测其中封装的ClassLoader对象,并从中选取第一个可用的ClassLoader完成相关功能。

    ClassLoaderWrapper的主要功能可以分为三类,分别是getResourceAsURL()方法、classForName()方法、getResourceAsStream()方法,这三个方法都有多个重载,这三类方法最终都会调用参数为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);
    
        }
    
        /**
         * 构造类加载器数组,指定了类加载器的使用顺序
         * <p>
         * classLoader 指定的类加载器
         * defaultClassLoader 指定的默认类加载器
         * Thread.currentThread().getContextClassLoader() 当前线程绑定的类加载器
         * getClass().getClassLoader() 加载当前类所使用的类加载器
         * systemClassLoader 系统类加载器
         * </p>
         *
         * @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提供了两个常用的Test接口实现,分别是IsA和AnnotatedWith,如图。IsA用于检测类是否继承了指定的类或接口,AnnotatedWith用于检测类是否添加了指定的注解。

    ResolverUtil.findImplementations()方法和ResolverUtil.findAnnotated()方法都是依赖ResolverUtil.find()方法实现的,findImplementations()方法会创建IsA对象作为检测条件,findAnnotated()方法会创建AnnotatedWith对象作为检测条件。

    image

    /**
     * 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 Set<Class<? extends T>> matches = new HashSet<Class<? extends T>>();
    
        /**
         * 类加载器,默认使用Thread.currentThread().getContextClassLoader() will be used.
         */
        private ClassLoader classloader;
    
        /**
         * 获取匹配的类集合
         *
         * @return 匹配的类集合
         */
        public Set<Class<? extends 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<T> 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<T> 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<T> find(Test test, String packageName) {
            //根据包名获取包的路径
            String path = getPackagePath(packageName);
    
            try {
                List<String> 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<T>) 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<ActionBean> resolver = new ResolverUtil<ActionBean>();
    
    //在pkg1,pkg2中查找继承了ActionBean的类
    resolver.findImplementation(ActionBean.class, pkg1, pkg2);
    resolver.find(new CustomTest(), pkg1);//在pkg1中查找符合条件CustomTest的类
    resolver.find(new CustomTest(), pkg2);//在pkg2中查找符合条件CustomTest的类
    //收集上述三个的结果
    Collection<ActionBean> beans = resolver.getClasses();

    VFS

    VFS表示虚拟文件系统(Virtual File System),它用来查找指定路径下的资源。VFS是一个抽象类,MyBatis中提供了JBoss6VFS 和 DefaultVFS两个VFS的实现,如图所示。用户也可以提供自定义的VFS实现类

    image

    VFS中定义了list(URL, String)和isValid()两个抽象方法,
    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<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<Class<? extends VFS>>();
    
        /**
         * 单例模式,记录全局唯一VFS
         */
        private static class VFSHolder {
            static final VFS INSTANCE = createVFS();
    
            @SuppressWarnings("unchecked")
            static VFS createVFS() {
                // 优先使用用户自定义的VFS实现类,其次使用系统的
                List<Class<? extends VFS>> impls = new ArrayList<Class<? extends VFS>>();
                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> 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<URL> 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<String> list(URL url, String forPath) throws IOException;
    
        /**
         * 递归列出在指定路径中找到的所有的完整资源路径。
         *
         * @param path 资源路径
         * @return 所有的完整资源路径集合
         * @throws IOException If I/O errors occur
         */
        public List<String> list(String path) throws IOException {
            List<String> names = new ArrayList<String>();
            for (URL url : getResources(path)) {
                names.addAll(list(url, path));
            }
            return names;
        }
    }
  • 相关阅读:
    MySQL 操作命令梳理(1)-- 索引
    Linux下对LVM逻辑卷分区大小调整 [针对xfs和ext4文件系统]
    CentOS6 虚拟机安装后,无Iptables配置文件
    Linux系统权限设置
    完整部署CentOS7.2+OpenStack+kvm 云平台环境(2)--云硬盘等后续配置
    完整部署CentOS7.2+OpenStack+kvm 云平台环境(3)--为虚拟机指定固定ip
    Android消息推送怎么实现?
    Android 下拉刷新
    Windows 10 周年更新正式版下载 + win10 快捷键
    markdown编辑器使用建议
  • 原文地址:https://www.cnblogs.com/liukaifeng/p/10052614.html
Copyright © 2011-2022 走看看