Spring check if a lazy bean is instantiated - java

Suppose I have a lazy Spring managed bean MyBean within a custom scope like this:
#Configuration
public class MyConfiguration {
#Scope("custom")
#Lazy
#Bean
MyBean myBean () {
return new MyBean();
}
}
and another Spring managed bean:
#Component
class MyBeanCounter{
void checkIfMyBeanIsInstantiated () {
// Check if there is an instance of MyBean within Spring context
}
}
Within checkIfMyBeanIsInstantiated I want to check if there is an instance of MyBean within Spring context without triggering the bean creation.
The obvious idea is to inject MyBean like this:
#Component
class MyBeanCounter {
#Autowired
MyBean myBean;
void checkIfMyBeanIsInstantiated () {
if (myBean != null) {// this doesn't trigger the bean creation
// there is an instance
}
}
}
The problem with the above solution is that I have to refresh the MyBeanCounter every time the MyBean instance changes according to it's custom scope.
The above solution doesn't work because #Autowired MyBean myBean; does instantiate the bean. Replacing it with #Lazy #Autowired MyBean myBean; still doesn't work since I end up with an injected proxy.
Is there any solution?

Since you seem to not actually need or want the instance in the check, just do
#Configuration
public class MyConfiguration {
#Scope("custom")
#Lazy
#Bean
MyBean myBean (MyBeanCounter counter) {
counter.setBeanInstantiated();
return new MyBean();
}
}
#Component
class MyBeanCounter {
#Setter
private boolean beanInstantiated;
void checkIfMyBeanIsInstantiated () {
if (beanInstantiated) {
// there is an instance
}
}
}

#Component
class MyBeanCounter {
#Autowired
private ApplicationContext appContext;
void checkIfMyBeanIsInstantiated () {
String[] beans= appContext.getBeanDefinitionNames();
// now loop the beans and if it is Null it has not been Instantiated
// beans will contain all the beans name
}
}

For the "refresh" issue, make sure your scope uses a proxy which will help you retrieve the correct bean instance. You can add proxyMode = ScopedProxyMode.TARGET_CLASS to your #Scope annotation. This way you won't need to reload the MyBeanCounter so that it uses the new instance.
As for checking if the bean has bean initialized, you can directly check if it's different to null. A non initialized bean is equals to null.
So your checkIfMyBeanIsInstantiated could be as follows:
void checkIfMyBeanIsInstantiated () {
if(myBean == null) {
// Not initialized
return;
}
// Initialized
}
Also make sure you add required = false to your #Autowired annotation:
#Autowired(required = false)
MyBean myBean;
If required is not set to false (it's default value is true), myBean will be instantiated right away because Spring see it as a required component for MyBeanCounter. By setting it to false and with using #Lazy, Spring will only initialize it when it's called.
But make sure you always test it for null to avoid a NullPointerException.

Related

#Configuration & #Bean multiple which can be inject

this is a springBoot1.5.22 project.I have 3 java config Bean ,use #Configuration and #Bean annotation.
when i try to run project with debug mode。why myBean method of ConfigurationC execute ? ,the myBean method of ConfigurationA and ConfigurationB not execute。what is mechanism ?
packages of the classes
start class
#Configuration
public class ConfigurationA {
#Bean
public MyBean myBean(){
System.out.println("ConfigurationA myBean init");
return new MyBean();
}
}
#Configuration
public class ConfigurationB {
#Bean
public MyBean myBean(){
System.out.println("ConfigurationB myBean init");
return new MyBean();
}
}
#Configuration
public class ConfigurationC {
#Bean
public MyBean myBean(){
System.out.println("ConfigurationC myBean init");
return new MyBean();
}
}
Like #jackycflau said in the comment above, all your beans have the same name.
These three beans, all with the same name and type, are being loaded (but not yet initialized) sequentially into the application context (bean container). When a bean named "myBean" of type MyBean is returned from the application context, you get the one from ConfigurationC because it was the last one written into the container, which overwrote the previous two beans of the same name/type. It's apparently not being initialized until it's actually pulled from the container by client code, which is why it's the only one whose code actually runs.
please provide code snippets to analyse it more.
Bean id should be unique. I don't think you would be allowed to create beans with same beanid.
please try below code
#Configuration
public class ConfigurationA {
#Bean
public MyBean myBean(){
System.out.println("ConfigurationA myBean init");
return new MyBean();
}
}
#Configuration
public class ConfigurationB {
#Bean
public MyBean myBean1(){
System.out.println("ConfigurationB myBean init");
return new MyBean();
}
}
#Configuration
public class ConfigurationC {
#Bean
public MyBean myBean2(){
System.out.println("ConfigurationC myBean init");
return new MyBean();
}
}
You can try specifying names:
#Bean(name="bean1")
and then select the injected bean:
#Autowired
#Qualifier("bean1")
In Spring Boot 1.5.x the bean overriding is enabled by default.
This means, the bean definition tree is built first and then the last overriding bean is used (executed), all others are ignored (as they were overrided). In your case the last definition comes from ConfigurationC.
This mechanism prevents from ambitious bean definitions, where more than one definition is found, and Spring can't know which one to use - an error occurs (BeanDefinitionOverrideException).
Please note, this must be explicit enabled in Spring Boot 2.x.

Autowiring Bean before initialization of another Bean

Spring question.
I have two questions related to spring.
If I declare bean like this:
#Service
public class Downloader {
#Bean
public String bean1() {
return "bean1";
}
}
Then if other classes will be autowiring "bean1" then method bean1 will be called several times? Or one instance of bean1 will be created and reused?
Second question. How to Autowire some other bean e.g. "bean2" which is String "externalBean" that can be used to construct bean1.
#Service
public class Downloader {
#Autowire
private String bean2;
#Bean
public String bean1() {
return "bean1" + this.bean2;
}
}
Currently I'm trying to Autowire this bean2 but it is null during bean1 call. Is there any mechanism that I can specify order of this. I don't know in what context looking for this kind of info in Spring docs.
Just simple #Bean annotation used sets the scope to standard singleton, so there will be only one created. According to the docs if you want to change you need to explicitly add another annotation:
#Scope changes the bean's scope from singleton to the specified scope
Then if other classes will be autowiring "bean1" then method bean1
will be called several times? Or one instance of bean1 will be created
and reused?
There will be only a single instance of bean1, as the implicit scope is Singleton (no #Scope annotation present).
Second question. How to Autowire some other bean e.g. "bean2" which is
String "externalBean" that can be used to construct bean1.
Being that it is a String, a #Qualifier might be required
#Bean
#Qualifier("bean2")
public String bean2() {
return "bean2";
}
Then
#Bean
public String bean1(#Qualifier("bean2") final String bean2) {
return "bean1" + bean2;
}
However, this works too.
Spring will be able to look at the name of the Bean and compare it to the parameter's one.
#Bean
public String bean2() {
return "bean2";
}
and
#Bean
public String bean1(final String bean2) {
return "bean1" + bean2;
}
The order is calculated automatically by Spring, based on a Bean dependencies.

How to ask for Prototype bean in Spring service class without applicationContext

I have a component defined with prototype scope. I want to use that component in my service class. I want spring to provide me a new instance of that Bean everytime I call for it.
Component Class:
#Getter
#Setter
#Component
#Scope("prototype")
public class ProtoTypeBean {
//.. Field variables
}
Service Class:
#AllArgsConstructor
#Service
public class ServiceClass {
ProtoTypeBean prototypeBean;
ArrayList<ProtoTypeBean> prototypeBeans;
public void demoMethod(ArrayList<String> someArrayList) {
for(var singleString: someArrayList) {
prototypeBean.setFieldValue(singleString);
prototypeBeans.add(prototypeBean);
}
System.out.println(prototypeBeans.toString());
}
}
By using this configuration, I am getting the same instance of ProtoTypeBean in my prototypeBeans ArrayList. The question is, how would I make Spring understand to give me a new instance of prototypeBean every time I am calling it into the foreach loop?
I found I can use ApplicationContext.getBean() to get a new instance of the Bean in foreach loop but I also heard that it's a bad practice. So kindly help me with the best practice.
Use an ObjectProvider to lazily get the result you want. However the first prototype scoped bean will not be represented in the list of beans as, well they are prototype scoped.
#AllArgsConstructor
#Service
public class ServiceClass {
private final ObjectProvider<ProtoTypeBean> provider;
public void demoMethod(ArrayList<String> someArrayList) {
PrototypeBean pb = provider.getIfUnique();
for(var singleString: someArrayList) {
pb.setFieldValue(singleString);
pb.add(prototypeBean);
}
System.out.println(prototypeBean.toString());
}
}
Also if you don't need all the dependency injection, proxy creation etc. for your object then why bother. There is nothing wrong with just the new keyword in a Spring application. Not everything has to be managed by Spring.
Set up your prototype bean similar to this:
#Getter
#Setter
#Component
#Scope("prototype")
public class ProtoTypeBean {
final private String param;
public ProtoTypeBean(final String p) {
this.param = p;
}
}
Now, in your service class use a BeanFactory to create the beans for you:
#Service
#AllArgsConstructor
public class ServiceClass {
private final BeanFactory factory;
private List<ProtoTypeBean> prototypeBeans;
#Autowired
public ServiceClass(final BeanFactory f) {
this.factory = f;
}
public void demoMethod(List<String> someArrayList) {
this.prototypeBeans = someArrayList
.stream()
.map(param -> factory.getBean(ProtoTypeBean.class, param))
.collect(Collectors.toList());
}
}
I came across this issue recently. I am sure there must be a better way than mine, but this is how I did it:
public class ServiceClass {
ArrayList<ProtoTypeBean> prototypeBeans = new ArrayList<>();
#Autowired
ApplicationContext ctx;
public void demoMethod(ArrayList<String> someArrayList) {
for(var singleString: someArrayList) {
//magic is in below line.. getting a bean from ApplicatioContext.
ProtoTypeBean prototypeBean= ctx.getBean("protoTypeBean"); //Or ctx.getBean(ProtoTypeBean.class);
prototypeBean.setFieldValue(qBean.getFieldValue());
prototypeBeans.add(prototypeBean);
}
System.out.println(prototypeBeans.toString());
}
This way, Spring container always give you a new instance. And it is totally managed by Spring container.
The way you tried it, I tried that as well, but it would always inject one instance at the time of autowiring, hence defeating the purpose of prototyping.
You could have gone the route of using new Keyword. But then that is just regular Java instantiation and I think that new instance is not managed by Spring because it is annotated with #Component instead of #Configuration. I could be wrong here though.

Autowiring class with non SpringBoot managed constructor arguments

As the question suggests, how do you Autowire a class with non SpringBoot managed class as constructor args.
The following is a code block illustrating this:
#Component
class Prototype
{
#Autowired
private Repository repository;
private NonSpringBootManagedBean bean;
Prototype(NonSpringBootManagedBean bean)
{
this.bean = bean;
}
}
#Component
class PrototypeClient
{
#Autowired
private ApplicationContext context;
private void createNewPrototype(NonSpringBootManagedBean bean)
{
// This throws an error saying no bean of type NonSpringBootManangedBean found
Prototype prototype = context.getBean(Prototype.class, bean);
}
}
The reason I am using ApplicationContext to obtain an instance of Prototype instead of using #Autowired is because I need a new instance of Prototype within the method createNewPrototype() every time it's invoked and not a singleton instance (Also, please advise if this way obtaining a new instance is incorrect).
Update:
As others have stated to move my creation of bean to a Java configuration class and adding method annotated by #Bean and instantiating the NonSpringBootManagedBean in the #Bean method. But I think this is not possible as this NonSpringBootManagedBean is passed by caller of PrototypeClient.createNewPrototype().
Update
I have updated my above code example with a more clarity. Please refer this now.
#Component
class Prototype
{
#Autowired
private Repository repository;
// Here Session is part of javx.websocket package and cannot be added as part of
// Java configuration class with a #Bean annotation
// In this case how can I use constructor injection?
private Session session;
Prototype(Session session)
{
this.session = session;
}
}
#Component
class PrototypeClient
{
#Autowired
private ApplicationContext context;
private void createNewPrototype(Session session)
{
Prototype prototype = context.getBean(Prototype.class, session);
}
}
#ServerEndpoint(value = "/resources")
class WebSocketController
{
private PrototypeClient client = ApplicationContext.getBean(PrototypeClient.class);
#OnMessage
void handleMessage(Session session, String message)
{
client.createNewPrototype(session);
}
}
Did you know that you can change your bean scope to be a prototype reference instead of a singleton. That way you can scope a single bean definition to any number of object instances.
https://docs.spring.io/spring/docs/3.0.0.M3/reference/html/ch04s04.html
private NonSpringBootManagedBean bean = new NonSpringBootManagedBean();
#Bean
public Prototype getPrototype(){
return new Prototype(bean);
}
Spring can not Autowire an Object if it is not aware of it. Some where there need to be #Component or #Bean or some other annotation like #Service etc to tell spring to manage the instance .
Also it is suggested that if you are using a private variable in Autowire it should be part of constructor(for constructor injection ) or a setter method must be provided(setter injection)
To solve your error : you can create a java config class and place it in you base pkg (same as #SpringBootApplication or add #ComponentScan("pkg in which config is present") on class with #SpringBootApplication)
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
#Configuration
public class myconfig {
#Bean
public NonSpringBootManagedBean nonSpringBootManagedBean()
{
return new NonSpringBootManagedBean();
}
}
Define a bean with scope prototype
That is each time injected as new instance.
In SpringBoot you can use the annotation #Scope("prototype") to your bean class Prototype.
#Component
#Scope("prototype")
class Prototype {
#Autowired
private Repository repository;
private NonSpringBootManagedBean bean;
Prototype() {
// you can only modify this 'NonSpringBootManagedBean' later
// because Spring calls constructor without knowing NonSpringBootManagedBean
this.bean = new NonSpringBootManagedBean();
// do something with 'repository' because its defined
}
public void setNonSpringBootManagedBean(NonSpringBootManagedBean bean) {
this.bean = bean;
}
}
Use instances of this bean
Via injection (e.g. #Autowired to constructor) you can use different instances of this prototypical bean within other beans.
#Component
class PrototypeClient {
// ApplicationContext still used?
#Autowired
private ApplicationContext context;
private Prototype prototypeInstance;
#Autowired // injects the new instance of Prototype
public PrototypeClient(Prototype p)
this.prototypeInstance = p;
// here you can change the NSBMB
modifyPrototype();
}
private void modifyPrototype(NonSpringBootManagedBean bean) {
this.prototypeInstance.setNonSpringBootManagedBean( new NonSpringBootManagedBean() );
}
}
Why is your exception thrown?
no bean of type NonSpringBootManangedBean found
Spring complains when trying to instantiate the bean of type Prototype
Prototype prototype = context.getBean(Prototype.class, bean);
because for calling its constructor it needs to pass an argument of type NonSpringBootManagedBean. Since all this bean-instantiating is done internally by Spring(Boot), you can not intercept and tell Spring: "Hey, use this bean of class NonSpringBootManagedBean" like you tried in method createNewPrototype(NonSpringBootManagedBean bean).
Why could'nt the NonSpringBootManagedBean be managed as bean by Spring(Boot)?
Autowiring in SpringBoot is a way of dependency-injection. This means a bean has been previously instantiated by SpringBoot, automatically at startup (when Spring boots). And this bean is now injected as dependency into another bean, etc. because this other bean depends on it.
If you tell us some more background, we could possibly bring light into your situation. This can be some answers to:
What is NonSpringBootManagedBean and why is it no managed bean?
What is Prototype and for which purpose does it use NonSpringBootManagedBean?
What is PrototypeClient and why does it create its own Prototype ?
I am not sure if I have understood the relationship and purpose between your objects/classes.

Registering #Component annotated class programmatically

I'm new to spring framework, my problem is to register spring component through the spring application context I tried it with many different ways but no luck yet.
#Configuration
#ComponentScan("com.example.app")
#EnableAutoConfiguration
public class ContextDataConfiguration
{
...
}
registered it with
#PostConstruct
public void initilize()
{
AutowireCapableBeanFactory beanFactory = context.getAutowireCapableBeanFactory();
beanFactory.initializeBean( new ContextDataConfiguration(), "contextDataConfiguration" );
}
but the other beans specified in the ContextDataConfiguration class are not getting initialized with this approach.
And if I specify the ContextDataConfiguration class in the component scan it is working but it is giving me an error like
not a managed type class
Is there any alternative way to do this?
You can use the #Bean annotation in a factory method to initialize your bean. So, lets say you have a MyBean component and wants to initialize it... You can do this inside your #Configuration class:
#Bean
public MyBean myBean() {
MyBean myBean = ... // initialize your bean here
return myBean;
}
How a about
#Bean
public ContextDataConfiguration contextDataConfiguration(){
return new ContextDataConfiguration();
}
This registers an instance of ContextDataConfiguration as a bean.

Categories

Resources