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
Related
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.
I'm new to Spring Boot, I only like to use its dependency injection, not all its opinionated frameworks but some. I know there are other alternatives for this functionality, but I would like to learn more Spring.
I am trying to have a request scoped bean and populate it with regular dependency injection. My plan is to have some User object that I can populate hold some custom business details that is easy to access that makes code clean.
#Component
#Scope(value = "request", proxyMode = ScopedProxyMode.TARGET_CLASS)
class MyBean {
#Autowired
lateinit var req: HttpServletRequest
#Autowired
lateinit var env: Environment
#PostConstruct
fun pc() {
println("I am constructed $this, $req, $env")
}
var a = 3
}
#RestController
class MyController {
#GetMapping("/api/xyz")
fun login(m: MyBean): Int {
println("new bean m")
return m.a
}
}
Every time I hit that endpoint, I see new objects are instantiated. However the inner dependencies are not autowired, they are always null. What am I doing wrong? Do I need to write a filter? But how would I know how to find all the endpoints that needs that bean to be initialized? If, I remove the request scope the variables are initialized.
RestControllers are beans like Components or Services. So in all of these you should deal with injected dependencies the same way.
Coming to the example you were adding, beans should not be parameters of actual endpoint mappings. Asking for request specific information like headers, path variables or the payload body are candidates for endpoint mapping method arguments.
For a bean instance of your class MyBean you should rather use a member variable injected directly or via the constructor of your MyController (where constructor based injection is the recommended approach).
Don't be confused when injecting your MyBean which is RequestScoped in the constructor of MyController: this is totally intended and works. Spring actually injects a proxy instance that will resolve to the request specific instance once your request is reaching the controller invocation.
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.
If I have a Spring bean that contains another non Spring bean like so :
#Component
class SpringBean {
private MyBean mb = new MyBean();
}
I receive error : unable to wire bean MyBean
But if I declare the Bean within a method :
#Component
class SpringBean {
private void myMethod(){
MyBean mb = new MyBean();
}
}
Then the bean is initialized correctly when the bean is called.
So does Spring not allow non Spring beans to be created by Spring beans ? What is the reasoning behind this?
Only beans created by the Spring container will be managed by Spring and provide dependency injection and all the benefits (and problems) that Spring gives you.
So does Spring not allow non Spring beans to be created by Spring beans ?
Spring beans are POJOs after all, so they can be manually created like any class with a public constructor. This means, you can do:
public void foo() {
SpringBean springBeanNotManagedBySpring = new SpringBean();
//use springBeanNotManagedBySpring...
}
But, as stated at the beginning of my answer, springBeanNotManagedBySpring is not managed by Spring, so it won't have the benefits of a Spring managed bean.
Note that you're doing two different things in your example:
In the first code, you have a field in the class of type MyBean and Spring tries to inject it but looks like doesn't find a Spring bean to inject. You should post the configuration of your bean in order for us to do a better analysis on this.
In the second code, you're creating a local variable (completely different from a field in class) of type MyBean, where the instantiation is up to the programmer.
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.