Inject bean at runtime reading properties file - java

Suppose I have a class MailConsoleService and a class MailSMTPService, both implement the MailService interface. I have a class EmailJob which loads the users from a db and send an email through a MailService instance injected by Spring.
How could I read a properties and determine at runtime which implementation of MailService to inject? The properties could change at any time the app is running, obviously.
I've thought about to create a factory bean which returns the right instance from the spring container to EmailJob but I don't know how to implement this.
Note: All my beans are configured to Singleton scope, so I guess I'll have to change to Prototype EmailJob at least.
Note 2: In the factory bean how could I avoid to read the properties file each time?
Thanks!

You can do something like this:
#Component
public class Factory {
#Autowired
private MailService mailConsoleService;
#Autowired
private MailService mailSmtpService;
#Value("${mailServiceProperty}")
private String mailServiceProperty;
public MailService getMailService() {
switch (mailServiceProperty) {
case "CONSOLE":
return mailConsoleService;
case "SMTP":
return mailSmtpService;
}
return null;
}
}
Also, you need to inject properties using PropertyPlaceholderConfigurer

I am not sure I fully understood your question. But based on what I understood, if you would like to get a bean at runtime from a properties file and the file could be changed at runtime, then the below is one way of doing this. You need a handle to the app context and get the bean name from the properties file.
The prototype scope has nothing to do with this. If you declare a bean of type prototype it means that you will get a new bean instance everytime you ask the app context for it.
#Component
public class EmailJob {
#Autowired
private ApplicationContext appContext;
public void sendEmail(){
MailSender mailSender=(MailSender)appContext.getBean(<get bean name from properties file>);
// do remaining things
}
}

Related

Class instantiation using the Autowired annotation

Is its advisable to instantiate the class using #Autowired annotation like below.
#Autowired
public static Car = new Car();
Case 1
If you are trying to autowire an attribute like following:
#Autowired
public Car car;
then Spring will try to search for a Bean definition in Spring context and will also instantiate it, hence no need to again invoke constructor.
Case 2
If you are getting confused with constructor autowire like following:
#Autowired
public Driver(License license){
this.license = license;
}
Here, it is trying to get the Bean of "License" class autowired, and it is not autowireing Driver.
In your case, I feel it is case 1.
If you are trying to set it manually cause you are seeing it as not initialized, then check the spring application context configuration file/class. You will need to have something like following in it:
<context:component-scan base-package="com.test.packagename" />
If using AppConfig class then:
#Configuration
#ComponentScan("com.test.packagename")
public class AppConfig {
}
Also, make sure that your spring core is deployed in lib where it is executing.
Also check that you are instantiating the class by using Spring application context as follows:
AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Car car= (Application)context.getBean("car");
Try to refer this: http://websystique.com/spring/spring-dependency-injection-annotation-beans-auto-wiring-using-autowired-qualifier-resource-annotations-configuration/
Happy coding DBZ fan :-)

spring - ApplicationContext registerBean autowiring fails but getBean works in Spring 5

I'm using a configuration class that uses dynamic bean registration:
#Configuration
public class ConfigClass {
#Autowired
private GenericApplicationContext applicationContext;
#PostConstruct
private void init() {
System.out.println("init");
applicationContext.registerBean("exService", ExecutorService.class, () -> Executors.newFixedThreadPool(10), bd -> bd.setAutowireCandidate(true));
System.out.println("init done");
}
}
If I try to autowire the bean, application startup fails with error Field exService in com.example.DemoApplication required a bean of type 'java.util.concurrent.ExecutorService' that could not be found.
From the logs I can see that the init method on config class wasn't called before the error as the two system out statements were not printed out.
However, when I use applicationContext.getBean(ExecutorService.class) it does work without any issues.
Anyway I can get the bean to Autowire?
I'm deliberately not using the #Bean annotation because I need to register the beans dynamically based on certain conditions.
It could be because you are registering your bean in the middle of the context initialization phase. If your target bean initializes and auto-wires ExecutorService before ConfigClass #PostConstruct is invoked there simply is no bean available.
You can try forcing the initialization order:
#Component
#DependsOn("configClass")
public class MyComponent
#Autowired
private ExecutorService executorService;
However it would be cleaner to register a bean definition using BeanFactoryPostProcessor with BeanDefinitionBuilder:
#Component
public class MyBeanRegistration implements BeanFactoryPostProcessor {
#Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) {
BeanDefinitionRegistry reg = (BeanDefinitionRegistry) bf;
reg.registerBeanDefinition("exService",
BeanDefinitionBuilder
.rootBeanDefinition(ExecutorService.class)
.setFactoryMethod("newWorkStealingPool")
.getBeanDefinition());
}
}
You can do like this:
#Resource
#Lazy
private ExecutorService executorService;
It works.
Actually, I wrote the small infrastructure that deals with such issues. Here is the idea how to do this:
Create a class (lets call it MyClass) that incapsulates ExecutorService.class as a property and declare it as Bean (#Component). Even before that create an Interface (Lets call it MyInterface) that your new class would implement
Create a factory class (Lets call it MyFactory) with method MyInterface getInstance(String) that returnce an instance of your interface. In that factory crate a static property Map[String, MyInterface] and public static method that allows you to add instances of the interface to this map
In MyClass create a constructor that at the end will place newly created instance of itself into that map in the factory with the key of your class name ("MyClass")
Now The trick is that when Spring starts and initializes it creates all its beans. As your MyClass will be created its constructor will place its instance into a factory. So now anywhere in your code you can call:
MyInterface myInterface = MyFactory.getInstance("MyClass");
And you get your bean without worrying about instantiating it. Spring already did it for you. Big extra bonus is non-intrucivness - you don't have to explicitely work with Spring classes

Inject list of initialized beans

I need to inject list of already initialized beans into some another one.
I have class with definitions of some lazy beans which are used depending on environment - like on server 1 only impl1 and impl2 will be used and on server 2 impl3 and impl1
#Component
class Definitions {
#Bean
#Lazy
public A impl1() { /* ... */ }
#Bean
#Lazy
public A impl2() { /* ... */ }
#Bean
#Lazy
public A impl3() { /* ... */ }
}
And I have some monitoring bean which don't know anything about environment and just collects all those A beans exposing some health information for actuator:
#Component
class Monitoring implements HealthIndicator {
#Autowired
private List<A> monitored;
}
Problem is that spring wires all beans into monitored even if they were not initialized before (which crashes the whole thing, cause there is no suitable environment). And I need to somehow explain to spring that I only need already initialized beans - something like #AutowireOnlyThoseLazyBeansWhichAlreadyBeenUsedSomewhereElse
P.S. I know that I can use dirty hack and declare a list property inside Definitions, fill it in bean factory methods and register another one bean with reference to that list but it is too dirty.
Solution was to just write custom OSGi-like "ServiceTracker" through monitoring beans of some specific type via BeanPostProcessor and registering dynamic bean with all tracked beans in concurrent map into context.

Using class.forname but want to autowire members of the target class

I have this requirement,
My framework is in a way that it reads the class name from the configuration file as a string and I would like to use methods inside that class.
Obvious solution is to use reflection,
I have used reflection and able to call methods I wanted, but the problem is the variables inside the target class are not autowired. I understand I am not letting spring to autowire the fields by using reflection (Spring with class.forname()).
Is there a way for me to autowire the class variables instead of creating new instance? Or Am I in a deadlock situation?
Option 1: If you have access to the current Spring ApplicationContext, you could do this as follows:
String className = <load class name from configuration>
Class<?> clazz = Class.forName(className);
ApplicationContext applicationContext = <obtain Spring ApplicationContext>
applicationContext.getBean(clazz);
This of course means that the class whose instance you wish to load is a Spring managed bean. Here is a concrete example:
package org.example.beans;
#Component
class Foo { ... }
#Component
class SpringApplicationContext implements ApplicationContextAware {
private static ApplicationContext CONTEXT;
#Override
public void setApplicationContext(final ApplicationContext context) throws BeansException
CONTEXT = context;
}
public static <T> T getBean(String className) {
return CONTEXT.getBean(Class.forName(className));
}
}
Option 2: You could manually create an instance of the required class and then ask Spring to populate its dependencies.
This again requires access to the ApplicationContext. For example:
T object = Class.forName(className).newInstance();
applicationContext..getAutowireCapableBeanFactory().autowireBean(object);
It's possible. Have a look at how Spring's JUnit test integration does it. That's in the spring-test module.
The runner is SpringJUnit4ClassRunner, but the actual injection code is in DependencyInjectionTestExecutionListener.injectDependencies. It uses a Spring context that implements AutowriteCapableBeanFactory.
The code to do this looks like below. Note that this assumes that you have used annotations to indicate which fields need to be autowired.
Object bean = ...;
AutowireCapableBeanFactory beanFactory = ...;
beanFactory.autowireBeanProperties(bean, AutowireCapableBeanFactory.AUTOWIRE_NO, false);
beanFactory.initializeBean(bean, "beanName");

Spring - passing a custom instance to a constructor

I'm trying to implement a command-query design pattern into
a MVC spring based application.
I have, for example, some decorated commands using decorator pattern
like bellow:
ICommandHandler handler =
new DeadlockRetryCommandHandlerDecorator<MoveCustomerCommand>(
new TransactionCommandHandlerDecorator<MoveCustomerCommand>(
new MoveCustomerCommandHandler(
new EntityFrameworkUnitOfWork(connectionString),
// Inject other dependencies for the handler here
)
)
);
How can I inject such a handler into a controller constructor? Where should
I instantiate this handler? A place where this can be instantiated can be
the controller constructor, but this isn't the best solution. Any other ideeas?
Thanks
If you're using PropertyPlaceholderConfigurer (old) or PropertySourcesPlaceholderConfigurer (new), and your connection string is in a .properties file or environment variable you can do the following for the connection string. You can also autowire objects into a configuration class and annotate a method with #Bean to do what the Spring context xml does. With this approach you can create your beans as you wish and they're available to autowire just like you defined them in xml.
#Configuration
public class MyAppConfig {
#Autowired private MyType somethingToAutowire;
#Bean
public ICommandHandler iCommandHandler(#Value("${datasource.connectionString}")
final String connectionString) {
return new DeadlockRetryCommandHandlerDecorator<MoveCustomerCommand>();
// You obviously have access to anything autowired in your configuration
// class. Then you can #Autowire a ICommandHandler type into one of your
// beans and this method will be called to create the ICommandHandler (depending on bean scope)
}
}

Categories

Resources