I have my custom starter. Inside it, I define a repository. How should I define it in the configuration? This is how I did the usual bean before.
#Bean
#ConditionalOnMissingBean(HelloWorldController.class)
public HelloWorldController helloWorldController() {
return new HelloWorldController();
}
Repository:
#Repository
public interface CarRepository extends JpaRepository<Car, Long> {
}
And configuration
#Configuration
#EnableJpaRepositories
public class DomainConfiguration {
}
If you use this starter, context will not see the repository bean. Because I did not declare it in the configuration.I don't know how to declare it.
Related
I have a Spring component defined like this:
#Component
public class SearchIndexImpl implements SearchIndex {
IndexUpdater indexUpdater;
#Autowired
public SearchIndexImpl(final IndexUpdater indexUpdater) {
Preconditions.checkNotNull(indexUpdater);
this.indexUpdater = indexUpdater;
}
}
along with two implementations of the IndexUpdater interface, like:
#Component
public class IndexDirectUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
#Component
public class IndexQueueUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
If I try to auto-wire SearchIndexImpl like this:
#Autowired
private SearchIndex searchIndex;
I get the following exception:
org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'IndexUpdater' available: expected single matching bean but found 2: indexDirectUpdater,indexQueueUpdater
which is expected since Spring cannot tell which IndexUpdater implementation to auto-wire for the indexUpdater parameter in the constructor of SearchIndexImpl. How do I guide Spring to the bean that it should use? I understand I can use the #Qualifier annotation, but that will hard-code the index updater to one of the implementation, while I want the user to be able to specify what index updater to use. In XML, I can do something like:
<bean id="searchIndexWithDirectUpdater" class="SearchIndexImpl">
<constructor-arg index="0" ref="indexDirectUpdater"/>
</bean>
How do I do the same using Spring's Java annotations?
Use the #Qualifier annotation to specify the dependency to use :
public SearchIndexImpl(#Qualifier("indexDirectUpdater") IndexUpdater indexUpdater) {
Preconditions.checkNotNull(indexUpdater);
this.indexUpdater = indexUpdater;
}
Note that #Autowired is not needed to autowire the arg constructor of a bean since Spring 4.
To answer to your comment.
To let the class that will use the bean to define the dependency to use you could allow it to define the IndexUpdater instance to inject in the container such as :
// #Component not required any longer
public class IndexDirectUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
// #Component not required any longer
public class IndexQueueUpdater implements IndexUpdater, DisposableBean, InitializingBean {
}
Declare the bean in a #Configuration class :
#Configuration
public class MyConfiguration{
#Bean
public IndexUpdater getIndexUpdater(){
return new IndexDirectUpdater();
}
The SearchIndexImpl bean will now resolve the dependency thanks to IndexUpdater getIndexUpdater().
Here we use #Component for one bean and #Bean for its dependency.
But we could also allow a full control on the beans to instantiate by using only #Bean and by removing #Component on the 3 classes :
#Configuration
public class MyConfiguration{
#Bean
public IndexUpdater getIndexUpdater(){
return new IndexDirectUpdater();
}
#Bean
public SearchIndexImpl getSearchIndexFoo(){
return new SearchIndexImpl(getIndexUpdater());
}
I have a project structure similar to the one linked here: https://stackoverflow.com/a/29583882/1243462 . I have a util library containing a Service class in one JAR, meant to be consumed from another Java library/Maven project. However, my Service class itself uses Constructor Injection. So, where the original question had:
#Service
public class PermissionsService { ... }
I have
#Service
public class PermissionsService {
public PermissionsService(#Autowired PermissionsDao dao) {
//assign private dao field to autowired dao
}
}
And, like the original post, I want to create an instance of PermissionsService and inject it into my client/consumer application. I'm not sure of how to create a Configuration class.
#Configuration
public class PersistenceConfig {
public PermissionsService getPermissionsServiceBean() {
//What goes here?
}
}
For now, I have a workaround where I replaced the #Autowired PermissionsDao constructor argument with a field injection, and having a no-args constructor. This allows me to:
#Configuration
public class PersistenceConfig {
public PermissionsService getPermissionsServiceBean() {
return new PermissionsService();
}
}
But, since Field injection is discouraged, what is the right way to structure this code?
In your main module
#Configuration
#Import(PersistenceConfig.class)
public class ServiceConfig() {
}
In your utils module
#Configuration
#ComponentScan(basePackages = {"path-to-persistence-service-and-any-dependencies"})
public class PersistenceConfig {
}
The fact that you use constructor injection for PermissionsDao should not matter if you get the configuration right.
This is my Spring repository interface.
#Repository
public interface WebappRepository extends CrudRepository<myModel, Long> {
}
In my controller, I can instantiate the WebappRepository even though it's an interface, because of Spring annotation magic.
public class controller{
#Autowire
WebappRepository repo;
public controller(){
}
}
But this variant, using the constructor, does not work, and rightly so, because WebappRepository is an interface.
public class controller{
WebappRepository repo;
public controller(){
this.repo = new WebappRepository();
}
}
Olivier Gierke himself advocates to avoid #Autowire fields at all costs. How can I "instantiate" the repository interface in my Spring application while avoiding the #Autowire?
Inject dependencies in constructor:
#Component
public class Controller{
WebappRepository repo;
#Autowire
public Controller(WebappRepository repo){
this.repo = repo;
}
}
If you're on Spring 4.3+ and your target class has just one constructor, you can omit autowired annotation. Spring will inject all the needed dependencies for it.
So writing just below constructor would suffice:
public controller(WebappRepository repo){
this.repo = repo;
}
Reference: https://docs.spring.io/spring/docs/4.3.x/spring-framework-reference/htmlsingle/#beans-autowired-annotation
I have a #Service annotated class which provides core functionality which I can use in all my projects:
#Service
public class MyService {}
and another one which extends it to implement project specific stuff:
#Service
public class ExtendedMyService extends MyService {}
Now I would like to configure a bean alias to be able to use #Qualifier("MyServiceAlias") when autowiring it using a property:
# MyService qualifier (default: myService)
myService.qualifier=extendedMyService
In XML it would look like:
<alias name="${myService.qualifier}" alias="MyServiceAlias" />
It is also discussed here, but I need to do it w/o XML, JavaConfig only.
Is it possible and how to realize?
There is an open Jira for this: https://jira.spring.io/browse/SPR-6736
The workaround is to use #Bean in #Configuration class:
#Configuration
public class AppConfig {
#Bean(name = { "dataSource", "subsystemA-dataSource", "subsystemB-dataSource" })
public MyService myService() {}
}
If you want to use the placeholder, another workaround is to use #Bean in a #Configuration class using #Value and the Spring applicationContext.
#Configuration
public class AppConfig {
#Autowired
private ApplicationContext context;
#Bean
public MyService myService(#Value("${myService.qualifier}") String qualifier) {
return (MyService) context.getBean(qualifier);
}
}
NB : special consideration must be taken for the placeholder bean which must be loaded at the beginning (cf javadoc)
With small amount of configuration and one ImportBeanDefinitionRegistrar you can configure bean aliases via Java configuration. You can check bean-alias library project for reference - developed for the needs of my projects. Feel free to modify and/or copy the source into your own project in case the spring version used in it does not work with your setup.
Once you have the library on your path, you declare an alias through the annotation:
#Configuration
#BeanAlias(name = "fromName", alias = "toName")
public class ExampleConfiguration {
}
That's it.
How it works is that with the annotation we import a ImportBeanDefinitionRegistrar implementation
#Import(BeanAliasBeanRegistrar.class)
public #interface BeanAlias {
}
which registers the alias in the BeanDefinitionRegistry
class BeanAliasBeanRegistrar implements ImportBeanDefinitionRegistrar, PriorityOrdered {
#Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
...
registerAlias(registry, metadata.getAnnotationAttributes(BeanAlias.class.getName()));
}
private void registerAlias(BeanDefinitionRegistry registry, Map<String, Object> attributes) {
...
registry.registerAlias(name, alias);
}
}
I have created a custom API jar library where I'd like to provide some commonly used services.
But I'd like to use and autowire some of these services optionally in my implementation projects. They should not get autowired automatically.
How could I tell Spring explicit to include the following StatsLogger?
API jar:
package my.spring.config
//#Component
public class MyStatsLogger {
#Autowired
private MyService someOtherServiceForLogging;
#Scheduled(fixedDelay = 60000)
public void log() {
//logging
}
}
IMPL project:
#Configuration
#EnableScheduling
public class AppConfig {
}
Simply add the service to your context:
#Configuration
#EnableScheduling
public class AppConfig {
#Bean
public MyStatsLogger myStatsLogger() {
return new MyStatsLogger();
}
}
Since MyStatsLogger has a default constructor, all you need to is the following:
#Configuration
#EnableScheduling
public class AppConfig {
#Bean
public MyStatsLogger myStatsLogger() {
return new MyStatsLogger();
}
}
The MyService dependency in MyStatsLogger will automatically be wired by Spring if of course there is a bean of type MyService declared.