需求
需要找出所有实现了某个接口或继承了某个父类的 Java 类。
使用步骤
步骤 1:添加依赖(用于类路径扫描)
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>0.10.2</version>
</dependency>
步骤 2:定义接口和实现类
// common/Processor.java
public interface Processor {
String getType();
void process(String data);
}
// impl/EmailProcessor.java
public class EmailProcessor implements Processor {
@Override
public String getType() { return "email"; }
@Override
public void process(String data) {
System.out.println("处理邮件: " + data);
}
}
// impl/SmsProcessor.java
public class SmsProcessor implements Processor {
@Override
public String getType() { return "sms"; }
@Override
public void process(String data) {
System.out.println("处理短信: " + data);
}
}
步骤 3:扫描并使用
@Service
public class ProcessorManager {
private final Map<String, Processor> processorMap = new HashMap<>();
public ProcessorManager() {
// 1. 扫描所有 Processor 的实现类
Class<Processor> clazz = Processor.class;
Reflections reflections = new Reflections(clazz.getPackage().getName()); // 指定包路径
Set<Class<? extends Processor>> classes = reflections.getSubTypesOf(Processor.class);
// 2. 手动实例化(要求有无参构造函数)
for (Class<? extends Processor> clazz : classes) {
try {
Processor instance = clazz.getDeclaredConstructor().newInstance();
processorMap.put(instance.getType(), instance);
} catch (Exception e) {
throw new RuntimeException("无法实例化处理器: " + clazz.getName(), e);
}
}
System.out.println("加载了 " + processorMap.size() + " 个非 Bean 处理器");
}
public void execute(String type, String data) {
Processor processor = processorMap.get(type);
if (processor != null) {
processor.process(data);
} else {
throw new IllegalArgumentException("不支持的类型: " + type);
}
}
}
步骤 4:测试
@RestController
public class TestController {
private final ProcessorManager processorManager;
public TestController(ProcessorManager processorManager) {
this.processorManager = processorManager;
}
@GetMapping("/test")
public String test() {
processorManager.execute("email", "Hello");
processorManager.execute("sms", "World");
return "OK";
}
}
问题
在本地使用idea启动服务,是可以正确扫描到所有目标类。
Reflections took 165 ms to scan 1 urls, producing 1 keys and 4 values
但是在服务器上启动服务时,却没有检测到目标类。
Reflections took 249 ms to scan 1 urls, producing 0 keys and 0 values
github上也有反馈这个问题:
因为在服务器上,应用是以jar包方式启动的,java -jar app.jar。
类路径结构:jar:file:/app.jar!/BOOT-INF/classes!(嵌套 JAR)
Spring Boot 的可执行 JAR 使用了自定义的 JarURLConnection,将应用类打包在 BOOT-INF/classes/ 下。而 org.reflections 库默认不支持这种嵌套结构。
高版本的Reflections (0.10.2版本)在源代码中也确实有较大改动:

解决办法
降低版本号。
将版本从 0.10.2 降级到 0.9.11。
使用 ConfigurationBuilder
修改指定扫描包路径的代码:
Reflections reflections = new Reflections(new ConfigurationBuilder().forPackages(clazz.getPackage().getName())); // 指定包路径
另一种扫描方式
使用 Spring Boot 推荐的组件扫描
public Set<Class<?>> doScan(String scanPath, Class<?> targetType) throws Exception {
Set<Class<?>> classes = new HashSet<>();
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AssignableTypeFilter(targetType));
for (BeanDefinition bd : scanner.findCandidateComponents(scanPath)) {
String className = bd.getBeanClassName();
Class<?> clazz = ClassUtils.forName(className,
Thread.currentThread().getContextClassLoader());
classes.add(clazz);
}
return classes;
}

341

被折叠的 条评论
为什么被折叠?



