I have a simple FooBarRepository
interface. I have a single implementation called DefaultFooBarRespository
, which looks like this:
@Repository("fooBarRepository")
public class DefaultFooBarRepository implements FooBarRepository {
…
}
I know that Spring is instantiating DefaultFooBarRepository
and giving it a bean name of fooBarRepository
, because in my Thymeleaf view I can do something like:
<li th:each="fooBar: ${@fooBarRepository.getFooBars()}" …
As we all know, it's best to program to interfaces. After all I might want to wire up a different implementation of FooBarRepository
at some point. So in my MVC controller FooBarController
I try to inject the interface:
@GetMapping
public String getFooBars(FooBarRepository repository) {
…
}
That results in an error:
java.lang.IllegalStateException: No primary or single unique constructor found for interface ….FooBarRepository
Spring I suppose is trying to create an instance of FooBarRepository
, not realizing that the DefaultFooBarRepository
it already created is an instance of FooBarRepository
.
I imagine I could create a separate class annotated with @Configuration
and have a method that returns an FooBarRepository
. I could even probably inject the DefaultFooBarRepository
instance into this method and return it as the FooBarRepository
.
But why should I have to do all that? Spring is already creating a DefaultFooBarRepository
, adding it to the context, giving it a bean name, etc. How can I tell Spring simply to register the DefaultFooBarRepository
with the FooBarRepository
interface rather than the DefaultFooBarRepository
implementation? Is there another annotation or an annotation parameter I can add to DefaultFooBarRepository
or something?
Update: And this is even stranger: in FooBarController
I tried to inject DefaultFooBarRepository
:
@GetMapping
public String getFooBars(DefaultFooBarRepository repository) {
…
}
Then I get another error saying that one of the parameters of the DefaultFooBarRepository
constructor is null
—specifically a @ConfigurationProperties
annotated class AppProperties
. It appears that in the controller GET
handler it's trying to create an instance of DefaultFooBarRepository
. But Spring should have already scanned the classes and instantiated both AppProperties
and DefaultFooBarRepository
. And we know it did, because as I mentioned the Thymeleaf view can access @fooBarRepository.getFooBars()
. What am I missing here? Why can't the controller see any of the bean classes the the view can see?