Consider the following situation. JPMS application with layer tree. In Module X on Layer C it is necessary to create Spring context while Spring framework is located on Layer B :
boot layer
|- Layer B with Spring framework.
|- Layer C with Module X and Module Y
In Module X we have the following code
System.out.println("ThreadContextClassLoader:" + Thread.currentThread().getContextClassLoader());
System.out.println("This classLoader:" + this.getClass().getClassLoader());
//now resource as stream for class from Module Y:
var stream1 = this.getClass().getClassLoader().getResourceAsStream("com/foo/moduley/ContextConfig.class");
System.out.println("Stream 1 = " + stream1);
var stream2 = this.getClass().getClassLoader().getResourceAsStream("org/springframework/context/ApplicationContextAware.class");
System.out.println("Stream 2 = " + stream2);
var clazz = this.getClass().getClassLoader().loadClass("org.springframework.context.ApplicationContextAware");
System.out.println("Class:" + clazz);
var stream3 = clazz.getModule().getResourceAsStream("org/springframework/context/ApplicationContextAware.class");
System.out.println("Stream 3 = " + stream3);
And this is output:
ThreadContextClassLoader:jdk.internal.loader.Loader@426bf2f2
This classLoader:jdk.internal.loader.Loader@426bf2f2
Stream 1 = sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@68dc2f53
Stream 2 = null
Class:interface org.springframework.context.ApplicationContextAware
Stream 3 = sun.net.www.protocol.jar.JarURLConnection$JarURLInputStream@1d51b6a8
As we see getResourceAsStream
works differently in JPMS. Now cosider ClassPathResource.getInputStream() and this line
https://github.com/spring-projects/spring-framework/blob/4e2d3573189b7c0afce62bce29cd915de4077f56/spring-core/src/main/java/org/springframework/core/io/ClassPathResource.java#L209
Because if this the given JPMS application won't work.
I suggest either add ModulePathResource
class (I looked for it but didn't find) or modify ClassPathResource.getInputStream().
Comment From: snicoll
Please share a sample that we can run ourselves. Code and log output like that is not complete enough, I am afraid.
Comment From: PavelTurk
@snicoll Thank you for a quick reply. Full test that creates layers is here with readme
Comment From: sbrannen
Have you tried using ModuleResource
?
Comment From: PavelTurk
@sbrannen No, I haven't. Could you say where I can find information how to use it? As I understand Spring must use it instead of ClassPathResource. But how to make Spring do it?
Comment From: PavelTurk
@sbrannen I've just tried Spring 6.1.1 result is the same:
Exception in thread "main" org.springframework.beans.factory.BeanDefinitionStoreException: I/O failure while processing configuration class [com.foo.moduley.ApplicationContextHolder]
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:250)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:188)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:304)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:245)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:196)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:164)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:416)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:289)
at spring.context@6.1.1/org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:349)
at spring.context@6.1.1/org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:118)
at spring.context@6.1.1/org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:775)
at spring.context@6.1.1/org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:597)
at com.foo.module.x@1.0.0/com.foo.modulex.ActivatorProvider.start(ActivatorProvider.java:16)
at com.foo.main@1.0.0/com.foo.main.Project.main(Project.java:42)
Caused by: java.io.FileNotFoundException: class path resource [org/springframework/context/ApplicationContextAware.class] cannot be opened because it does not exist
at spring.core@6.1.1/org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:215)
at spring.core@6.1.1/org.springframework.core.type.classreading.SimpleMetadataReader.getClassReader(SimpleMetadataReader.java:54)
at spring.core@6.1.1/org.springframework.core.type.classreading.SimpleMetadataReader.<init>(SimpleMetadataReader.java:48)
at spring.core@6.1.1/org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:103)
at spring.core@6.1.1/org.springframework.core.type.classreading.CachingMetadataReaderFactory.getMetadataReader(CachingMetadataReaderFactory.java:122)
at spring.core@6.1.1/org.springframework.core.type.classreading.SimpleMetadataReaderFactory.getMetadataReader(SimpleMetadataReaderFactory.java:81)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.asSourceClass(ConfigurationClassParser.java:620)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser$SourceClass.getInterfaces(ConfigurationClassParser.java:946)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.processInterfaces(ConfigurationClassParser.java:386)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:332)
at spring.context@6.1.1/org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:245)
... 13 more