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
Related
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.
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 #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.
Well I've been watching some tutorials about Spring dependency injection as well as MVC, but I still seem to not understand how we can instantiate classes specifically?
I mean if for instance I have a variable
#Autowired
ClassA someObject;
How can I make spring create someObject as an Instance of ClassB which would extend ClassA? like someObject = new ClassB();
I don't really understand how it works in spring, does the ContextLoaderListener do it automatically or do we have to create some kind of configuration class where we specify exactly what spring should instantiate those classes to? (In this case I haven't seen that anywhere in the tutorials) If yes, then how do we specify and how does it look like? And how do we configure it to work in web.xml, etc?
You can do it like this:
Interface:
package org.better.place
public interface SuperDuperInterface{
public void saveWorld();
}
Implementation:
package org.better.place
import org.springframework.stereotype
#Component
public class SuperDuperClass implements SuperDuperInterface{
public void saveWorld(){
System.out.println("Done");
}
}
Client:
package org.better.place
import org.springframework.beans.factory.annotation.Autowire;
public class SuperDuperService{
#Autowire
private SuperDuperInterface superDuper;
public void doIt(){
superDuper.saveWorld();
}
}
Now you have your interface defined, written an implementation and marked it as a component - docs here. Now only thing left is to tell spring where to find components so they can be used for autowiring.
<beans ...>
<context:component-scan base-package="org.better.place"/>
</beans>
You have to specify the type of the class that you want to create object of in your applicationContext.xml file or you can directly annotate that class with any of #Component , #Service or #Repository if you are using latest version of Spring. In web.xml, you have to specify path of xml files as a context-param to servlet, if you are using xml-based configuration.
Yes, you have to provide a context.xml file in which you specify the instances. Give it to the ApplicationContext and it will autowire all fields for you.
http://alvinalexander.com/blog/post/java/load-spring-application-context-file-java-swing-application
Best Practices
#RestController
#RequestMapping("/order")
public class OrderController {
private final IOrderProducer _IOrderProducer;
public OrderController(IOrderProducer iorderProducer) {
this._IOrderProducer = iorderProducer;
}
#GetMapping("/OrderService")
void get() {
_IOrderProducer.CreateOrderProducer("This is a Producer");
}
}
Interface
#Service
public interface IOrderProducer {
void CreateOrderProducer(String message);
}
Implementation
public class OrderProducer implements IOrderProducer{
private KafkaTemplate<String, String> _template;
public OrderProducer(KafkaTemplate<String, String> template) {
this._template = template;
}
public void CreateOrderProducer(String message){
this._template.send("Topic1", message);
}
}
You need to include Project Lombok dependency in spring boot
Gradle implementation 'org.projectlombok:lombok'