路径扫描工具SearchClassUtil
,用于扫描指定包(XXXX)下的所有.class
文件,并将它们的全限定类名(如tomcat.SearchClassUtil
)收集到列表中返回。该工具使用递归文件遍历和反射机制,是实现 Spring 框架组件扫描、Servlet 容器类加载等功能的基础。
完整代码:
package tomcat;import java.io.File;
import java.util.ArrayList;
import java.util.List;/*
* 扫描包下的文件,获取全路径名
* */
public class SearchClassUtil {public static List<String> classPaths = new ArrayList<String>();public static List<String> searchClass(){//需要扫描的包名String basePack = "tomcat";//将获取到的包名转换为路径String classPath = SearchClassUtil.class.getResource("/").getPath();basePack = basePack.replace(".", File.separator);String searchPath = classPath + basePack;doPath(new File(searchPath),classPath);//这个时候我们已经得到了指定包下所有的类的绝对路径了。我们现在利用这些绝对路径和java的反射机制得到他们的类对象return classPaths;}/*** 该方法会得到所有的类,将类的绝对路径写入到classPaths中* @param file*/private static void doPath(File file,String classpath) {if (file.isDirectory()) {//文件夹//文件夹我们就递归File[] files = file.listFiles();for (File f1 : files) {doPath(f1,classpath);}} else {//标准文件//标准文件我们就判断是否是class文件if (file.getName().endsWith(".class")) {String path = file.getPath().replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class","");//如果是class文件我们就放入我们的集合中。classPaths.add(path);}}}public static void main(String[] args) {List<String> classes = SearchClassUtil.searchClass();for (String s: classes) {System.out.println(s);}}
}
代码逐行解释
1. 类定义与成员变量
public class SearchClassUtil {public static List<String> classPaths = new ArrayList<String>();
}
- 功能:定义工具类,使用静态列表存储扫描到的类名。
- 注意点:静态变量会在多次调用时累积结果,需手动清空或改进为非静态设计。
2. searchClass()
方法
public static List<String> searchClass(){String basePack = "tomcat";String classPath = SearchClassUtil.class.getResource("/").getPath();basePack = basePack.replace(".", File.separator);String searchPath = classPath + basePack;doPath(new File(searchPath),classPath);return classPaths;
}
- 功能:入口方法,初始化扫描参数并启动递归扫描。
- 步骤分解:
- 设置扫描包名:
basePack = "tomcat"
硬编码扫描目标包。 - 获取类路径根目录:
SearchClassUtil.class.getResource("/").getPath()
- 返回当前类所在的类路径根(如
/target/classes/
或 JAR 包路径)。 - 示例:若类位于
tomcat/SearchClassUtil.class
,则返回/path/to/classes/
。
- 返回当前类所在的类路径根(如
- 转换包名为路径格式:
basePack.replace(".", File.separator)
- 将包名(如
tomcat.util
)转换为文件路径(如tomcat/util
)。 - 使用
File.separator
确保跨平台兼容性(Windows 为\
,Linux 为/
)。
- 将包名(如
- 拼接完整搜索路径:
String searchPath = classPath + basePack;
- 示例:拼接后为
/path/to/classes/tomcat/
。
- 示例:拼接后为
- 启动递归扫描:
doPath(new File(searchPath), classPath);
- 传递搜索目录和类路径根目录。
- 设置扫描包名:
3. doPath(File file, String classpath)
方法
private static void doPath(File file, String classpath) {if (file.isDirectory()) {File[] files = file.listFiles();for (File f1 : files) {doPath(f1, classpath);}} else {if (file.getName().endsWith(".class")) {String path = file.getPath().replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class","");classPaths.add(path);}}
}
- 功能:递归遍历文件系统,提取类名并存储到列表。
- 逻辑分解:
- 目录处理:
if (file.isDirectory()) { ... }
- 递归调用
doPath
处理子文件 / 目录。
- 递归调用
- 文件处理:
if (file.getName().endsWith(".class")) { ... }
- 筛选
.class
文件。
- 筛选
- 路径处理:
String path = file.getPath().replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class","");
- 复杂替换逻辑详解:
classpath.replace("/","\\").replaceFirst("\\\\","")
:- 将类路径(如
/path/to/classes/
)转换为系统路径格式(如path\to\classes\
)。 - 示例:输入
/C:/path/to/classes/
→ 输出C:\path\to\classes\
。
- 将类路径(如
file.getPath().replace(...)
:- 移除类路径前缀,保留相对路径。
- 示例:原路径
C:\path\to\classes\tomcat\MyClass.class
→tomcat\MyClass.class
。
replace("\\",".")
:- 将路径分隔符转换为
.
,形成类名格式。 - 示例:
tomcat\MyClass.class
→tomcat.MyClass.class
。
- 将路径分隔符转换为
replace(".class","")
:- 移除文件扩展名,得到全限定类名。
- 示例:
tomcat.MyClass.class
→tomcat.MyClass
。
- 复杂替换逻辑详解:
- 目录处理:
4. main(String[] args)
方法
public static void main(String[] args) {List<String> classes = SearchClassUtil.searchClass();for (String s: classes) {System.out.println(s);}
}
- 功能:测试工具类,打印扫描到的类名。
- 执行流程:
- 调用
searchClass()
方法获取类名列表。 - 遍历列表并打印每个类名。
- 调用
关键技术细节
1. 类路径资源获取
SearchClassUtil.class.getResource("/").getPath()
- 工作原理:
getResource("/")
返回类路径根目录的URL
对象。.getPath()
将URL
转换为文件系统路径。
- 注意点:
- 路径格式可能包含协议前缀(如
file:/
),但后续替换逻辑会处理此问题。 - 在 JAR 包环境中,可能返回
jar:file:/path/to/app.jar!/
格式,此时该代码会失效。
- 路径格式可能包含协议前缀(如
2. 路径处理逻辑
file.getPath().replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"")
- 设计意图:
- 移除类路径前缀,保留相对路径。
- 存在问题:
- 跨平台兼容性:混用
/
和\\
作为路径分隔符,可能在非 Windows 系统上出错。 - 特殊路径处理:若类路径包含特殊字符(如空格),替换逻辑可能失效。
- 跨平台兼容性:混用
3. 静态列表的线程安全
public static List<String> classPaths = new ArrayList<String>();
4.classpath.replace("/","\\").replaceFirst("\\\\","")
详解
这段代码的核心目标是将类路径字符串转换为系统路径格式,并移除可能存在的前导路径分隔符,以便后续正确拼接和替换路径。
假设初始 classpath
值为:
String classpath = "/C:/workspace/project/target/classes/";
-
第一步:
classpath.replace("/", "\\")
- 将所有斜杠
/
替换为反斜杠\
- 结果:
"\C:\workspace\project\target\classes\"
- 将所有斜杠
-
第二步:
.replaceFirst("\\\\", "")
- 使用正则表达式
\\\\
(对应 Java 字符串中的"\\"
,即正则中的\
)匹配第一个反斜杠 - 将其替换为空字符串
- 结果:
"C:\workspace\project\target\classes\"
- 使用正则表达式
5. dopath递归
假设目录结构如下:
classes/
└── tomcat/├── util/│ ├── StringUtil.class│ └── FileUtil.class└── SearchClassUtil.class
递归执行步骤:
-
初始调用:
doPath("classes/tomcat/", "classes/")
- 处理目录
tomcat/
- 递归调用子项:
util/
和SearchClassUtil.class
- 处理目录
-
处理
util/
目录:- 递归调用子项:
StringUtil.class
和FileUtil.class
- 递归调用子项:
-
处理
StringUtil.class
:- 路径转换:
classes/tomcat/util/StringUtil.class
→tomcat.util.StringUtil
- 加入结果列表
- 路径转换:
-
处理
FileUtil.class
:- 路径转换:
classes/tomcat/util/FileUtil.class
→tomcat.util.FileUtil
- 加入结果列表
- 路径转换:
-
返回处理
SearchClassUtil.class
:- 路径转换:
classes/tomcat/SearchClassUtil.class
→tomcat.SearchClassUtil
- 加入结果列表
- 路径转换: