I have a JPMS application with layer tree. On Layer C
I need to create Spring context while Spring framework is located on Layer B
:
boot layer
|- Layer B with Spring framework.
|- Layer C where I need to create my context
Component:
@Component
public class ApplicationContextHolder implements ApplicationContextAware {
private static ConfigurableApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextHolder.applicationContext = (ConfigurableApplicationContext) applicationContext;
}
public static ConfigurableApplicationContext getApplicationContext() {
return applicationContext;
}
}
Config:
@Configuration
@ComponentScan(basePackageClasses = {
ApplicationContextHolder.class
})
public class ContextConfig {
}
Creating context:
System.out.println("ThreadContextClassLoadere:" + Thread.currentThread().getContextClassLoader());
System.out.println("This classLoader:" + this.getClass().getClassLoader());
var clazz = this.getClass().getClassLoader().loadClass("org.springframework.context.ApplicationContextAware");
System.out.println("RESULT:" + clazz);
iocContext = new AnnotationConfigApplicationContext();
iocContext.register(ContextConfig.class);
iocContext.setClassLoader(this.getClass().getClassLoader());
iocContext.refresh();
Result:
ThreadContextClassLoadere:jdk.internal.loader.Loader@63fcbf75
This classLoader:jdk.internal.loader.Loader@63fcbf75
RESULT:interface org.springframework.context.ApplicationContextAware
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.foo.ContextConfig]
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:178)
at [email protected]/org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:415)
at [email protected]/org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:287)
at [email protected]/org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:344)
at [email protected]/org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115)
at [email protected]/org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:779)
at [email protected]/org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
....
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/context/ApplicationContextAware.class] cannot be opened because it does not exist
at [email protected]/org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:211)
at [email protected]/org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(SimpleMetadataReader.java:54)
at [email protected]/org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:48)
at [email protected]/org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:103)
at [email protected]/org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:122)
at [email protected]/org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:81)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.asSourceClass(ConfigurationClassParser.java:613)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser$SourceClass.getInterfaces(ConfigurationClassParser.java:939)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.processInterfaces(ConfigurationClassParser.java:379)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:325)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:243)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:188)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:297)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:243)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:196)
at [email protected]/org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:164)
... 16 more
As I understand (I may be wrong) Spring uses some other class loader. Could anyone say how to fix it?
I solved the problem.
Spring loads classes via
getResourceAsStream()
and by default usesClassPathResource
withclassLoader.getResourceAsStream(this.absolutePath)
. However, it wont work for the JPMS application. To make it work it is required to useModuleResource
(added in Spring 6.1) instead ofClassPathResource
.So, it is necessary to create custom
ResourceLoader
that will useModuleResource
add set it to context: