Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Issue receiving event on prototype bean when used through interface?

Tags:

java

spring

A little bit new with spring. When I instantiate a bean via interface, it doesn't seem to get events, if however, I use actual class implementing the interface, then the event is received. Why is this? Code below.

package javabeans.di;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;


public class HelloWorldImpl  implements HelloWorld,  ApplicationListener<ContextStartedEvent> {

    private String msg;

    public HelloWorldImpl(String s){
        msg = s;
    }

    @Override
    public void printHelloWorld() {

        System.out.println("Hello : " + msg);
    }


    public void onApplicationEvent(ContextStartedEvent event) {
          System.out.println("ContextStartedEvent Received");
    }
}

Here is the calling code:

public static void main(String[] args) {
     ConfigurableApplicationContext  ctx = new AnnotationConfigApplicationContext(HelloWorldConfig.class);

     // Let us raise a start event.
    ctx.start();           

    HelloWorld obj = (HelloWorld) ctx.getBean("helloWorld");        
    obj.printHelloWorld();

    ctx.stop();
}

Config class:

@Configuration
public class HelloWorldConfig {

   @Bean 
   @Scope("prototype")
   public HelloWorld helloWorld(){
      return new HelloWorldImpl("Hello java beans");
   }



}

The interface:

package javabeans.di;

public interface HelloWorld {

    void printHelloWorld();

}

"ContextStartedEvent Received" never gets shown if the bean has a prototype scope.

NOTE: If I change return type of bean method to HelloWorldImpl in the config class, and also change HelloWorld to HelloWorldImpl inside main (two occurrences - basically on the line where I call getBean), then this works also with prototype beans. Why would that be? Additionally if I create two instances of HelloWorldImpl in main, in a manner described in this paragraph, still the event is received only once (but that might be separate issue).

like image 880
Giorgi Moniava Avatar asked Jun 13 '16 14:06

Giorgi Moniava


People also ask

What type of Spring exception is raised when two or more bean implementing the interface in the context?

NoUniqueBeanDefinitionException. Another similar cause for the bean creation exception is Spring trying to inject a bean by type, namely by its interface, and finding two or more beans implementing that interface in the context.

Are prototype beans garbage collected?

It's because that as long as the prototype bean does not hold a reference to another resource itself, such as a database connection or a session object, it will get garbage collected as soon as all references to the object have been removed or the object goes out of scope.

Which of the following interface does Spring bean implement?

Spring provides an ApplicationContextAware interface that allows beans access to the ApplicationContext . This interface provides a single setApplicationContext method. The following code shows the use of ApplicationContextAware . The preceding code is of a bean that implements ApplicationContextAware .


2 Answers

When using java based configuration what happens is very early in the process the @Configuration classes are read with ASM (they aren't loaded through a class loader yet). Based on that read bytecode Spring creates the bean definitions and proxy based classes.

A @Bean method (regardless where it is) is basically the same as a FactoryBean. It acts more or less in the same way. When the meta data is created it does so by inspecting the method signature and using the return type to create a factory. This return type is basically used for the getObjectType method of a FactoryBean. And this the result of that method is used to determine what the bean supports.

Now when return HelloWorld as a type you get a factory creating beans of that type. When using HelloWorldImpl you will get a factory creating beans of that type. The first doesn't contain the ApplicationListener interface and as such is ignored by spring, the second however does (it is detected at that point of generating the (auto) configuration meta data).

So when using @Configuration with @Bean it is important to be as specific as possible about the return type.

like image 105
M. Deinum Avatar answered Oct 01 '22 19:10

M. Deinum


Isn't this because the interface itself doesn't have the listening method?
Shouldn't you

package javabeans.di;

public interface HelloWorld extends ApplicationListener<ContextStartedEvent>{

    void printHelloWorld();
    public void onApplicationEvent(ContextStartedEvent event);

}

And then @Override in the implementing class?

like image 42
Vale Avatar answered Oct 01 '22 18:10

Vale