ResolverUtil
ResolverUtil用于查找在类路径可用并满足条件的类。最常见的两种条件是判断一个类是否继承或实现了另一个类 ,比如:IsA 类的matches方法。或者判断此类是否被指定的注解标记了比如:AnnotatedWith 类的matches方法。然而,通过实现Test类,自己编写matches的逻辑,可以满足相对应的条件搜索。
其中有一个接口、两个内部类。用到的设计模式原则之开放-封闭原则,对于扩展是开放的(Open for extension),对于更改是封闭的(Closed for modification)。
如此设计:
我们可以自己实现Test接口,调用find方法时以参数的形式传入,而不用修改ResolverUtil的内部方法。这是我自定义的类并实现Test 接口,这里可以写自己想定义的逻辑。
public class Testtest implements Test {
@Override
public boolean matches(Class<?> type) {
if (type == Testtest.class) {
return true;
}
return false;
}
}
看看Test接口和两个内部类是怎样的:
public class ResolverUtil<T> {
private static final Log log = LogFactory.getLog(ResolverUtil.class);
//定义Test接口
public interface Test {
boolean matches(Class<?> type);
}
//IsA方法实现Test接口
public static class IsA implements Test {
private Class<?> parent;
public IsA(Class<?> parentType) {
this.parent = parentType;
}
/*
parent类是否是type类的父类
isAssignableFrom方法解释可以看这篇文章
https://blog.csdn.net/qq_36666651/article/details/81215221
*/
@Override
public boolean matches(Class<?> type) {
return type != null && parent.isAssignableFrom(type);
}
@Override
public String toString() {
return "is assignable to " + parent.getSimpleName();
}
}
//AnnotatedWith方法实现Test接口
public static class AnnotatedWith implements Test {
private Class<? extends Annotation> annotation;
public AnnotatedWith(Class<? extends Annotation> annotation) {
this.annotation = annotation;
}
/*
注解annotation否是在type类上
isAnnotationPresent方法解释可以看这篇文章
https://blog.csdn.net/qq_41084324/article/details/83787052
*/
@Override
public boolean matches(Class<?> type) {
return type != null && type.isAnnotationPresent(annotation);
}
@Override
public String toString() {
return "annotated with @" + annotation.getSimpleName();
}
}
//省略其他代码
}
接着看看ResolverUtil类的其他的方法
find方法:
public class ResolverUtil<T> {
/** The set of matches being accumulated. */
private Set<Class<? extends T>> matches = new HashSet<>();
//省略其他代码
public ResolverUtil<T> find(Test test, String packageName) {
String path = getPackagePath(packageName);
try {
/*
找到path包下所有资源,
VFS含义是虚拟文件系统;主要是通过程序能够方便读取本地文件系统、FTP文件系统等系统中的文件资源。
*/
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;
}
/**
* 把Java类的包名转换成路径名
* @param packageName 要转换成路径的Java包名
*/
protected String getPackagePath(String packageName) {
return packageName == null ? null : packageName.replace('.', '/');
}
@SuppressWarnings("unchecked")
protected void addIfMatching(Test test, String fqn) {
try {
//fqn是类的路径名,将路径名带有/替换为.
//例如:com/example/maybatissource/dao/main.class 将/替换为.
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);
/*检测是否满足,IsA类的matches方法或者AnnotatedWith类的matches方法,
或者自定义的类实现Test接口的matches方法
*/
if (test.matches(type)) {
//添加到set集合中
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());
}
}
/**
* 用于查找类的类加载器。
*/
private ClassLoader classLoader;
public Set<Class<? extends T>> getClasses(){
return matches;
}
/*
如果classLoader 为空就得到当前线程的类加载器,否者就用classLoader
*/
public ClassLoader getClassLoaer(){
return classLoader == null ? Thread.currentThread().getContextClassLoader() : classLoader;
}
public void setClassLoader(ClassLoader classloader){
this.classloader = classloader;
}
}