精尽 MyBatis 源码分析 —— IO 模块

1. 概述

资源加载模块,主要是对类加载器进行封装,确定类加载器的使用顺序,并提供了加载类文件以及其他资源文件的功能 。

2. ClassLoaderWrapper

org.apache.ibatis.io.ClassLoaderWrapper ,ClassLoader 包装器。可使用多个 ClassLoader 加载对应的资源,直到有一成功后返回资源。

2.1 构造方法
// ClassLoaderWrapper.java

/**
 * 默认 ClassLoader 对象
 */
ClassLoader defaultClassLoader;
/**
 * 系统 ClassLoader 对象
 */
ClassLoader systemClassLoader;

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

defaultClassLoader 属性,默认 ClassLoader 对象。目前不存在初始化该属性的构造方法。可通过 ClassLoaderWrapper.defaultClassLoader = xxx 的方式,进行设置。
systemClassLoader 属性,系统 ClassLoader 对象。在构造方法中,已经初始化。

2.2 getClassLoaders

#getClassLoaders(ClassLoader classLoader) 方法,获得 ClassLoader 数组。代码如下:

// ClassLoaderWrapper.java

ClassLoader[] getClassLoaders(ClassLoader classLoader) {
    return new ClassLoader[]{
            classLoader,
            defaultClassLoader,
            Thread.currentThread().getContextClassLoader(),
            getClass().getClassLoader(),
            systemClassLoader};
}
2.3 getResourceAsURL

#getResourceAsURL(String resource, …) 方法,获得指定资源的 URL 。代码如下:

// ClassLoaderWrapper.java

/**
 * Get a resource as a URL using the current class path
 *
 * @param resource - the resource to locate
 * @return the resource or 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    - the resource to find
 * @param classLoader - the first classloader to try
 * @return the stream or null
 */
public URL getResourceAsURL(String resource, ClassLoader classLoader) {
    return getResourceAsURL(resource, getClassLoaders(classLoader));
}
2.4 getResourceAsStream

#getResourceAsStream(String resource, …) 方法,获得指定资源的 InputStream 对象。代码如下:

// ClassLoaderWrapper.java

/**
 * Get a resource from the classpath
 *
 * @param resource - the resource to find
 * @return the stream or null
 */
public InputStream getResourceAsStream(String resource) {
    return getResourceAsStream(resource, getClassLoaders(null));
}

/**
 * Get a resource from the classpath, starting with a specific class loader
 *
 * @param resource    - the resource to find
 * @param classLoader - the first class loader to try
 * @return the stream or null
 */
public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
    return getResourceAsStream(resource, getClassLoaders(classLoader));
}

先调用 #getClassLoaders(ClassLoader classLoader) 方法,获得 ClassLoader 数组。
再调用 #getResourceAsStream(String resource, ClassLoader[] classLoader) 方法,获得指定资源的 InputStream 。代码如下:

// ClassLoaderWrapper.java

InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    // 遍历 ClassLoader 数组
    for (ClassLoader cl : classLoader) {
        if (null != cl) {
            // 获得 InputStream ,不带 /
            // try to find the resource as passed
            InputStream returnValue = cl.getResourceAsStream(resource);
            // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
            // 获得 InputStream ,带 /
            if (null == returnValue) {
                returnValue = cl.getResourceAsStream("/" + resource);
            }

            // 成功获得到,返回
            if (null != returnValue) {
                return returnValue;
            }
        }
    }
    return null;
}
2.5 classForName

#classForName(String name, …) 方法,获得指定类名对应的类。代码如下:

// ClassLoaderWrapper.java

/**
 * Find a class on the classpath (or die trying)
 *
 * @param name - the class to look for
 * @return - the class
 * @throws ClassNotFoundException Duh.
 */
public Class<?> classForName(String name) throws ClassNotFoundException {
    return classForName(name, getClassLoaders(null));
}

/**
 * Find a class on the classpath, starting with a specific classloader (or die trying)
 *
 * @param name        - the class to look for
 * @param classLoader - the first classloader to try
 * @return - the class
 * @throws ClassNotFoundException Duh.
 */
public Class<?> classForName(String name, ClassLoader classLoader) throws ClassNotFoundException {
    return classForName(name, getClassLoaders(classLoader));
}

先调用 #getClassLoaders(ClassLoader classLoader) 方法,获得 ClassLoader 数组。
再调用 #classForName(String name, ClassLoader[] classLoader) 方法,获得指定类名对应的类。代码如下:

    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);
  }

3. Resources

org.apache.ibatis.io.Resources ,Resource 工具类。

3.1 构造方法
/**
 * ClassLoaderWrapper 对象
 */
private static ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();

/**
 * 字符集
 */
private static Charset charset;

Resources() {
}

public static void setDefaultClassLoader(ClassLoader defaultClassLoader) {defaultClassLoader
    classLoaderWrapper.defaultClassLoader = defaultClassLoader; // 修改 ClassLoaderWrapper.
}

public static void setCharset(Charset charset) {
    Resources.charset = charset;
}
3.2 getResource

基于 classLoaderWrapper 属性的封装。

3.2.1 getResourceURL

#getResourceURL(String resource) 静态方法,获得指定资源的 URL 。代码如下:

// Resources.java

public static URL getResourceURL(String resource) throws IOException {
    // issue #625
    return getResourceURL(null, resource);
}

public static URL getResourceURL(ClassLoader loader, String resource) throws IOException {
    URL url = classLoaderWrapper.getResourceAsURL(resource, loader);
    if (url == null) {
        throw new IOException("Could not find resource " + resource);
    }
    return url;
}
3.2.2 getResourceAsStream

#getResourceAsStream(String resource) 静态方法,获得指定资源的 InputStream 。代码如下:

// Resources.java

public static InputStream getResourceAsStream(String resource) throws IOException {
    return getResourceAsStream(null, resource);
}

public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
    InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
    if (in == null) {
        throw new IOException("Could not find resource " + resource);
    }
    return in;
}
3.2.3 getResourceAsReader

#getResourceAsReader(String resource) 静态方法,获得指定资源的 Reader 。代码如下:

// Resources.java

public static Reader getResourceAsReader(String resource) throws IOException {
    Reader reader;
    if (charset == null) {
        reader = new InputStreamReader(getResourceAsStream(resource));
    } else {
        reader = new InputStreamReader(getResourceAsStream(resource), charset);
    }
    return reader;
}

public static Reader getResourceAsReader(ClassLoader loader, String resource) throws IOException {
    Reader reader;
    if (charset == null) {
        reader = new InputStreamReader(getResourceAsStream(loader, resource));
    } else {
        reader = new InputStreamReader(getResourceAsStream(loader, resource), charset);
    }
    return reader;
}

4. ResolverUtil

org.apache.ibatis.io.ResolverUtil ,解析器工具类,用于获得指定目录符合条件的类们。

4.1 Test

Test ,匹配判断接口。代码如下:

// ResolverUtil.java 内部类

/**
 * A simple interface that specifies how to test classes to determine if they
 * are to be included in the results produced by the ResolverUtil.
 */
public interface Test {

    /**
     * Will be called repeatedly with candidate classes. Must return True if a class
     * is to be included in the results, false otherwise.
     */
    boolean matches(Class<?> type);

}
4.1.1 IsA

IsA ,实现 Test 接口,判断是否为指定类。代码如下:

// ResolverUtil.java 内部类

/**
 * A Test that checks to see if each class is assignable to the provided class. Note
 * that this test will match the parent type itself if it is presented for matching.
 */
public static class IsA implements Test {

    /**
     * 指定类
     */
    private Class<?> parent;

    /** Constructs an IsA test using the supplied Class as the parent class/interface. */
    public IsA(Class<?> parentType) {
        this.parent = parentType;
    }

    /** Returns true if type is assignable to the parent type supplied in the constructor. */
    @Override
    public boolean matches(Class<?> type) {
        return type != null && parent.isAssignableFrom(type);
    }

}
4.1.2 AnnotatedWith

AnnotatedWith ,判断是否有指定注解。代码如下:

// ResolverUtil.java 内部类

/**
 * A Test that checks to see if each class is annotated with a specific annotation. If it
 * is, then the test returns true, otherwise false.
 */
public static class AnnotatedWith implements Test {

    /**
     * 注解
     */
    private Class<? extends Annotation> annotation;

    /** Constructs an AnnotatedWith test for the specified annotation type. */
    public AnnotatedWith(Class<? extends Annotation> annotation) {
        this.annotation = annotation;
    }

    /** Returns true if the type is annotated with the class provided to the constructor. */
    @Override
    public boolean matches(Class<?> type) {
        return type != null && type.isAnnotationPresent(annotation);
    }

}
4.2 构造方法
// ResolverUtil.java

/** The set of matches being accumulated. */
private Set<Class<? extends T>> matches = new HashSet<>(); // 符合条件的类的集合

private ClassLoader classloader;

public Set<Class<? extends T>> getClasses() {
    return matches;
}

public ClassLoader getClassLoader() {
    return classloader == null ? Thread.currentThread().getContextClassLoader() : classloader;
}
public void setClassLoader(ClassLoader classloader) {
    this.classloader = classloader;
}
4.3 find

#find(Test test, String packageName) 方法,获得指定包下,符合条件的类。代码如下:

// ResolverUtil.java

public ResolverUtil<T> find(Test test, String packageName) {
    // <1> 获得包的路径
    String path = getPackagePath(packageName);

    try {
        // <2> 获得路径下的所有文件
        List<String> children = VFS.getInstance().list(path);
        // <3> 遍历
        for (String child : children) {
            // 是 Java Class
            if (child.endsWith(".class")) {
                // 如果匹配,则添加到结果集
                addIfMatching(test, child);
            }
        }
    } catch (IOException ioe) {
        log.error("Could not read package: " + packageName, ioe);
    }

    return this;
}

调用 #addIfMatching(Test test, String fqn) 方法,如果匹配,则添加到结果集。代码如下:

// ResolverUtil.java

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());
    }
}

5. VFS

org.apache.ibatis.io.VFS ,虚拟文件系统( Virtual File System )抽象类,用来查找指定路径下的的文件们。

5.1 静态属性
// VFS.java

/** The built-in implementations. */
public static final Class<?>[] IMPLEMENTATIONS = {JBoss6VFS.class, DefaultVFS.class}; // 内置的 VFS 实现类的数组

/** The list to which implementations are added by {@link #addImplClass(Class)}. */
public static final List<Class<? extends VFS>> USER_IMPLEMENTATIONS = new ArrayList<>(); // 自定义的 VFS 实现类的数组

public static void addImplClass(Class<? extends VFS> clazz) {
    if (clazz != null) {
        USER_IMPLEMENTATIONS.add(clazz);
    }
}
5.2 getInstance

#getInstance() 方法,获得 VFS 单例。代码如下:

// VFS.java

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

private static class VFSHolder {

    static final VFS INSTANCE = createVFS();

    @SuppressWarnings("unchecked")
    static VFS createVFS() {
        // Try the user implementations first, then the built-ins
        List<Class<? extends VFS>> impls = new ArrayList<>();
        impls.addAll(USER_IMPLEMENTATIONS);
        impls.addAll(Arrays.asList((Class<? extends VFS>[]) IMPLEMENTATIONS));

        // Try each implementation class until a valid one is found
        // 创建 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 | IllegalAccessException e) {
                log.error("Failed to instantiate " + impl, e);
                return null;
            }
        }

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

        return vfs;
    }
}
5.3 反射相关方法
// VFS.java

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

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;
    }
}

protected static <T> T invoke(Method method, Object object, Object... parameters)
        throws IOException, RuntimeException {
    try {
        return (T) method.invoke(object, parameters);
    } catch (IllegalArgumentException | IllegalAccessException e) {
        throw new RuntimeException(e);
    } catch (InvocationTargetException e) {
        if (e.getTargetException() instanceof IOException) {
            throw (IOException) e.getTargetException();
        } else {
            throw new RuntimeException(e);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值