Spring Bean Alias in JavaConfig - java

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);
}
}

Related

How to specify sub-dependency for AutoWired Beans

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());
}

Initialise all classes inside a package as Beans without declaring them with #Component annotation

I have a package com.org.pages inside which there are more than 40 classes.
I want to get all these classes initialised via Spring as Beans. Now I don't want to go and add #Component for each class in that package.
Question 1: Is there any other way around to achieve this without annotating all these classes with #Component to treat them as beans?
Question 2: Once I annotate these classes with #Component annotation, do I need to #Autowire references of these classes somewhere to actually create instances of them?
You need to create your own bean definition,
do something like that
#Component
public class CustomBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Reflections reflections = new Reflections("my.package.prefix", new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses = reflections.getSubTypesOf(Object.class);
for (Class clazz : allClasses) {
GenericBeanDefinition gbd = new GenericBeanDefinition();
gbd.setBeanClass(clazz);
gbd.setAttribute("attributeName", "attributeValue");
registry.registerBeanDefinition(clazz.getSimpleName() + "RegisteredBean", gbd);
}
}
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// Custom post process the existing bean definitions
}
}
by this way you will register all classes as beans in the provided package
The documentation of the #ComponentScan annotation should give you some insight. One thing you will notice is the includeFilters argument. You can specify with this what to include. The default filters include classes that have the #Component annotation.
If you look at the #Filter annotation you can see that there are several types. The type you want here is the REGEX filter type. You define an expression and if the expression matches the class will become a component.
#ComponentScan(includeFilters =
#Filter(type=FilterType.REGEXP,
pattern= {"com.org.pages..*.*" )
Something like the above should do the trick.
You can define your own configuration class. In your configuration class you can define all the beans with #Bean annotation. Also you can do your DI there.
Imagine you have a UserDao class and a UserServcie class. And UserService has a userDao. Then the configuration file will be like this:
#Configuration
public class Config {
#Bean
public UserDao userDao(){
return new UserDao();
}
#Bean
public UserService userService(){
UserService userService = new UserService();
userService.setUserDao(userDao());
return userService;
}
}

Spring boot: Consider defining a bean of type 'com.repository.services.interfacename' in your configuration

I've deployed an spring boot application, where i created an interface and implement it with two classes.
public interface interfacename {
LinkedHashSet<String> names(String path);
}
And implemented classes are
#Component
public class class1 implements interfacename {
......
}
#Component
public class class2 implements interfacename {
......
}
Now i try to create an instance for both the classes using interface name,
#Autowired
#Qualifier("class1")
interfacename imp1;
#Autowired
#Qualifier("class2")
interfacename imp2;
It is the configuration class,
#Configuration
public class interfacenameConfig {
#Bean
#ConditionalOnProperty(name = "class1", matchIfMissing = true)
public interfacename class1Service() {
return new class1();
}
#Bean
#ConditionalOnProperty(name = "stanfordname")
public interfacename class2Service() {
return new class2();
}
}
My Project structure is,
com.repository
application.java(#SpringApplcation)
com.repository.controller
applicationcontroller.java(#RestController)
com.repository.services
interfacename.java
interfacenameconfig.java(#configuration)
class1.java(#component)
class2.java(#component)
It throws the following error
Action:
Consider defining a bean of type 'com.repository.services.interfacename' in your configuration.
please someone guide me to solve this.
Thanks in advance
In you're usage you're saying that you want beans with the ids / names class1 and class2respectively:
#Autowired
#Qualifier("class1")
interfacename imp1;
#Autowired
#Qualifier("class2")
interfacename imp2;
But in the configuration you gave them different names:
#Configuration
public class interfacenameConfig {
#Bean
#ConditionalOnProperty(name = "class1", matchIfMissing = true)
public interfacename class1Service() {
return new class1();
}
#Bean
#ConditionalOnProperty(name = "stanfordname")
public interfacename class2Service() {
return new class2();
}
}
Namely: class1Service and class2Service. Those Ids are derived from the name of the function instantiating the beans
Two possible fixes:
Give them the names you want with #Bean("class1") and #Bean("class2").
OR
Use the names they have in the qualifier, that is: #Qualifier("class1Service") and #Qualifier("class2Service")
In your configuration class you should have an annotation to prompt for component scanning to the package that your interface interfacename belongs.
E.g.:
#ComponentScan({"com.repository.services"})
In Spring-boot you usually have this annotation in the Spring boot application class
e.g.
#SpringBootApplication
#ComponentScan({"com.repository.services"})
public class MyApplication {
}
UPDATE
If you have multiple classes implementing an interface you can use the value attribute when annotating them as #Component
#Component(value="class1")
public class class1 implements interfacename
#Component(value="class2")
public class class2 implements interfacename
and then #Autowire them with #Qualifier as you already do.
Based on your last update, since the #SpringBootApplication is in the parent directory of your spring-managed beans I think you can omit the #ComponentScan annotation. Spring will scan by default all the sub-packages below com.repository.
However I still believe that the interfacenameconfig class is redundant. Why are you declaring the same beans as the ones you have annotated as #Component? Either #Component or #Bean, there is no reason having both for the same beans as far as I know and it could probably be the source of your problem.
You need to add #Service annotation above interface implementation.
e.g. #Component
public interface interfacename {
LinkedHashSet<String> names(String path);
}
#Service
public class interfaceDefinition implements interfacename{
LinkedHashSet<String> names(String path){
// write code here
}
}
I have add the qualifier annotation along with #Component annotation. Then i ensure the application it is working fine.
#Component
#Qualifier("class1")
public class class1 implements interfacename {
......
}
Thanks for the reply

Spring service implementation by environment property

I have a service interface
interface ImageSearchService {
// methods...
}
And I have 2 implementations:
#Service
class GoogleImageSearchImpl implements ImageSearchService {
// methods...
}
#Service
class AzureImageSearchImpl implements ImageSearchService {
// methods...
}
And I have a controller to use one of the both:
#Controller
ImageSearchController {
#Autowired
ImageSearchService imageSearch; // Google or Azure ?!?
}
How can I use a Environment API to set the right one implementation?
If environment property is my.search.impl=google spring needs to use GoogleImageSearchImpl, or if environment my.search.impl=google spring needs to use AzureImageSearchImpl.
You can achieve this using Spring Profiles.
interface ImageSearchService {}
class GoogleImageSearchImpl implements ImageSearchService {}
class AzureImageSearchImpl implements ImageSearchService {}
Note that the #Service annotation has been removed from both the implementation classes because we will instantiate these dynamically.
Then, in your configuration do this:
<beans>
<beans profile="azure">
<bean class="AzureImageSearchImpl"/>
</beans>
<beans profile="google">
<bean class="GoogleImageSearchImpl"/>
</beans>
</beans>
If you use Java configuration:
#Configuration
#Profile("azure")
public class AzureConfiguration {
#Bean
public ImageSearchService imageSearchService() {
return new AzureImageSearchImpl();
}
}
#Configuration
#Profile("google")
public class GoogleConfiguration {
#Bean
public ImageSearchService imageSearchService() {
return new GoogleImageSearchImpl();
}
}
When running the application, select the profile you want to run as by setting the value for the variable spring.profiles.active. You can pass it to the JVM as -Dspring.profiles.active=azure, configure it as an environment variable, etc.
Here is a sample application that shows Spring Profiles in action.
You can use #Conditional also
#Configuration
public class MyConfiguration {
#Bean
#Conditional(LinuxCondition.class)
public MyService getMyLinuxService() {
return new LinuxService();
}
}
Example given : Spring choose bean implementation at runtime

How to optionally import a Spring managed component?

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.

Categories

Resources