MyBatis源码之IO模块

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

1. ClassLoaderWrapper

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

1.1 构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 默认 ClassLoader 对象,
* 可通过ClassLoaderWrapper.defaultClassLoader = xxx 的方式初始化
*/
ClassLoader defaultClassLoader;
/**
* 系统 ClassLoader 对象, 由构造方法初始化
*/
ClassLoader systemClassLoader;

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

1.2 getClassLoaders方法

获得 ClassLoader 数组方法

1
2
3
4
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
return new ClassLoader[] { classLoader, defaultClassLoader, Thread.currentThread().getContextClassLoader(),
getClass().getClassLoader(), systemClassLoader };
}

1.3 getResourceAsURL

获得指定资源的URL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 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));
}

1.3.1 getResourceAsURL方法,

调用该方法, 遍历ClassLoaders, 获得指定的URL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/**
* Get a resource as a URL using the current class path
*
* @param resource
* - the resource to locate
* @param classLoader
* - the class loaders to examine
*
* @return the resource or null
*/
URL getResourceAsURL(String resource, ClassLoader[] classLoader) {

URL url;
// 遍历classLoader数组
for (ClassLoader cl : classLoader) {

if (null != cl) {
// 获得不带 / 的 URL
// look for the resource as passed in...
url = cl.getResource(resource);
// 获得带 / 的 URL
// ...but some class loaders want this leading "/", so we'll add it
// and try again if we didn't find the resource
if (null == url) {
url = cl.getResource("/" + resource);
}

// "It's always in the last place I look for it!"
// ... because only an idiot would keep looking for it after finding it, so stop looking already.
if (null != url) {
return url;
}

}

}

// didn't find it anywhere.
return null;

}

1.4 getResourceAsStream

获得指定资源的 InputStream 对象, 先获取ClassLoaders数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/**
* 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));
}

1.4.1 getResourceAsStream

获得指定资源的InputStream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* Try to get a resource from a group of classloaders
*
* @param resource
* - the resource to get
* @param classLoader
* - the classloaders to examine
*
* @return the resource or null
*/
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
// 遍历 classLoader 数组
for (ClassLoader cl : classLoader) {
if (null != cl) {

// try to find the resource as passed
// 获得不带 / 的InputStream
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;
}

1.5 classForName

获得指定类名对应的类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/**
* 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));
}

1.5.1 Class<?> classForName(String name, ClassLoader[] classLoader)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Attempt to load a class from a group of classloaders
*
* @param name
* - the class to load
* @param classLoader
* - the group of classloaders to examine
*
* @return the class
*
* @throws ClassNotFoundException
* - Remember the wisdom of Judge Smails: Well, the world needs ditch diggers, too.
*/
Class<?> classForName(String name, ClassLoader[] classLoader) throws ClassNotFoundException {

// 遍历 classLoader 数组
for (ClassLoader cl : classLoader) {

if (null != cl) {

try {
// 知道有一成功类找到, 返回
return Class.forName(name, true, cl);

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

}

}

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

}

2. Resources

Resource 工具类, 基本是对ClassLoaderWrapper的封装, 底层是调用wrapper方法 所在包: org.apache.ibatis.io.Resources

2.1 构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
private static final ClassLoaderWrapper classLoaderWrapper = new ClassLoaderWrapper();

/**
* Charset to use when calling getResourceAsReader. null means use the system default.
*/
private static Charset charset;

private Resources() {
}

/**
* Returns the default classloader (may be null).
*
* @return The default classloader
*/
public static ClassLoader getDefaultClassLoader() {
return classLoaderWrapper.defaultClassLoader;
}

/**
* Sets the default classloader
*
* @param defaultClassLoader
* - the new default ClassLoader
*/
public static void setDefaultClassLoader(ClassLoader defaultClassLoader) {
classLoaderWrapper.defaultClassLoader = defaultClassLoader;
}

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

2.2getResource

基于 classLoaderWrapper 属性的封装, 最终是调用 classLoaderWrapper, 获得指定资源的 URL / InputStream

2.2.1 getResourceURL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* Returns the URL of the resource on the classpath
*
* @param resource
* The resource to find
*
* @return The resource
*
* @throws java.io.IOException
* If the resource cannot be found or read
*/
public static URL getResourceURL(String resource) throws IOException {
// issue #625
return getResourceURL(null, resource);
}

/**
* Returns the URL of the resource on the classpath
*
* @param loader
* The classloader used to fetch the resource
* @param resource
* The resource to find
*
* @return The resource
*
* @throws java.io.IOException
* If the resource cannot be found or read
*/
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;
}

2.2.2 getResourceAsStream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Returns a resource on the classpath as a Stream object
*
* @param resource
* The resource to find
*
* @return The resource
*
* @throws java.io.IOException
* If the resource cannot be found or read
*/
public static InputStream getResourceAsStream(String resource) throws IOException {
return getResourceAsStream(null, resource);
}

/**
* Returns a resource on the classpath as a Stream object
*
* @param loader
* The classloader used to fetch the resource
* @param resource
* The resource to find
*
* @return The resource
*
* @throws java.io.IOException
* If the resource cannot be found or read
*/
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;
}

…. 省略其它代码 了解classLoaderWrapper即可

2.3 getUrl

获得指定URL的Reader / Properties

2.3.1 getUrlAsStream(String urlString)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Gets a URL as an input stream
*
* @param urlString
* - the URL to get
*
* @return An input stream with the data from the URL
*
* @throws java.io.IOException
* If the resource cannot be found or read
*/
public static InputStream getUrlAsStream(String urlString) throws IOException {
URL url = new URL(urlString);
URLConnection conn = url.openConnection();
return conn.getInputStream();
}

2.3.2 getUrlAsReader(String urlString)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* Gets a URL as a Reader
*
* @param urlString
* - the URL to get
*
* @return A Reader with the data from the URL
*
* @throws java.io.IOException
* If the resource cannot be found or read
*/
public static Reader getUrlAsReader(String urlString) throws IOException {
Reader reader;
if (charset == null) {
reader = new InputStreamReader(getUrlAsStream(urlString));
} else {
reader = new InputStreamReader(getUrlAsStream(urlString), charset);
}
return reader;
}

2.3.3 getUrlAsProperties(String urlString)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
* Gets a URL as a Properties object
*
* @param urlString
* - the URL to get
*
* @return A Properties object with the data from the URL
*
* @throws java.io.IOException
* If the resource cannot be found or read
*/
public static Properties getUrlAsProperties(String urlString) throws IOException {
Properties props = new Properties();
try (InputStream in = getUrlAsStream(urlString)) {
props.load(in);
}
return props;
}

2.3.4 classForName

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* Loads a class
*
* @param className
* - the class to fetch
*
* @return The loaded class
*
* @throws ClassNotFoundException
* If the class cannot be found (duh!)
*/
public static Class<?> classForName(String className) throws ClassNotFoundException {
return classLoaderWrapper.classForName(className);
}

3. ResolverUtil

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

3.1 Test

匹配判断接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 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.
*
* @param type
* the type
*
* @return true, if successful
*/
boolean matches(Class<?> type);
}

3.1.1 ISA

实现 Test 接口, 判断是否为指定类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**
* 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 {

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

/**
* Constructs an IsA test using the supplied Class as the parent class/interface.
*
* @param parentType
* the parent type
*/
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);
}

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

3.1.2 AnnotatedWith

实现 Test 接口, 判断是否有指定注解

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 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 {

/** The annotation. */
private final Class<? extends Annotation> annotation;

/**
* Constructs an AnnotatedWith test for the specified annotation type.
*
* @param annotation
* the annotation
*/
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);
}

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

3.2 构造方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
/** The set of matches being accumulated. */
// 符合条件的类的集合
private Set<Class<? extends T>> matches = new HashSet<>();

/**
* The ClassLoader to use when looking for classes. If null then the ClassLoader returned by
* Thread.currentThread().getContextClassLoader() will be used.
*/
private ClassLoader classloader;

/**
* Provides access to the classes discovered so far. If no calls have been made to any of the {@code find()} methods,
* this set will be empty.
*
* @return the set of classes that have been discovered.
*/
public Set<Class<? extends T>> getClasses() {
return matches;
}

/**
* Returns the classloader that will be used for scanning for classes. If no explicit ClassLoader has been set by the
* calling, the context class loader will be used.
*
* @return the ClassLoader that will be used to scan for classes
*/
public ClassLoader getClassLoader() {
return classloader == null ? Thread.currentThread().getContextClassLoader() : classloader;
}

/**
* Sets an explicit ClassLoader that should be used when scanning for classes. If none is set then the context
* classloader will be used.
*
* @param classloader
* a ClassLoader to use when scanning for classes
*/
public void setClassLoader(ClassLoader classloader) {
this.classloader = classloader;
}

3.3 find

获得指定包

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public ResolverUtil<T> find(Test test, String packageName) {
// 获取包路径
String path = getPackagePath(packageName);

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

return this;
}

3.3.1 getPackagePath

1
2
3
protected String getPackagePath(String packageName) {
return packageName == null ? null : packageName.replace('.', '/');
}

3.3.2 addIfMatching

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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);
// 判断匹配, 使用对应的test匹配
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());
}
}

3.4 findImplementations

判断指定目录下, 符合指定类的类们

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

3.5 findAnnotated

判断指定目录下, 符合指定注解的类们

1
2
3
4
5
6
7
8
9
10
11
12
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;
}

4. 5. VFS

虚拟文件系统( Virtual File System )抽象类,用来查找指定路径下的的文件们。不太重要后续补充