Affects: 6.0.2
Since #22154 , it seems not possible anymore to declare a Controller class via an @Bean
method.
Before we could do that:
@RequestMapping
class MyController {
}
@ConditionalOnSomething
@AutoConfiguration
class MyConfiguration {
@Bean
public MyController myController() {
return new MyController();
}
}
Now, because of https://github.com/spring-projects/spring-framework/blob/4f232a90035de9ddc98a7a3402e1d20997d87600/spring-webmvc/src/main/java/org/springframework/web/servlet/mvc/method/annotation/RequestMappingHandlerMapping.java#L288-L288 , the controller is not considered as a request mapping handler because it is missing @Controller
.
But annotating it make it discoverable by component scan (enabled by the test classes for example), which prevents us from declaring the component conditionally.
Is there something I am missing here?
Comment From: reda-alaoui
I think it relates to #28978
Comment From: bclozel
This change has been introduced in #22154. I'm closing this issue as this is the expected behavior now.
Comment From: reda-alaoui
@bclozel I know it has been introduced in #22154. I read #22154 before creating this issue. But #22154 discussion was about many pre existing use cases that would break but did not cover controller conditional declaration. #22154 forgot this use case.
Can you please re-open this issue? It would at least allow someone to tell me that conditional controller declaration
is supported via another technique or not supported anymore at all.
Comment From: bclozel
Conditional controller declaration is still supported. Adding @Controller
to MyController
should work. Note that component scanning auto-configuration packages should not be done for this very reason: components and configuration classes would be picked up when they're only meant to be discovered through the auto-configuration process.
Comment From: reda-alaoui
To test my boot starter library, I need to declare a class like this in my test directory:
@SpringBootApplication
public class App {
public static void main(String[] args) {
new SpringApplication(App.class).run(args);
}
}
@SpringBootApplication
automatically enables component scanning on the library package. This leads to a duplicate MyController
declaration (when MyController
is annotated with @Controller
) failing the tests bootstrap. How should I test my starter library?
Comment From: reda-alaoui
Moreover, from a usability POV, #22154 creates a difference between @RequestMapping
injectables and the other injectables. I can conditionally declare any injectable without annotating it with @Component
, but now we have this special case where request mappers need to be indirectly annotated with @Component
. Again it looks like #22154 didn't consider this use case.
Comment From: bclozel
@SpringBootApplication automatically enables component scanning on the library package. This leads to a duplicate MyController declaration (when MyController is annotated with @Controller) failing the tests bootstrap. How should I test my starter library?
You need to organize your auto-configuration library and its testing support so that the auto-configuration packages are never scanned. This is called out in the Spring Boot reference documentation:
Auto-configurations must be loaded only by being named in the imports file. Make sure that they are defined in a specific package space and that they are never the target of component scanning. Furthermore, auto-configuration classes should not enable component scanning to find additional components. Specific @Imports should be used instead.
As for
Moreover, from a usability POV, https://github.com/spring-projects/spring-framework/issues/22154 creates a difference between @RequestMapping injectables and the other injectables. I can conditionally declare any injectable without annotating it with @Component, but now we have this special case where request mappers need to be indirectly annotated with @Component. Again it looks like https://github.com/spring-projects/spring-framework/issues/22154 didn't consider this use case.
@RequestMapping
has no specific meaning when it comes to application beans. Interfaces can be annotated with those and they're never considered in the application context. Only @Controller
is meta-annotated to signal that controllers are meant to be components.
This is an important behavior change and I understand that this is quite inconvenient for you, but considering all the related issues and reasons listed in #22154 I don't think we're going to change this behavior back. Especially since the case you're reporting is invalid in the first place, since you were using this behavior without knowing it to work around the fact that the package+scanning arrangement of your library is not ideal. You could very much run into another issue that's related to scanning there without having controllers involved.
Comment From: reda-alaoui
I don't think we're going to change this behavior back.
I am not asking for a revert. I think the notion of @Controller
should be decoupled from @Component
. That way, the behaviour of all injectables would remain the same without defeating the intent of #22154 . Similar to #28978 .
That way controllers would be declared like this by starter libraries:
@RequestMappingController
@RequestMapping
class MyController {
}
And like this for component scanning:
@Controller
@RequestMappingController
@RequestMapping
class MyController {
}
A meta anotation coupling @Controller
with @RequestMappingController
could be imagined to avoid the need of passing 3 annotations for the second example.