For our applications we use a self written auto configuration to create a /version endpoint for example. When migrating from Spring-Boot 2.7.6 to Spring-Boot 3.0.3 i followed the instructions provided by you
In the paragraph "Auto-configuration Files" you link to the newly introduced Auto-configuration Mechanism. I clicked on the link to get to the according Section in the 2.7-Release-Notes
There is a paragraph called New @AutoConfiguration Annotation that reccomends to use @AutoConfiguration at the top-level auto-configuration instead of @Configuration.
By doing so my /version endpoint no longer works as expected.
I want to provide some code for context:
Auto-Configuration-File
@Bean
@ConditionalOnProperty("teamstarter.add.version-endpoint")
public VersionController versionController() {
return new VersionController(versionService());
}
@Bean
@ConditionalOnProperty("teamstarter.add.version-endpoint")
public VersionService versionService() {
return new VersionService();
}
Version Service
```java /* * The version field contains the current version of the pom.xml / @Value("${teamstarter.version:unknown}") private String version;
/* * Obtains the value from the {@link #version version field} and returns it to the caller. * * @return Value of the {@link #version version field}. / public String getVersion() { return this.version; }
When using the old `@Configuration`-Annotation this works fine and the version from the application.yml is returned.
But when i use the new `@AutoConfiguration`-Annotation, it always returns null.
While Debugging i added
```java
@PostConstruct
public void init(){
System.out.println("Version in PostConstruct" + this.version);
}
and found out that the Bean with the Version set is created, but an other instance is used later in the Controller.
To me, this seems like a bug, or is this intentional behavior? If this behavior is intentional, should I continue to use @Configuration instead and what are the reasons for this behaviour?
Comment From: philwebb
The @AutoConfiguration annotation is meta-annotated with @Configuration so they should work in the same way. Could you please provide a sample application that replications the problem so we can try to get to the bottom of it.
Comment From: NicoStrecker
Where could i provide that ? @philwebb
Comment From: NicoStrecker
Spring-AutoConfiguration-Demo.zip Here is my Demo @philwebb
How-To:
Unpack zip mvn clean install auto-config-project mvn clean package demo project
Run demo Project and call localhost:8080/v1/version/ endpoint Should work
Change @Configuration to @AutoConfiguration in ProblematicAutoCofing class
mvn clean install auto-config-project
mvn clean package demo project
Run demo Project and call localhost:8080/v1/version/ endpoint Should not work now
Comment From: mhalbritter
The @AutoConfiguration is a @Configuration(proxyBeanMethods = false). In your autoconfiguration:
@Bean
@ConditionalOnProperty("app.add.version-endpoint")
public VersionController versionController() {
return new VersionController(versionService());
}
this call to versionService() doesn't work with proxyBeanMethods = false. Rewriting it this way (which I recommend to always do):
@Bean
@ConditionalOnProperty("app.add.version-endpoint")
public VersionController versionController(VersionService versionService) {
return new VersionController(versionService);
}
and it works.
Comment From: NicoStrecker
@mhalbritter Could you explain what the difference is between this two approaches?
Comment From: mhalbritter
When calling versionService() inside a @Bean method on a @Configuration(proxyBeanMethods = false), the instance returned by versionService() is not a Spring managed bean. That's why @Value isn't injected and is null. By using method parameter injection via versionController(VersionService versionService) the argument in VersionService versionService is managed by spring and the @Value has been injected.
It behaves the same when using @Configuration(proxyBeanMethods = true) (or just @Configuration, which defaults to true for proxyBeanMethods), because then Spring creates a CGlib-proxy for your configuration class and intercepts calls to versionService() and replaces the return value with a managed Spring bean. But this ONLY works when a proxy is created.
The method parameter injection (versionController(VersionService versionService)) works regardless of a proxy or not, and is much less "magic", therefore I favor this approach.
Comment From: NicoStrecker
Thank you a lot now i got it.