8
votes

I am new to Spring and trying to understand the concept "Injecting a prototype bean into a singleton bean". As per my understanding In singleton, only a single instance per Spring IoC container, no matter how many time you retrieve it. validator.validate(requestId);, because still private RequestValidator validator not instantiated. I developed below example where in a singleton bean I give a reference of prototype bean like below:

<bean id="requestProcessor" class="com.injection.testing.RequestProcessor">
        <property name="validator" ref="validator" />
</bean>

<bean id="validator" class="com.injection.testing.RequestValidator" scope="prototype" />

RequestProcessor.java

public class RequestProcessor {
    private RequestValidator validator;

    public RequestProcessor(){
        System.out.println("Constructor:: RequestProcessor instance created!");
    }

    public void handleRequest(String requestId){
        System.out.println("Request ID : "+ requestId);
        validator.validate(requestId);
    }

    public RequestValidator getValidator() {
        return validator;
    }

    public void setValidator(RequestValidator validator) {
        this.validator= validator;
    }
}

RequestValidator.java

public class RequestValidator {
    private List<String> errorMessages = new ArrayList<String>();

    public RequestValidator() {
        System.out.println("Constructor:: RequestValidator instance created!");
    }

    // Validates the request and populates error messages
    public void validate(String requestId){
        System.out.println("RequestValidator :"+requestId);
    }

    public List<String> getErrorMessages() {
        return errorMessages;
    }
}

Now when I called the main method I see the following output: MainDemo.java

public class MainDemo {

    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        RequestProcessor processor = (RequestProcessor) context.getBean("requestProcessor");
        processor.handleRequest("1212");
        System.out.println("------------------------");
        processor.handleRequest("1213");
    }
}

the output is:

Constructor:: RequestProcessor instance created!
Constructor:: RequestValidator instance created!
Request ID : 1212
RequestValidator :1212
------------------------
Request ID : 1213
RequestValidator :1213

Now looking at the output, it looks like for the 2nd call processor.handleRequest("1213"); bean is not instantiated, instead already instantiated bean gets used thats why constructor wont get called again. So Prototype bean validator acting as a singleton bean only.

To me : it is expected that when ever I fetch requestProcessor from application context, it will be wired with a new validator as we declared the validator bean is of prototype scope. But this does not happen.

How to solve it ? Is my understanding correct ?

Another way:

<!-- Lookup way  -->
    <bean id="requestProcessor" class="com.injection.testing.RequestProcessor" >
        <lookup-method name="getValidator" bean="validator" />
    </bean>

    <bean id="validator" class="com.injection.testing.RequestValidator" scope="prototype" />

If I call my main method I see below output + error: Here code validator.validate(requestId); executes, private RequestValidator validator; is not instatiated and whats why null pointer exception coming.

I've shown in the below code:

public class MainDemo {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");

        RequestValidator requestValidator = (RequestValidator) context.getBean("validator");

        RequestProcessor processor = (RequestProcessor) context.getBean("requestProcessor");
        processor.handleRequest("1212");
        System.out.println("------------------------");
        processor.handleRequest("1213");
    }
}

Now I see the below error:

Constructor:: RequestProcessor instance created!
Constructor:: RequestValidator instance created!
Request ID : 1212
Exception in thread "main" java.lang.NullPointerException
    at com.injection.testing.RequestProcessor.handleRequest(RequestProcessor.java:12)
    at com.injection.testing.MainDemo.main(MainDemo.java:14)
4

4 Answers

8
votes

Injection happens only once, when the Spring context is started. If bean has prototype scope, Spring will create new prototype bean for every injection. But prototype bean will not be created every time you call its methods. Lets consider next example:

<bean id="firstRequestProcessor" class="com.injection.testing.RequestProcessor">
        <property name="validator" ref="validator" />
</bean>

<bean id="secondRequestProcessor" class="com.injection.testing.RequestProcessor">
        <property name="validator" ref="validator" />
</bean>


<bean id="validator" class="com.injection.testing.RequestValidator" scope="prototype" />

In this case both of RequestProcessor beans will have its own instance of RequestValidator bean.


Lookup method is the method, you should call every time when you need new instance of prototype bean. It's better to make this method abstract, because anyway Spring will override this method automatically. For example:

public class abstract RequestProcessor {

    public void handleRequest(String requestId){
        System.out.println("Request ID : "+ requestId);
        RequestValidator validator = createValidator(); //here Spring will create new instance of prototype bean
        validator.validate(requestId);
    }

    protected abstract RequestValidator createValidator();
}

Note, that createValidator returns instance of RequestValidator and has not any parameters. Also you don't need private class variable validator. In this case bean's configuration will looks like:

<bean id="requestProcessor" class="com.injection.testing.RequestProcessor" >
    <lookup-method name="createValidator" bean="validator" />
</bean>

<bean id="validator" class="com.injection.testing.RequestValidator" scope="prototype" />

Now every time you call createValidator method, Spring will create new instance of validator bean.

You can find more details in documentation.

0
votes

When spring creates the context, it will instantiate an instance of the RequestProcessor. During the instantiation of that instance, an instance of the RequestValidator is created and injected in the RequestProcessor.

Because any subsequent reference to the RequestProcessor Bean will access the same instance of the RequestProcessor in the context - and it is already fully constructed - there will never be a call to create a new instance of a RequestValidator. Although your Validator is scoped to be a prototype - in the example above - you only ever ask for a single copy - when you create the RequestProcessor singleton.

For the second part of your question - you misunderstand the use of the lookup-method. Spring allows you to override the method on your RequestProcessor - so when you call requestProcessor.getValidator() - spring will return a new instance of that RequestValidator. However - during the construction of your requestProcessor instance - you never initialized the validator field. Nor did spring inject a validator during instantiation. Hence the NullPointerException.

0
votes

Lookup method injection

As noted earlier, lookup method injection is an advanced feature that you should use rarely. It is useful in cases where a singleton-scoped bean has a dependency on a prototype-scoped bean. Using Java for this type of configuration provides a natural means for implementing this pattern.

public abstract class CommandManager {
    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();

        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
    return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}

Using Java-configuration support , you can create a subclass of CommandManager where the abstract createCommand() method is overridden in such a way that it looks up a new (prototype) command object:

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}

@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with command() overridden
    // to return a new prototype Command object
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}
0
votes

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 creates the singleton bean A only 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.

A solution is to forego some inversion of control. You can make bean A aware of the container by implementing the ApplicationContextAware interface, and by making a getBean("B") call to the container ask for (a typically new) bean B instance every time bean A needs it.

With this approach , our business logic is get coupled with Spring conatiner (Application Context) which does not looks to be a good Solution. An alternate of this is Lookup Method Injection.