一、前言
数据库,表与对象映射关系,可以通过解析XML配置文件或使用注解方法获取。两者相较,注解优势是,使用方便,解析代码简单易实现。但当处理多张表时,就需要手动指定扫描多个类,相当麻烦。为了解决这一缺陷,JAVA包扫描技术,可以一键式扫描指定父包及子包内所有类,获取注解信息。
二、思路简述
1. 普通包扫描
1.获取文件包路径、类加载器。
2.类加载器根据包路路径获取url集合。
3.根据url创建File包文件对象,获取包内Class文件或包文件。
4.针对Class,截取类名,并实例化交付类处理方法。
5.针对包文件,递归处理获取包内Class文件或包文件。
2. jar包扫描
1.获取文件包路径、类加载器。。
2.类加载器根据包路路径获取url集合。
3.根据url建立远程连接JarURLConnection,获取包资源JarFile对象。
4.通过JarFile,获取文件名与文件对应关系Entry
5.根据Entry获取文件名,截取类名,并实例化交付类处理方法。
三、代码实现
在代码实现过程中发现,Jar包资源定位符URL是以“jar”开头,普通包是"file",那么可以根据协议名称分别处理。因此可以统一实现jar包和普通包扫描。
public abstract class PackageScan {
public PackageScan() {
}
public void scanPackage(String packageName) {
//1.获取文件包路径、类加载器。
String path = packageName.replace('.', '/');
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
// 2.类加载器根据包路路径获取url集合。
Enumeration<URL> urls = classLoader.getResources(path);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
//获取协议名称
String protocol = url.getProtocol();
if (protocol.equalsIgnoreCase("file")) {
File file = new File(url.toURI());
dealPackage(file, packageName);
} else if (protocol.equalsIgnoreCase("jar")) {
dealJarFile(path, url);
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
private void dealPackage(File curFile, String packageName) {
//3.获取包内Class文件或包文件。
File[] files = curFile.listFiles();
for (File file : files) {
String fileName = file.getName();
//4.针对Class,截取类名,并实例化交付类处理方法。
if (file.isFile() && fileName.endsWith(".class")) {
String className = packageName + "." + fileName.replace(".class", "");
try {
Class<?> klass = Class.forName(className);
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
} else {
//5.针对包文件,递归获取包内Class文件或包文件。
dealPackage(file, packageName + "." + file.getName());
}
}
}
private void dealJarFile(String jarPackagePath, URL url) {
try {
//3.根据url建立远程连接JarURLConnection,获取包资源JarFile对象。
JarURLConnection jarUrlconnection = (JarURLConnection) url.openConnection();
JarFile jarFileContain = jarUrlconnection.getJarFile();
// 4.通过JarFile,获取文件名与文件对应关系Entry
Enumeration<JarEntry> filesEntries = jarFileContain.entries();
while (filesEntries.hasMoreElements()) {
JarEntry jarEntry = filesEntries.nextElement();
//5.根据Entry获取文件名,截取类名,并实例化交付类处理方法。
String fileName = jarEntry.getName();
if (fileName.endsWith(".class") && fileName.startsWith(jarPackagePath)) {
try {
String classPath = fileName.replace(".class", "");
String className = classPath.replace('/', '.');
Class<?> klass = Class.forName(className);
dealClass(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
} catch (IOException e2) {
e2.printStackTrace();
}
}
protected abstract void dealClass(Class<?> klass);
}
四、后话
包扫描工具解决了批量类扫描。此工具不仅限于注解使用,也可以用于反射机制等方面。笔者首次在扫描Jar包时,有的jar包无任何反应,最后发现是打Jar包时,没有Add directory entries。