I have a component EmbeddedRedis that depends on a configuration object RedisConfig parsed from the application's property file. There are different property files, corresponding to the possible application profiles that can be run. Thus, when run in profile master, the component EmbeddedRedis will be provisioned according to the master profile.
In a test class, that is supposed to set-up a local Redis cluster, I also require Redis objects provisioned according to all other profiles. I sketched my idea below using the #Qualifier annotation, which does not bring the desired result.
#Autowired #Qualifier("dev-cluster-master")
private Redis embeddedRedisMaster;
#Autowired #Qualifier("dev-cluster-slave-001")
private Redis embeddedRedisSlave1;
#Autowired #Qualifier("dev-cluster-slave-002")
private Redis embeddedRedisSlave2;
How can I archive the desired result in Spring Boot? If that doesn't work directly, would it also suffice to obtain the before-mentioned configuration objects parsed from the different property files.
#Component
#ConfigurationProperties(prefix = "spring.redis")
public class RedisConfig {
....
}
Thanks in advance!
You can do something like this:
Consider you have a class definition (Redis in your example)
public class CustomService {
private String name;
public CustomService(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
And a configuration class like:
#Configuration
public class Config {
#Bean
#Profile("master")
CustomService serverConfig1(){
CustomService service1 = new CustomService("master");
return service1;
}
#Bean
#Profile("slave")
CustomService serverConfig2(){
CustomService service1 = new CustomService("slave");
return service1;
}
}
which initiate 2 different objects based on current active profile. If current active profile is "master", then serverConfig1() will get executed, otherwise serverConfig2().
And finally autowired your service/object like this:
#Autowired
CustomService service;
This will depends on above executed bean definition in configuration file.
And property file should look like this:
spring.profiles.active=slave
So in this example, after executing above code, the value of 'name' in CustomService service; will be "slave" instead of "master", because current active profile is "slave" and thus "serverConfig2()" will get executed
You can do something like this: Consider you have an interface definition:
public interface SomeService {
String someMethod;
}
And two implemented class:
#Profile("production")
#Service
public class SomeServiceProd implements SomeService {
#Override String someMethod() {return "production";}
}
#Profile("development")
#Service
public class SomeServiceProd implements SomeService {
#Override String someMethod() {return "development";}
}
And use this service in test and main code:
#Autowired SomeService service;
If you need your component only with certain profile and you don't want to or can't create a common interface you could inject like this:
#Autowired
private Optional< Redis > redis;
You would have to check if present on each different object on every profile.
If you share a common interface use the other answer solution to create different beans implementing the interface per each profile.
Related
Given that I have two implementation of a Processor interface:
One Synchronous:
#Service("Synchronous")
#Primary
public class SyncProcessor implements Processor { ... }
Other Async:
#Service("Asynchronous")
public class AsynchronousProcessor implements Processor { ... }
The class that uses these services:
public class TestController{
private final Processor processor;
public TestController(final Processorprocessor) {
this.processo r= processor;
}
}
These services and the controller class sit in a common library framework maven project (say project A) and they get injected as dependencies in other projects (e.g. project B & C).
I want project B & C to use SyncProcessor by default which it does since it is annotated with #Primary.
However, the async service gets also instantiated and I would like to prevent that.
It should only be instantiated when in the projects B and C I do the following:
#Bean("customAsync")
public Processor processor(MyRepo repo,
JobRunner jobRunner) {
return new AsynchronousProcessor (repo, jobRunner);
}
The problem I am currently having is AsyncProcessor instance is getting created multiple times. How can I prevent this from happening?
The other issue I am having is that even when I create the bean in project B, the synchronous bean is being called. How can I use async?
You can use #Profile so you can decide on startup what processor you use.
#Component
#Profile("sync")
public class SyncProcessor implements Processor {...}
#Component
#Profile("async")
public class SyncProcessor implements Processor {...}
This way, your project can decide which will get created by setting the spring.profiles.active property accordingly (sync or async). It's not really intended for libraries, so your use case isn't as simple as you intend,.
By default, the following will inject your #Primary Processor
#RestController
public class TestController{
private final Processor processor;
public TestController(final Processorprocessor) {
this.processor = processor;
}
}
If you want a processor other that #Primary injected, you have to specify it
#RestController
public class TestController{
#Autowired
#Qualifier("customAsync")
private final Processor processor;
public TestController(final Processorprocessor) {
this.processor = processor;
}
}
I have the following scenario: A factory interface with 2 implementations, while the second one used as decorator to the first one.
public final class BaseMailFactory implements MailFactory {
#Autowired
private final ClassA classA;
#Autowired
private final ClassB classB;
public Mail createMail(){
.
.
.
}
}
public final class MetricAwareMailFactory implements MailFactory {
private final MailFactory mailFactory;
public Mail createMail(){
var mail = mailFactory.createMail();
return new MetricsAwareMail(mail);
}
}
#Configuration
public class MailFactoryConfiguration {
#Bean
public MailFactory metricsAwareMailFactory(){
return new MetricAwareMailFactory(???);
}
}
The wrapped object previously instantiated through spring container (context), hence all auto wired fields populated successfully. After creation of the second implementation I am struggle to find an elegant way to initialize the first instance without adding multiple implementations to MailFactory interface which leads to application startup errors due to ambiguity.
I know that I can use qualifies for that but they pollute my code.
I am looking for a way to instantiate a class through spring but without actually register it as a bean, in older spring versions I get to use anonymous beans for such purposes.
I found the #Primary annotation useful here:
#Configuration
public class MailFactoryConfiguration {
#Bean
#Lazy
MailFactory baseMailFactory(){
return new BaseMailFactory();
}
#Bean
#Primary
public MailFactory metricsAwareMailFactory(){
return new MetricAwareMailFactory(baseMailFactory());
}
}
I such way, both beans will be created but the primary one will be selected in case of multiple implementations.
In my Spring Boot application, i have a configuration, which reads entries from a Mongo database.
After this is done, my subclass of AbstractMongoEventListener is created, even though it operates on a different table and different scope (my own custom #CustomerScope).
Here is the listener:
#CustomerScoped
#Component
public class ProjectsRepositoryListener extends AbstractMongoEventListener<Project> {
#Override
public void onAfterSave(Project source, DBObject dbo) {
System.out.println("saved");
}
}
And here the configuration:
#Configuration
public class MyConfig {
#Autowired
private CustomersRepository customers;
#PostConstruct
public void initializeCustomers() {
for (Customer customer : customers.findAll()) {
System.out.println(customer.getName());
}
}
}
I find it surprising that the listener is instantiated at all. Especially since it is instantiated well after the call to the customers repository has finished.
Is there a way to prevent this? I was thinking of programmatically registering it per table/scope, without annotation magic.
To prevent auto-instantiation, the listener must not be annotated as #Component. The configuration needs to get ahold of the ApplicationContext, which can be autowired.
Thus, my configuration class looks like this:
#Autowired
private AbstractApplicationContext context;
private void registerListeners() {
ProjectsRepositoryListener firstListener = beanFactory.createBean(ProjectsRepositoryListener.class);
context.addApplicationListener(firstListener);
MySecondListener secondListener = beanFactory.createBean(MySecondListener.class);
context.addApplicationListener(secondListener);
}
Note that this works for any ApplicationListener, not just AbstractMongoEventListener.
I am writing services in Spring boot that get their configurations from Spring cloud. These services are multi-tenant and the tenant is based on the host name.
what I have now is
public class MyController {
#Autowired
public MyController(MyServiceFactory factory) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#RequestHeader header, #PathVariable id) {
return factory.getMyService(header).handle(id);
}
}
where MyServiceFactory looks something like...
public class MyServiceFactory {
private final HashMap<String, MyService> serviceRegistry = new HashMap<>();
public MyService getMyService(String key) {
return serviceRegistry.get(key);
}
MyServiceFactory withService(String key, MyService service) {
this.serviceRegistry.put(key, service);
return this;
}
}
then in a configuration file
#Configuration
public ServiceFactoryConfiguration {
#Bean
public MyServiceFactory getMyServiceFactory() {
return new MyServiceFactory()
.withService("client1", new MyService1())
.withService("client2", new MyService2());
}
}
While what I have now works, I don't like that I need to create a factory for every dependency my controller may have. I'd like to have my code look something like this...
public class MyController {
#Autowired
public MyController(MyService service) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#PathVariable id) {
return service.handle(id);
}
}
with a configuration file like
#Configuration
public class MyServiceConfiguration() {
#Bean
#Qualifier("Client1")
public MyService getMyService1() {
return new MyService1();
}
#Bean
#Qualifier("Client2")
public MyService getMyService2() {
return new MyService2();
}
}
I can get the code that I want to write if I use a profile at application start up. But I want to have lots of different DNS records pointing to the same (pool of) instance(s) and have an instance be able to handle requests for different clients. I want to be able to swap out profiles on a per request basis.
Is this possible to do?
Spring profiles would not help here, you would need one application context per client, and that seems not what you want.
Instead you could use scoped beans.
Create your client dependent beans with scope 'client' :
#Bean
#Scope(value="client",proxyMode = ScopedProxyMode.INTERFACES)
#Primary
MyService myService(){
//does not really matter, which instance you create here
//the scope will create the real instance
//may be you can even return null, did not try that.
return new MyServiceDummy();
}
There will be at least 3 beans of type MyService : the scoped one, and one for each client. The annotation #Primary tells spring to always use the scoped bean for injection.
Create a scope :
public class ClientScope implements Scope {
#Autowired
BeanFactory beanFactory;
Object get(String name, ObjectFactory<?> objectFactory){
//we do not use the objectFactory here, instead the beanFactory
//you somehow have to know which client is the current
//from the config, current request, session, or ThreadLocal..
String client=findCurrentClient(..);
//client now is something like 'Client1'
//check if your cache (HashMap) contains an instance with
//BeanName = name for the client, if true, return that
..
//if not, create a new instance of the bean with the given name
//for the current client. Easiest way using a naming convention
String clientBeanName=client+'.'+name;
Object clientBean=BeanFactory.getBean(clientBeanName);
//put in cache ...
return clientBean;
};
}
And your client specific beans are configured like this :
#Bean('Client1.myService')
public MyService getMyService1() {
return new MyService1();
}
#Bean('Client2.myService')
public MyService getMyService2() {
return new MyService2();
}
Did not test it but used it in my projects. Should work.
tutorial spring custom scope
I've got two beans. Both implement the mailing function. One is only working when it is deployed to an application server. The other one is used for testing.
We have profile for each developer and environment. I want to wire the testing bean only when actually testing. The other bean should be used when not testing. How can I archive this?
#Component
#Profile("localtest")
public class OfflineMail implements Mailing {}
Solution approaches:
Using "default" I read this somewhere, but there seems to be no fall-back to "default" for a profile like "dev":
#Component
#Profile("default")
public class OnlineMail implements Mailing {}
-> Exception for no bean for wiring found.
Leaving the profile out:
#Component
public class OnlineMail implements Mailing {}
-> Throws a unique exception when running the "localtest" profile.
Adding all profiles:
#Component
#Profile("prod")
#Profile("integration")
#Profile("test")
#Profile("dev1")
#Profile("dev2")
#Profile("dev3")
...
public class OnlineMail implements Mailing {}
This is actually working, however our devs aren't numbered they use "dev<WindowsLogin>" and adding the profiles, may work for one bean, but one will get into trouble when using it for several beans as this definitely gets ugly.
Using something like #Profile("!localtest") doesn't seem to work as well.
Does anyone know a nicer way to get a "wire by default if no specific bean is found"?
I finally found an easy solution.
The online mail is just wired by default.
#Component
public class OnlineMail implements Mailing {}
Using the #Primary annotation the offline mail takes precedence over the OnlineMail and avoids the Unique exception.
#Component
#Profile("localtest")
#Primary
public class OfflineMail implements Mailing {}
Try this:
#Component
#Profile("production")
public class OnlineMail implements Mailing {}
#Component
#Profile("localtest")
public class OfflineMail implements Mailing {}
Then run tests using #ActiveProfiles("localtest") and run production enviroment using "production" as DEFAULT profile.
Also I hope in next version of Spring ActiveProfilesResolver will be introduced SPR-10338 - it may be helpfull for you (to avoid "dev1", "dev2" and so on).
Spring supports inject the Bean by #Profile very well:
interface Talkative {
String talk();
}
#Component
#Profile("dev")
class Cat implements Talkative {
public String talk() {
return "Meow.";
}
}
#Component
#Profile("prod")
class Dog implements Talkative {
public String talk() {
return "Woof!";
}
}
Works in unit test
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:applicationContex-test.xml"})
#ActiveProfiles(value = "dev")
public class InjectByDevProfileTest
{
#Autowired
Talkative talkative;
#Test
public void TestTalkative() {
String result = talkative.talk();
Assert.assertEquals("Meow.", result);
}
}
Works in Main():
#Component
public class Main {
public static void main(String[] args) {
// Enable a "dev" profile
System.setProperty(AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME, "dev");
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
Main p = context.getBean(Main.class);
p.start(args);
}
#Autowired
private Talkative talkative;
private void start(String[] args) {
System.out.println(talkative.talk());
}
}
Check this for the Demo code: https://github.com/m2land/InjectByProfile