gpt4 book ai didi

java - @ComponentScan 未检测到模块路径中的类

转载 作者:行者123 更新时间:2023-12-02 02:35:36 29 4
gpt4 key购买 nike

我正在使用 Java 11 和 Spring 开发 JavaFX 应用程序。应用程序模块使用 jlink 与自定义 JRE 捆绑在一起,它只允许命名模块包含在 bundle 中。由于 Spring 不提供命名模块,而是依赖自动模块来实现 Java 9 模块系统支持,因此我使用 moditect将模块描述符 (module-info.java) 添加到 Spring JAR。

编译、jlink和执行应用程序没有任何问题。但是,尽管我的 AppConfig 类使用 @ComponentScan 进行注释,Spring 并未检测到我的应用程序的任何使用 @Component 进行注释的类:

@Configuration
@ComponentScan
public class AppConfig {
}

Main中,我基于AppConfig创建一个AnnotationConfigApplicationContext,并打印所有注册的bean以及类路径上可用的资源:

public class Main extends Application {

private ConfigurableApplicationContext applicationContext;

public static void main(String[] args) {
launch(args);
}

@Override
public void init() {
applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
}

@Override
public void start(Stage mainWindow) throws IOException {
System.out.println("Printing beans: " + applicationContext.getBeanDefinitionNames().length);
for (String beanDefinitionName : applicationContext.getBeanDefinitionNames()) {
System.out.println(beanDefinitionName);
}

PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
System.out.println(Arrays.toString(resolver.getResources("classpath*:com/myapp/**/*.class")));
}

@Override
public void stop() {
applicationContext.stop();
}
}

如果我使用 IntelliJ 运行应用程序,PathMatchingResourcePatternResolver 会在类路径上找到我的所有类(我猜是因为 IntelliJ 使用类路径而不是模块路径运行应用程序)。因此,通过组件扫描检测所有组件并创建相应的 bean:

Printing beans: 8
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalPersistenceAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
appConfig
com.myapp.services.UserServiceImpl
com.myapp.services.BookingServiceImpl
[file [/Users/user/myapp/target/classes/com/myapp/AppConfig.class], file [/Users/user/myapp/target/classes/com/myapp/Main.class], file [/Users/user/myapp/target/classes/com/myapp/services/UserService.class], file [/Users/user/myapp/target/classes/com/myapp/services/UserServiceImpl.class], file [/Users/user/myapp/target/classes/com/myapp/services/BookingService.class], file [/Users/user/myapp/target/classes/com/myapp/services/BookingServiceImpl.class]]

但是,如果我通过 jlink'ed bundle 运行应用程序,即在使用模块路径的自定义 JRE 上,Spring 无法检测到我的任何类:

Printing beans: 5
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
appConfig
[]

PathMatchingResourcePatternResolver 找不到任何类(因为所有内容现在都位于模块路径上),并且组件扫描没有实例化任何一个 bean。

如果我将组件类手动导入到 AppConfig 中,则 Bean 会正确创建并通过 @Autowired 注入(inject):

@Configuration
@Import({
com.myapp.service.UserServiceImpl.class,
com.myapp.service.BookingServiceImpl.class
})
public class AppConfig {
}

为什么 Spring 在使用 @Import 时能够创建 bean,但无法通过 @ComponentScan 检测到它们?如何通过 @ComponentScan 解析我的组件?

最佳答案

除了@IggyBlob在问题评论中提供的解决方案之外,另一种可能的解决方案是修补PathMatchingResourcePatternResolver以将资源搜索到模块路径中,至少直到发布完全模块兼容的Spring版本为止。

一种可能的实现:

public class PathMatchingResourcePatternResolverJigsaw extends PathMatchingResourcePatternResolver {

public PathMatchingResourcePatternResolverJigsaw() {
}

public PathMatchingResourcePatternResolverJigsaw(ResourceLoader resourceLoader) {
super(resourceLoader);
}

public PathMatchingResourcePatternResolverJigsaw(ClassLoader classLoader) {
super(classLoader);
}

public List<Resource> getResourcesFromModules(String locationPattern) throws IOException {
String pattern = locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length());
List<Resource> list = new ArrayList<>();
ModuleLayer.boot().configuration().modules().stream()
.map(ResolvedModule::reference)
.forEach(mref -> {
try (ModuleReader reader = mref.open()) {
list.addAll(reader.list()
.filter(p -> getPathMatcher().match(pattern, p))
.map(p -> {
try {
return convertClassLoaderURL(reader.find(p).get().toURL());
} catch (Exception e) {
return null;
}
})
.collect(Collectors.toList()));
} catch (IOException ioe) {
throw new UncheckedIOException(ioe);
}
});
return list;
}
@Override
public Resource[] getResources(String locationPattern) throws IOException {
boolean addModSearch = true;

Resource[] result = super.getResources(locationPattern);
if (addModSearch && locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
List<Resource> list = getResourcesFromModules(locationPattern);
list.addAll(Arrays.asList(result));
result = list.toArray(new Resource[0]);
}

return result;
}
}

并使用以下方法初始化您的 Spring 应用程序上下文:

private ConfigurableApplicationContext context;

@Override
public void init() throws Exception {
ApplicationContextInitializer<GenericApplicationContext> initializer = new ApplicationContextInitializer<GenericApplicationContext>() {
@Override
public void initialize(GenericApplicationContext genericApplicationContext) {
genericApplicationContext.setResourceLoader(new PathMatchingResourcePatternResolverJigsaw());
}
};

this.context = new SpringApplicationBuilder().sources(MyApplication.class)
.initializers(initializer)
.build().run(new String[0]);
}

这只是一个丑陋的解决方法,因此请谨慎使用

关于java - @ComponentScan 未检测到模块路径中的类,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57204211/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com