How to switch/change bean implementation at runtime spring boot? - java

I have a parent bean and a child bean in the spring configuration class like attached. How can I inject the child bean dynamically into parent bean based on some condition (like feature toggle).
#Configuration
public class FooConfig {
#Bean
public void parentBean(#Qualifier("dependantBean") Object bean){
//use the correct bean at runtime
}
#Bean("dependantBean")
#FeatureToggle(feature = "feature.one", expectedToBeOn = true)
public Object test1(){
//some logic and returns a object
return new Object();
}
#Bean("dependantBean")
#FeatureToggle(feature = "feature.one",expectedToBeOn = false)
public Object test2(){
//some logic and returns a object which is different from test1 method
return new Object();
}
}

In your example it seems that the decision would be taken when the application starts.
In that case you can handle what bean gets injected with profiles (#Profile).
Side note: Use different names for the method names that create the same bean. The bean name could be the same in this case.
Another alternative could be to create a Map with the 2 dependant beans, and inject the map into the parent bean constructor.
Based on the logic you want you can use any of the two beans in the Map.
In this case use different names for the the two dependant beans.

Related

Retrieving Classes of Beans prior/during instantiation phase

In an Spring Application it is possible to retrieve all (?) Beans with applicationContext.getBeansOfType(Object.class). This is of course only possible, after all Beans have been created.
So, if I call this method in the constructor of a Bean, I have to be lucky, to be the last Bean to be created, to have access to all of them.
As far as I understand the life cycle of Spring Beans, there is a phase in which BeanDefinitions are created, before the Beans are initialized.
How is it possible to retrive all created BeanDefinitions in the constructor of a Bean?
Can I also retrive the types (as Class) of those BeanDefinitions? The type BeanDefinition seems to only provide the "current bean class name of this bean definition".
Or is the only way to get those types after all Beans have been constructed (e.g. #PostConstruct)?
Maybe this code could help
for (String name : applicationContext.getBeanFactory().getBeanDefinitionNames()) {
BeanDefinition beanDefinition = applicationContext.getBeanFactory().getBeanDefinition(name);
String className = beanDefinition.getBeanClassName();
Class<?> clazz = Class.forName(className);
}
The loop gets you all the BeanDefinitions and then you load the class for each and do what you want?
By the way this might not be a good way to use Spring but it will work.
You can create a last bean by putting it for example in an #Configuration class with a minimum initialization order, so that it is the last one with
#Order(Ordered.LOWEST_PRECEDENCE), that would be it:
#Configuration
#Order(Ordered.LOWEST_PRECEDENCE)
public class Last {
#Autowired
ApplicationContext applicationContext;
#Bean
public String lastBean() {
applicationContext.getBeanDefinitionNames(); //retrive all created BeanDefinitions in the constructor of a Bean
applicationContext.getBeansOfType(Object.class); //retrive the types (as Class)
return "lastBean";
}
}

Getting Spring beans by method invocation vs new operator

I am confused about this little topic. Somewhere I read that if a class is annotated with #Component, it is spring managed bean and whenever it is required, spring will provide it. I am confusing it with scope of a bean. Let me explain:
Let's say a class
#Component
public class Example{ }
If I instantiate this class in other class using new Example(), would container always provide me the same Example object all the time? Or would it return me new object every time?
Here comes the confusing part:
If in the same class I have two beans like this:
#Component
public class Example {
#Bean DataSource sqlDataSource() {
// some logic
}
#Bean #Scope("prototype") SomeObject getSomeObject() {
return new SomeObject(sqlDataSource()); //**
}
}
What will happen in this case? sqlDataSource() method invocation would return the same object again and again every time SomeObject bean is requested, or new instance of DataSource will be returned every time SomeObject is requested?
#Bean is a method-level annotation that indicates Spring to create a bean when that method is invoked. It means to have the same functionality thatn tag in XML config.
This annotation must be used inside of a #Configuration annotated class, otherwise if you invoke the method from another method it will be a normal java new operation, not spring's. See this post --> #Bean inside class with #Configuration and witout it
Bearing this in mind new SomeObject(sqlDataSource()); would be equal to new SomeObject(new SqlDataSource());
if you annotate Example with #Configuration what will happen is that you'll get always a new SomeObject instance with the same sqlDataSource object, this means that Spring will take care of creating ONLY ONE sqlDataSource because it is singleton.
#Bean DataSource sqlDataSource() {
// some logic
}
This defines a singleton instance of DataSource. So everytime you request an instance of SomeObject a new SomeObject will be created (while it is defined in the prototype scope) but all of them will share the same DataSource object (since it's a singleton bean).

Reinitialize spring #Autowire bean

I have a scenario where I need to initialize a bean based on application configuration during startup. Later, due to dynamic configuration fetched based on an event, I have to update the bean.
This bean can't be updated but can only be replaced with a new instance.
Does using the new operator initialize only the local instance or will it change the bean?
#Component
public class TestComp {
#Autowired
private BeanA beanA;
public void updateBean() {
beanA = new BeanA("new value");
}
}
I referred the bean in another class and checked after I initialized it with new. It reflected the new object. But, I need a confirmation from experts if it does.
I have a scenario where I need to initialize a bean based on application configuration during startup.
It's fine. The singleton scope is a good choice here.
Later, due to dynamic configuration fetched based on an event, I have to update the bean.
It's a problem. Updating a bean in the context is a complex process: you need to remove the existing bean definition, add a new one, and update all the beans that are somehow related to the bean (reinitialise these components, refresh the context). Technically, it's possible and it has been simplified by Spring Cloud's #RefreshScope.
Does using new operator initialize only the local instance or will it change the bean?
It affects only the field in this class. No one is aware of the change. ApplicationContext#getBean still will return the old object, and all the components will be (or have already been) initialised with the old instance.
I referred the bean in another class and checked after I initialized it with new. It reflected the new object.
It can't be true. Probably, it refers to the TestComp#beanA field, not to its own BeanA field.
The solution I am suggesting is to define a custom bean scope based on the events you are receiving. It will keep the bean and the context updated.
It sounds like you want a factory instead. Below is a rough idea of what that might look like; your needs may vary.
#Component
public class BeanFactory {
private volatile BeanA beanAInstance;
public BeanA createBeanA(String value) {
if (null == beanAInstance) {
synchronized (this) {
if (null == beanAInstance) {
beanAInstance = new BeanA(value);
}
}
}
return beanAInstance;
}
public void refreshBeanA(String newValue) {
synchronized (this) {
beanAInstance = new BeanA(newValue);
}
}
}
You then wire this in, and based on configuration, you can then refresh and use the new value. Bear in mind that this would change the value you get from this bean.

Same instance with two ids in Spring

Is there any way to use two different ids for referring to the same instance in a Spring context?
What I'm trying to find is a way for aliasing the bean id, for a singleton scope.
http://docs.spring.io/autorepo/docs/spring/4.1.3.RELEASE/javadoc-api/org/springframework/context/annotation/Bean.html
Bean Names section
the name attribute may be used. Also note that name accepts an array of Strings. This is in order to allow for specifying multiple names (i.e., aliases) for a single bean.
#Bean(name={"b1","b2"}) // bean available as 'b1' and 'b2', but not 'myBean'
public MyBean myBean() {
// instantiate and configure MyBean obj
return obj;
}
You could use the #Bean annotation (Spring 3.0+) with its name value.
The name of this bean, or if plural, aliases for this bean. If left unspecified the name of the bean is the name of the annotated method. If specified, the method name is ignored.
public #interface Bean {
String[] name() default {};
...
}
For example, your bean of C class will be available as a or b (BUT not c) in a Spring context.
public #Bean(name = {"a", "b"}) C getInstance() { ... }

Why in Spring I am not allowed to annotate a final class with #Configuration?

I am studying for the Spring Core certification and I have some doubts related to the answer of this question founded on the study material stuff.
Why are you not allowed to annotate a final class with #Configuration
My reasoning is the following one for substantiate this assertion:
Consider the following configuration class:
#Bean
public AccountRepository accountRepository() {
return new JdbcAccountRepository();
}
#Bean
public TransferService transferService() {
TransferServiceImpl service = new TransferServiceImpl();
service.setAccountRepository(accountRepository());
return service;
}
#Bean
public AccountService accountService() {
return new AccountServiceImpl(accountRepository());
}
At first look this situation could appear strange because the first method (accountRepository()) instantiates an JdbcAccountRepository object as a bean having id=AccountRepository that, following the Spring default behavior, is a singleton
The second and the third method call twice more time the accountRepository() method that should instantiate twice more JdbcAccountRepository objects and this is not possibile because it is singleton !!!
So, to solve this situation Spring use the Inheritance-based Proxies strategy that expect to create a child class of my configuration class (the one annoted by #Configuration) and it is does:
For each bean, an instance is cached in the child class
Child class only calls super at first instantiation
So the child class is the entry point because the following behavior is implemented by this child class:
public class AppConfig$$EnhancerByCGLIB$ extends AppConfig {
public AccountRepository accountRepository() {
// if bean is in the applicationContext
// return bean
// else call super.accountRepository() and store bean in context
}
public TransferService transferService() {
// if bean is in the applicationContext, return bean
// else call super.transferService() and store bean in context
}
.....................................................
.....................................................
.....................................................
}
So if I annotate a configuration class with final Spring can't have this behavior because in Java a final class cannot be subclassed
Is it correct?
Using the same reasoning can I also assert that in Spring I can't have a final method annoted with #Bean annotation?
Because, as shown in the previous example, I have that when at startup time is created the child class (the proxy) of my configuration class happens that for each bean, an instance is cached in the child class and if it is final it is not possible (but I am absolutly not sure about this assertion)
Am I missing something? Can you give me the exact explaination?
Tnx
Spring creates dynamic proxies for classes annotated with #Configuration classes. Spring uses CGLIB to extend your class to create proxy. Hence, configuration classes cannot be final.
Regarding accountRepository() being invoked twice:
If you invoke accountRepository() method to create an instance, it is no more a Spring managed bean. Spring will not have any idea of the instances created in this manner. Hence, you will end up with multiple instances of JdbcAccountRepository
You can preserve the singleton behavior if you configure as below:
#Bean
public TransferService transferService(JdbcAccountRepository jdbcAcctRepo) {
TransferServiceImpl service = new TransferServiceImpl();
service.setAccountRepository(jdbcAcctRepo);
return service;
}
#Bean
public AccountService accountService(JdbcAccountRepository jdbcAcctRepo) {
return new AccountServiceImpl(jdbcAcctRepo);
}

Categories

Resources