Change Autowired object at runtime - java

I have an interface ClusterHelper and a class ZKClusterHelper implementing that interface. ZooKeeperConfig is a class in which we are creating instance of zookeeper object. Now, in ClusterHelperFactory we have:
#Autowired
ConfigAccessor configAccessor
#Bean
ClusterHelper clusterHelper(){
logger.info("Returning zookeeper cluster herper");
ZKClusterHelper zch = new ZKClusterHelper();
zch.setZookeeper((ZooKeeperConfig)configAccessor);
ch = zch;
return ch;
}
I am autowiring in class B.
#Autowired
ClusterHelper ch;
Now if I change the value of some fields in ZooKeeperConfig class.
How do I make it to reflect in ClusterHelper autowired in class B.

I do not think that you need to do dynamic autowiring here. I propose you to create the separate service bean for it and inject instead of creating the new one manually.
Take into account that by default spring beans have scope singleton.

Related

How many instance(s) of #Autowired "prototype" bean is(are) created during the usage of a #Component

I have a Managerclass annotated with #Component
 and #Scope
#Component
#Scope(value = "prototype")
public class Manager {
...
}
So I expect a new instance of the Manager bean will be created each time the bean is requested.
Then I have an Adapter class which uses this Manager bean. To use it, I have two ways of Autowire: 1. on the property or 2. on the constructor:
#Component
public class Adapter {
#Autowired
Manager m_Manager;
...
}
Or
#Component
public class Adapter {
Manager m_manager;
#Autowired
public Adapter(Manager manager) {
m_manager = manager;
}
...
}
Since the Adaptor class is a singleton bean, so both #Autowire the Manageron the property or on the constructor will only create one instance of the Manager? Meaning Managerbean is actually used as a singleton bean instead of prototype bean, right?
#Autowire behaves in the same way as ApplicationContext.getBean
It creates a prototype bean for each autowired instance. you can see that the prototype object in two singletons has a different identifier
So each singleton has its own prototype instance. It doesn't have any difference if you do it with #Autowire in the constructor or field.
Do Autowire on constructor is just more convenient way to avoid annotation duplication.
P.S. To define scope is better to use
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
If you don't have any other injection points, where you inject a Manager instance, then you will only have one instance in the application context, that's correct.

Builder class using Spring services

I would like to create a builder class (i.e. a class implementing the builder design pattern) in a Spring project.
The problem is that I need the builder to use some Spring services.
I obviously don't want the user to explicitly provide the services in the constructor, but when I tried to #Autowire the services, I got Autowired members must be defined in valid Spring bean. I could Annotate my builder with #Component, but that would make it a singleton which will not be desirable for a builder.
How can I inject services to my builder class without making it a singleton?
To use the example from this article, lets say I have the following builder:
BankAccount account = new BankAccount.Builder(1234L)
.withOwner(25324)
.atBranch("Springfield")
.openingBalance(100)
.atRate(2.5)
.build();
I want withOwner to use my UserService to get the actual user from the database given the id number received as a parameter. How would I go about injecting UserService to builder?
There are 2 ways to do that:
1) Put service into withOwner() method
new BankAccount.Builder(1234L)
.withOwner(25324, userService)
2) Add UserService to the Builder and create a builder factory:
#Component
class BuilderFactory {
#Autowire
private UserService user service
BankAccount.Builder newBuilder(Long id) {
return BankAccount.Builder(service, id);
}
}
Usage:
builderFactory.newBuilder(1234L)
.withOwner(25324)
How would I go about injecting UserService to builder?
In your spring bean definitions, you cannot and have not to mix objects managed by Spring and these created by yourself and which Spring is not aware.
While you may make it working but it should be used only in very specific rare cases and generally for legacy/third party dependencies reasons, not in a code where you can change that.
Definitively, you want to inject beans dependencies in beans.
This runtime error message means that you don't respect this rule :
but when I tried to #Autowire the services, I got Autowired members
must be defined in valid Spring bean
About :
I could Annotate my builder with #Component, but that would make it a
singleton which will not be desirable for a builder.
singleton is the default scope but Spring allows you to specify other scopes for a component. Just define it as a prototype bean and it will create a new instance of it at each call.
public class BankAccount{
// ...
#Component
#Scope(value="prototype")
public static class Builder{
//...
}
}

How to reinit bean object in previously autowired objects?

I need to reinit singleton bean instance for all previously autowired fields.
I tried to remove the bean from registy in app context, and re-register new object.
ConfigurableApplicationContext configContext = (ConfigurableApplicationContext)appContext;
SingletonBeanRegistry beanRegistry = configContext.getBeanFactory();
((DefaultListableBeanFactory) beanRegistry).destroySingleton("fbdProg");
((DefaultListableBeanFactory) beanRegistry).registerSingleton("fbdProg", program);
It works only once, the first time it is called.
The issue is how to reinit the bean to make new instance available in other classes, where it is autowired
If you need a new instance of this bean everytime it is injected, you can use #Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) on your bean. E.g:
#Component
#Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class MyComponent {
// methods, fields, etc.
}
Spring then creates a new instance of MyComponent everytime it is injected into another component. So Bean A will have a different instance of MyComponent than Bean B, evaluating beanA.myComponent != beanB.myComponent to true
Every class with a #Bean annotation is a singleton by default in spring boot, but you can provide a #Scope annotation if you want that the lifehook of a variable to be related to
a session
an http request
the lifehook of a serveletContext
anyway you have to keep in mind that #Bean should be singleton, for example in the persistence pattern you have an entity and a repository. The respository should be a singletone because it essentialy gives only methods to store and retrieve entities, while an entity should not be a singletone because you need an entity for every object you have to store in a database. The same for services. You may also want to taake a look at this article: spring boot scopes

Inject properties after object creation

I have a class that has some dynamic attributes which I received on the constructor.
Other properties on this same class are/can be injected by spring.
I want to know if this is possible and how should I config my application. I using spring 3 and xml configuration.
Here is an example:
class MyClass {
private MyClass2 obj2; // should be injected by spring
private Long myId;
public MyClass(Long dynamicId) {
myId = dynamicId;
}
public void doSomehting() {
obj2.doOtherStuff(this);
}
}
So, what I want, since I must create MyClass dynamically, is that after I call new MyClass(1234), the obj2 gets injected by Spring.
Is it possible?
use an ObjectFactory to retrieve the bean from the di-container. If you wrap this factory in a factory object of your own, you can set any property and still have the bean managed by Spring. Since you want the bean to have some dynamically set property think it through whether you need a singleton-scoped bean (default) or a prototype one.
If you intend to do this from xml config you need to muck around with objectfactorycreatingfactorybean. Spring documentation is excellent, just follow the example.
If you do annotation-based configuration, you just need to autowire the ObjectFactory. Note that YourClass has to be declared as a bean too!
I'm pretty sure that's not possible. It looks like you want a (scary music) factory.
class MyClassFactory {
private final MyClass2 object;
public(MyClass2 object) {
this.object = object;
}
public createMyClass(Lond id) {
return new MyClass(id, object);
}
}
If you create an instance of MyClass dynamically with the new operator, that instance is outside of Spring's bean factory, so Spring cannot inject anything. You really have two options, from what I can see.
1) Make MyClass a prototype bean so Spring gives you a new instance every time you need it. You would need to then define obj2 as a property to be set (or Autowired).
2) Inject obj2 via Spring into the class that is creating the instance of MyClass and make obj2 a constructor argument so you have to inject it.
My 0.02 from what I read from your question. A note of caution, when creating a new instance in Java, it is always outside the bean factory and all work performed outside the bean factory looses the Spring proxy behaviors. Be very careful here, you can get yourself into a rabbit hole that can be hard to track down.

Will this prototype class be created once only by Spring containter?

Since beans in Spring are by default a singleton will just a single instance of the prototype class be created if it's caller is a singleton :
#Service
MySingletonClass
{
new MyInstanceClass
}
#Scope("prototype")
MyInstanceClass
Yes.
If you inject the prototype in the singleton it will happen only one time, because Spring will "pull" the prototype from the context only one time.
#Service
MySingletonClass
{
#Autowired
private MyInstanceClass;
}
Notice that if another beans uses the prototype bean, this will create another instance.
Notice that in your example creating the object with new will not interact with Spring framework. This object will not be in the context and you will not be able to use the spring features on it.

Categories