4
votes

I'm working with some existing code and it is doing things I haven't seen before. I've dealt with autowiring prototype beans into singletons using method injection or getting the bean from the context using getBean(). What I am seeing in this code I am working on is a bean that is a prototype and retrieved using getBean(), and it has autowired dependencies. Most of these are singleton beans, which makes sense. But there is an autowire of another prototype bean, and from what I see, it does seem like it is getting a new bean. My question is when you autowire a prototype into a prototype, will that give you a new instance? Since the autowire request is not at startup but rather when this bean is created, does it go and create a new instance? This goes against what I thought about autowire and prototype beans and I wanted to hear an answer from out in the wild. Thanks for any insight. I'm trying to minimize my refactoring of this code as it is a bit spaghetti-ish.

example:

@Scope("prototype")
public class MyPrototypeClass  {

    @Autowired
    private ReallyGoodSingletonService svc;

    @Autowired
    private APrototypeBean bean;

    public void doSomething() {
        bean.doAThing();
    }
}

@Scope("prototype)
public class APrototypeBean {
   private int stuffgoeshere;

   public void doAThing() {
   }
}

So when doSomething() in MyPrototypeClass is called, is that "bean" a singleton or a new one for each instance of MyPrototypeClass?

3

3 Answers

12
votes

In your example, the APrototypeBean bean will be set to a brand new bean which will live through until the instance of MyPrototypeClass that you created is destroyed.

If you create a second instance of MyPrototypeClass then that second instance will receive its own APrototypeBean. With your current configuration, every time you call doSomething(), the method will be invoked on an instance of APrototypeBean that is unique for that MyPrototypeClass object.

1
votes

Your understanding of @Autowired or autowiring in general is flawed. Autowiring occurs when an instance of the bean is created and not at startup.

If you would have a singleton bean that is lazy and that bean isn't directly used nothing would happen as soon as you would retrieve the bean using for instance getBean on the application context an instance would be created, dependencies get wired, BeanPostProcessors get applied etc.

This is the same for each and every type of bean it will be processed as soon as it is created not before that.

Now to answer your question a prototype bean is a prototype bean so yes you will receive fresh instances with each call to getBean.

0
votes

Adding more explanation to @Mark Laren's answer.

As explained in Spring 4.1.6 docs

In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean, or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container only creates the singleton bean A once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.

Below approach will solve this problem, but this is not desirable because this code couples business code with Spring framework and violating IOC pattern. The following is an example of this approach:

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

So, there are 2 desirable ways to solve this problem.

1. Using Spring's method injection

  • As name suggests, Spring will implement & inject our abstract method by using @Lookup annotation from Spring 4 or tag if you use xml version. Refer this DZone article.

By using @Lookup.

from Java Doc...

An annotation that indicates 'lookup' methods, to be overridden by the container to redirect them back to the BeanFactory for a getBean call. This is essentially an annotation-based version of the XML lookup-method attribute, resulting in the same runtime arrangement.

Since: 4.1

@Component
public class MyClass1 {
  doSomething() {
    myClass2();
  }

  //I want this method to return MyClass2 prototype
  @Lookup
  public MyClass2 myClass2(){
    return null; // No need to declare this method as "abstract" method as
                 //we were doing with earlier versions of Spring & <lookup-method> xml version. 
                 //Spring will treat this method as abstract method and spring itself will provide implementation for this method dynamically.
  }
}

The above example will create new myClass2 instance each time.

2. Using Provider from Java EE (Dependency Injection for Java (JSR 330)).

@Scope(BeanDefinition.SCOPE_PROTOTYPE)
    @Component
    public static class SomeRequest {}

    @Service
    public static class SomeService {

        @Autowired
        javax.inject.Provider<SomeRequest> someRequestProvider;

        SomeRequest doSomething() {
            return someRequestProvider.get();
        }
    }

The above example will create new SomeRequest instance each time.