文章目录
前言
注解在 Java 是一个非常重要的存在,而且它出现的非常频繁。
在一个工程下可能有许多的包或者Jar包,为了结合注解可以准确的定位到一个需要的类上,并且扫描到一个包下的所有类方便我们使用反射机制,所以产生了包扫描工具实现的想法,它可以帮我们找到带有注解的类,再通过反射执行。
包扫描
我们可以通过用户提供的包名,扫描该包下的所有类。
实现思路(需求分析):
通过博主思考,我们需要实现功能:
-
一个工程下存在普通包或者Jar包,分开处理 Jar 包和普通包;
-
得到该包下我们所要找的类(例如:带有注解的类或者接口或者枚举类型等)这里主要用于扫描带有注解的类。
因为通过包扫描找到该类,我们可以通过注解信息得到该类里面带有注解的成员或方法的信息,然后通过反射机制执行。
具体实现
- 创建一个
IClassDealer
接口:
public interface IClassDealer {
void classDealer(Class<?> klass);
}
-
包扫描类实现:
-
这里根据包名,通过得到 url 协议名称,然后判断是 Jar 包还是普通包,如果是普通包就调用
fileScan(String packageName, File dir)
这个方法,进行普通包扫描,如果是 Jar 包,就调用jarScan(String packageName, URL url)
,进行 Jar 扫描。public void scanPackage(String packageName) throws URISyntaxException { String packagePath = packageName.replace(".", "/"); URL url = Thread.currentThread().getContextClassLoader().getResource(packagePath); if (url.getProtocol().equals("file")) { File root = new File(url.toURI()); fileScan(packageName, root); } else if (url.getProtocol().equals("jar")) { try { jarScan(packageName, url); } catch (IOException e) { e.printStackTrace(); } } }
-
普通包扫描
private void fileScan(String packageName, File dir) { File[] files = dir.listFiles(); for (File file : files) { if (file.isDirectory()) { fileScan(packageName + "." + file.getName(), file); } else { String fileName = file.getName(); if (!fileName.endsWith(".class") || fileName.contains("$")) { continue; } fileName = fileName.replace(".class", ""); String className = packageName + "." + fileName; try { Class<?> klass = Class.forName(className); classDealer.classDealer(klass); } catch (ClassNotFoundException e) { e.printStackTrace(); continue; } } } }
-
Jar 扫描
private void jarScan(String packageName, URL url) throws IOException { JarURLConnection connection = (JarURLConnection) url.openConnection(); JarFile jarFile = connection.getJarFile(); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry entry = entries.nextElement(); String entryName = entry.getName(); String className = entryName.replace("/", "."); if (!className.startsWith(packageName) || className.contains("$") || !entryName.endsWith(".class")) { continue; } className = className.replace(".class", ""); try { Class<?> klass = Class.forName(className); classDealer.classDealer(klass); } catch (ClassNotFoundException e) { e.printStackTrace(); } } }
-
完整代码
- 创建一个
IClassDealer
接口:
public interface IClassDealer {
void classDealer(Class<?> klass);
}
- 包扫描具体代码:
package com.hb.util;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class PackageScanner {
/**
*因为扫描到的类的相关具体操作不是本工具需要完成的,
*因此给一个接口,具体操作由工具使用者完成。
*用这个方法可以去筛选使用者需要扫描的包下的类达到自己的目的。
*/
private IClassDealer classDealer;
public PackageScanner() {
this.classDealer = new IClassDealer() {
@Override
public void classDealer(Class<?> klass) {
}
};
}
public PackageScanner addClassDealer(IClassDealer classDealer) {
this.classDealer = classDealer;
return this;
}
public void scanPackage(String packageName) throws URISyntaxException {
String packagePath = packageName.replace(".", "/");
URL url = Thread.currentThread().getContextClassLoader().getResource(packagePath);
if (url.getProtocol().equals("file")) {
File root = new File(url.toURI());
fileScan(packageName, root);
} else if (url.getProtocol().equals("jar")) {
try {
jarScan(packageName, url);
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void jarScan(String packageName, URL url) throws IOException {
JarURLConnection connection = (JarURLConnection) url.openConnection();
JarFile jarFile = connection.getJarFile();
Enumeration<JarEntry> entries = jarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
String entryName = entry.getName();
String className = entryName.replace("/", ".");
if (!className.startsWith(packageName) || className.contains("$") || !entryName.endsWith(".class")) {
continue;
}
className = className.replace(".class", "");
try {
Class<?> klass = Class.forName(className);
classDealer.classDealer(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
private void fileScan(String packageName, File dir) {
File[] files = dir.listFiles();
for (File file : files) {
if (file.isDirectory()) {
fileScan(packageName + "." + file.getName(), file);
} else {
String fileName = file.getName();
if (!fileName.endsWith(".class") || fileName.contains("$")) {
continue;
}
fileName = fileName.replace(".class", "");
String className = packageName + "." + fileName;
try {
Class<?> klass = Class.forName(className);
classDealer.classDealer(klass);
} catch (ClassNotFoundException e) {
e.printStackTrace();
continue;
}
}
}
}
}
测试运行结果: