ClassLoaderWrapper
在IO包中提供的ClassLoaderWrapper是一个ClassLoader包装器,其中包含多个ClassLoader对象。
ClassLoaderWrapper的功能
-
getResourceAsURL
-
getResourceAsStream
-
classForName
public class ClassLoaderWrapper {
ClassLoader defaultClassLoader;//默认加载器 ClassLoader systemClassLoader; ClassLoaderWrapper() { try {//初始化systemClassLoader systemClassLoader = ClassLoader.getSystemClassLoader(); } catch (SecurityException ignored) { // AccessControlException on Google App Engine } } public URL getResourceAsURL(String resource, ClassLoader classLoader) { return getResourceAsURL(resource, getClassLoaders(classLoader)); } //返回ClassLoader[]数组,该数组指明类加载器的使用顺序 ClassLoader[] getClassLoaders(ClassLoader classLoader) { return new ClassLoader[]{ classLoader,//参数指定的类加载器 defaultClassLoader,//系统指定的默认类加载器 Thread.currentThread().getContextClassLoader(),//当前线程绑定的加载器 getClass().getClassLoader(),//当前类使用的加载器 systemClassLoader};//systemClassLoader } URL getResourceAsURL(String resource, ClassLoader[] classLoader) { URL url; for (ClassLoader cl : classLoader) {//遍历classLoader数组 if (null != cl) { // 调用classLoader.getResource()查找指定的资源 url = cl.getResource(resource); if (null == url) {//尝试以"/"开头,再次查找 url = cl.getResource("/" + resource); } if (null != url) {//查找到指定的资源 return url; } } } return null; } 复制代码
ResolverUtil
他可以根据指定的条件查找指定包下的类,条件由Test接口表示。
private ClassLoader classloader;//记录当前使用的类加载器(默认绑定上下文的classloader),可通过setClassLoader()设置
//Test接口的两个实现
//IsA用于检测类是否继承指定类或者接口
public static class IsA implements Test {
private Class> parent;
public IsA(Class> parentType) {
this.parent = parentType;
}
@Override
public boolean matches(Class> type) {
return type != null && parent.isAssignableFrom(type);
}
@Override
public String toString() {
return "is assignable to " + parent.getSimpleName();
}
}
//AnnotatedWith检测类是否添加指定的注解
public static class AnnotatedWith implements Test {
private Class extends Annotation> annotation;
public AnnotatedWith(Class extends Annotation> annotation) {
this.annotation = annotation;
}
@Override
public boolean matches(Class> type) {
return type != null && type.isAnnotationPresent(annotation);
}
@Override
public String toString() {
return "annotated with @" + annotation.getSimpleName();
}
}
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")) {
addIfMatching(test, child);//检测是否符合条件
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
protected void addIfMatching(Test test, String fqn) {
try {//fqn是类完全限定名
String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
ClassLoader loader = getClassLoader();
//日志
Class> type = loader.loadClass(externalName);//加载类
if (test.matches(type)) {//检测是否满足
matches.add((Class) type);//存到matches
}
} catch (Throwable t) {
}
复制代码
VFS
它用来查找指定路径下的资源。它是一个抽象类,有两个实现类(DefaultVFS,JBoss6VFS)。
public abstract class VFS {
//记录MyBatis提供的实现类
public static final Class>[] IMPLEMENTATIONS = { JBoss6VFS.class, DefaultVFS.class };
//用户自定义的实现类。VFS.addImplClass方法添加
public static final List> USER_IMPLEMENTATIONS = new ArrayList<>();
//取VFS实例,单例
public static VFS getInstance() {
return VFSHolder.INSTANCE;
}
private static class VFSHolder {
static final VFS INSTANCE = createVFS();
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()) {//isValid抽象方法
//log
}
} catch (InstantiationException e) {
return null;
}
}
return vfs;
}
}
//这个方法是VFS的抽象方法。在ResolverUtil.find中调用
//todo没仔细研究这个函数
public List list(URL url, String path) throws IOException {
InputStream is = null;
try {
List resources = new ArrayList<>();
//如果url指向的资源在一个jar包中,则获取该Jar包对应的URL,否则返回null
URL jarUrl = findJarForResource(url);
if (jarUrl != null) {
is = jarUrl.openStream();
//遍历Jar,返回以path开头的资源列表
resources = listResources(new JarInputStream(is), path);
}
else {
List children = new ArrayList<>();
try {
if (isJar(url)) {
is = url.openStream();
try (JarInputStream jarInput = new JarInputStream(is)) {
for (JarEntry entry; (entry = jarInput.getNextJarEntry()) != null; ) {
children.add(entry.getName());
}
}
}
else {
is = url.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
List lines = new ArrayList<>();
for (String line; (line = reader.readLine()) != null;) {
lines.add(line);
if (getResources(path + "/" + line).isEmpty()) {
lines.clear();
break;
}
}
if (!lines.isEmpty()) {
children.addAll(lines);
}
}
} catch (FileNotFoundException e) {
if ("file".equals(url.getProtocol())) {
File file = new File(url.getFile());
if (file.isDirectory()) {
children = Arrays.asList(file.list());
}
}
else {
throw e;
}
}
// The URL prefix to use when recursively listing child resources
String prefix = url.toExternalForm();
if (!prefix.endsWith("/")) {
prefix = prefix + "/";
}
// Iterate over immediate children, adding files and recursing into directories
for (String child : children) {
String resourcePath = path + "/" + child;
resources.add(resourcePath);
URL childUrl = new URL(prefix + child);
resources.addAll(list(childUrl, resourcePath));
}
}
return resources;
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
// Ignore
}
}
}
}
}
protected List listResources(JarInputStream jar, String path) throws IOException {
//开始和结束位置添加"/"
if (!path.startsWith("/")) {
path = "/" + path;
}
if (!path.endsWith("/")) {
path = path + "/";
}
// 遍历jar
List resources = new ArrayList<>();
for (JarEntry entry; (entry = jar.getNextJarEntry()) != null;) {
if (!entry.isDirectory()) {
String name = entry.getName();
if (!name.startsWith("/")) {
name = "/" + name;
}
// 检测name是否以path开头
if (name.startsWith(path)) {
resources.add(name.substring(1));//记录资源名称
}
}
}
return resources;
}
复制代码