Hello.
I've a Spring Boot 3.4.5
project that uses the old XML style to configure the application context.
While in Spring Boot 2.1.16
, the below was working fine:
appContext.xml
<context:property-placeholder ignore-resource-not-found="true" location="classpath:/testConfig.properties,/config.properties" />
<import resource="beanContext.xml" />
beanContext.xml
<import resource="infrastructureContext.xml" />
<import resource="functionContext.xml" />
<!-- Other beans -->
infrastructureContext.xml
<context:annotation-config />
<context:component-scan base-package="com.foo.dfi.hiku.commons.util.model.mdf" />
<bean id="bplCall" class="${bpl.class}">
<constructor-arg value="${bpl.server}" />
<constructor-arg value="${bpl.port}" />
<property name="Application" value="${bpl.name}" />
</bean>
<!-- Other beans -->
in Spring Boot 3.4.5
fails with:
2025-05-16 10:38:32,510 [main] ERROR KFFCalculatorApplication:103 - org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [${bpl.class}] for bean with name 'bplCall' defined in file [C:\Daten\gitlab\hiku-kff-calculator\target\classes\infrastructureContext.xml]
org.springframework.beans.factory.CannotLoadBeanClassException: Cannot find class [${bpl.class}] for bean with name 'bplCall' defined in file [C:\Daten\gitlab\hiku-kff-calculator\target\classes\infrastructureContext.xml]
at org.springframework.beans.factory.support.AbstractBeanFactory.resolveBeanClass(AbstractBeanFactory.java:1585) ~[spring-beans-6.2.6.jar:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineTargetType(AbstractAutowireCapableBeanFactory.java:692) ~[spring-beans-6.2.6.jar:6.2.6]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.predictBeanType(AbstractAutowireCapableBeanFactory.java:660) ~[spring-beans-6.2.6.jar:6.2.6]
at org.springframework.beans.factory.support.AbstractBeanFactory.getType(AbstractBeanFactory.java:738) ~[spring-beans-6.2.6.jar:6.2.6]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAnnotationOnBean(DefaultListableBeanFactory.java:817) ~[spring-beans-6.2.6.jar:6.2.6]
at org.springframework.boot.sql.init.dependency.AnnotationDependsOnDatabaseInitializationDetector.detect(AnnotationDependsOnDatabaseInitializationDetector.java:36) ~[spring-boot-3.4.5.jar:3.4.5]
at org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor.detectDependsOnInitializationBeanNames(DatabaseInitializationDependencyConfigurer.java:152) ~[spring-boot-3.4.5.jar:3.4.5]
at org.springframework.boot.sql.init.dependency.DatabaseInitializationDependencyConfigurer$DependsOnDatabaseInitializationPostProcessor.postProcessBeanFactory(DatabaseInitializationDependencyConfigurer.java:115) ~[spring-boot-3.4.5.jar:3.4.5]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:363) ~[spring-context-6.2.6.jar:6.2.6]
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:197) ~[spring-context-6.2.6.jar:6.2.6]
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:791) ~[spring-context-6.2.6.jar:6.2.6]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:609) ~[spring-context-6.2.6.jar:6.2.6]
at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:146) ~[spring-boot-3.4.5.jar:3.4.5]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:753) ~[spring-boot-3.4.5.jar:3.4.5]
at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:439) ~[spring-boot-3.4.5.jar:3.4.5]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:318) ~[spring-boot-3.4.5.jar:3.4.5]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1362) ~[spring-boot-3.4.5.jar:3.4.5]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1351) ~[spring-boot-3.4.5.jar:3.4.5]
at com.sixgroup.dfi.hiku.kff.calculator.KFFCalculatorApplication.execute(KFFCalculatorApplication.java:65) [classes/:?]
at com.sixgroup.dfi.hiku.kff.calculator.KFFCalculatorApplication.main(KFFCalculatorApplication.java:59) [classes/:?]
Caused by: java.lang.ClassNotFoundException: ${bpl.class}
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[?:?]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[?:?]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[?:?]
at java.base/java.lang.Class.forName0(Native Method) ~[?:?]
at java.base/java.lang.Class.forName(Class.java:534) ~[?:?]
at java.base/java.lang.Class.forName(Class.java:513) ~[?:?]
at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) ~[spring-core-6.2.6.jar:6.2.6]
at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:503) ~[spring-beans-6.2.6.jar:6.2.6]
What I observed is that this is happening only for beans which have dynamic/property-based class names. In the Spring migration documentation I cannot really spot anything useful, so your help would be highly appreciated here.
Kind regards and thanks.
Comment From: sbrannen
@cdprete, what happens if you upgrade the Spring Framework dependency to 6.2.7?
Comment From: cdprete
@cdprete, what happens if you upgrade the Spring Framework dependency to 6.2.7?
Hello @sbrannen.
How could I do it, given that I'm not using the spring-boot-parent
but just the spring-boot-dependencies
?
Is there a BoM for Spring itself?
Comment From: bclozel
@cdprete https://repo1.maven.org/maven2/org/springframework/spring-framework-bom/6.2.7/
Comment From: cdprete
@cdprete https://repo1.maven.org/maven2/org/springframework/spring-framework-bom/6.2.7/
Thanks @bclozel. Here was not visible that version.
I'll give it a try.
Comment From: bclozel
That's because mvnrepository is a third party index that often lags behind. If you want consistent results the official central search is usually better: https://central.sonatype.com/artifact/org.springframework/spring-framework-bom/versions
Comment From: cdprete
@cdprete, what happens if you upgrade the Spring Framework dependency to 6.2.7?
It doesn't help, unfortunately.
Caused by: java.lang.ClassNotFoundException: ${bpl.class} at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:641) ~[?:?] at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:188) ~[?:?] at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:526) ~[?:?] at java.base/java.lang.Class.forName0(Native Method) ~[?:?] at java.base/java.lang.Class.forName(Class.java:534) ~[?:?] at java.base/java.lang.Class.forName(Class.java:513) ~[?:?] at org.springframework.util.ClassUtils.forName(ClassUtils.java:321) ~[spring-core-6.2.7.jar:6.2.7] at org.springframework.beans.factory.support.AbstractBeanDefinition.resolveBeanClass(AbstractBeanDefinition.java:503) ~[spring-beans-6.2.7.jar:6.2.7]
Comment From: cdprete
My workaround so far has been to mimic what Spring should be doing and that is
public class BplCallFactoryBean implements FactoryBean<BPLCall> {
private final String server;
private final String application;
private final String port;
private final Class<? extends BPLCall> clazz;
private BPLCall instance;
public BplCallFactoryBean(String server, String application, String clazz, String port) throws Exception {
this.server = server;
this.application = application;
this.port = port;
this.clazz = (Class<? extends BPLCall>) Class.forName(clazz);
}
@Override
public BPLCall getObject() throws Exception {
if(instance == null) {
Constructor<? extends BPLCall> constructor = clazz.getDeclaredConstructor(String.class, String.class);
instance = constructor.newInstance(server, port);
instance.setApplication(application);
}
return instance;
}
@Override
public Class<?> getObjectType() {
return BPLCall.class;
}
}
and then in the XML context definition
<bean id="bplCall" class="com.foo.dfi.hiku.kff.calculator.factories.BplCallFactoryBean">
<constructor-arg value="${bpl.server}" />
<constructor-arg value="${bpl.name}" />
<constructor-arg value="${bpl.class}" />
<constructor-arg value="${bpl.port}" />
</bean>
It's not ideal and I hope it gets fixed soon, so that I can drop my handcrafted factories. ;)
Comment From: sbrannen
@cdprete, what happens if you upgrade the Spring Framework dependency to 6.2.7?
It doesn't help, unfortunately.
Thanks for the feedback, @cdprete.
I attempted to reproduce the behavior you have described (albeit in a simplified version) here: https://github.com/spring-projects/spring-framework/compare/main...sbrannen:spring-framework:issues/gh-34909-xml-placeholder-resolution
That test passes against 6.1.x
, 6.2.x
, and main
(7.0).
If you would like us to investigate this further, please provide a minimal sample application that reproduces the problem (preferably something that we can download and run such as a ZIP file attached to this issue or a link to a public Git repository).
Comment From: cdprete
@cdprete, what happens if you upgrade the Spring Framework dependency to 6.2.7?
It doesn't help, unfortunately.
Thanks for the feedback, @cdprete.
I attempted to reproduce the behavior you have described (albeit in a simplified version) here: main...sbrannen:spring-framework:issues/gh-34909-xml-placeholder-resolution
That test passes against
6.1.x
,6.2.x
, andmain
(7.0).If you would like us to investigate this further, please provide a minimal sample application that reproduces the problem (preferably something that we can download and run such as a ZIP file attached to this issue or a link to a public Git repository).
Hi @sbrannen.
Tests like this are working fine also on our side, but that's when we start the application that it fails. I wonder if it's caused on how our application is started/initialised:
@SpringBootApplication
@ImportResource({"classpath*:appContext.xml" })
public class KFFCalculatorApplication implements Observer {
private ConfigurableApplicationContext ctx;
public static void main(String[] args) {
KFFCalculatorApplication m = new KFFCalculatorApplication();
m.execute(args);
}
public void execute(String[] args) {
try {
// mit Java 1.8, wenn i.e. direkt ein tomcat gestartet werden soll, für HTTP-Services
ctx = SpringApplication.run(KFFCalculatorApplication.class, args);
// Bunch of very unappealing stuff are happening in here
} catch (Exception e) {
LOG.error(e.toString(), e);
shutdown();
}
}
}
Moreover, I wonder about that @ImportResource
.
Maybe we should load the XML differently.
Comment From: sbrannen
Moreover, I wonder about that
@ImportResource
. Maybe we should load the XML differently.
I modified my example to use @Configuration
and @ImportResource
, and it still works against 6.1.x/6.2.x/main. See commit https://github.com/spring-projects/spring-framework/commit/275e17aecbf59d92239b13170ff592c65cb7b4a0.
Tests like this are working fine also on our side, but that's when we start the application that it fails. I wonder if it's caused on how our application is started/initialised:
Feel free to experiment with that on your own and report back here.
Otherwise, as mentioned above, we will need a minimal sample application that reproduces it.
Comment From: cdprete
I'll try to build a sample app that reproduces the issue.
Also because, for some reason unknown to me, all the beans get registered twice and without the bean override I'm not able to start it either.
I suspect something really bad is done in there with the context, but it's difficult to me to provide a real pin-pointed feedback since I'm not the one who implemented the application. As such, I don't know what was passing by in the mind of the implementor to have the things which are in there. :-/