5

I have a prototype Bean which is instantiated by singleton bean with a Provider:

@Component
@Scope("prototype")
class MyPrototype {}

@Component
class MySingleton {
    @Autowired
    javax.inject.Provider<MyPrototype> prototypeFactory;
}

This works fine, but our company rules state that @Autowired is not allowed; the common pattern is @Resource(SingletonBeanClass.BEAN_ID).

Is it possible to annotate the Provider this way so the Spring lookup can create it?

I'm aware I can add a factory method with @Lookup, or a singleton factory bean, but I prefer the Provider.

EDIT: I didn't get it to work this way and in the end had to edit spring.xml; see below for details.

daniu
  • 14,137
  • 4
  • 32
  • 53

2 Answers2

1

As you have an XML configuration file, you can configure it via XML in the following way:

<bean id="myPrototype" class="some.package.MyPrototype" scope="prototype" />

<bean id="mySingleton" class="some.package.MySingleton">
    <lookup-method name="getPrototypeFactory" bean="myPrototype "/>
</bean>

In this way, you have to access to the myPrototype with the getPrototypeFactory() and not directly to the property. You can even remove the annotations on those 2 classes.

For any extra details, you can look at the following blog post Injecting a prototype bean into a singleton bean

araknoid
  • 3,065
  • 5
  • 33
  • 35
  • It's what I ended up doing; I just wrote an answer myself because it was a bit more complicated. Setting yours to "answered" though. – daniu Oct 10 '17 at 12:17
  • Oh, and I just noticed, you can't use `ref` or the prototype bean will be created only once for the singleton, but I had to instantiate it several times during the singletons lifetime. – daniu Oct 10 '17 at 12:23
  • @daniu Corrected my answer. Wrong configuration pasted and added a little extra details. – araknoid Oct 10 '17 at 12:41
0

For reference, if someone comes across this via Google:

I ended up needing to declare it in the spring.xml. I tried @Lookup, but even that didn't work due to the prototype-bean referencing yet another prototype-bean.

This is how it was recommended here, but it does not work:

@Component("proto1")
@Scope("prototype")
class MyPrototypeBean1 {
    @Lookup(value="proto2")
    protected MyPrototypeBean2 createBean2() { return null; }
}

@Component("proto2")
@Scope("prototype")
class MyPrototypeBean2 {
}

@Component("singleton")
class MySingleton {
    @Lookup(value="proto1")
    protected MyPrototypeBean1 createBean1() { return null; }
}

This results in the error message "Cannot apply @Lookup to beans without corresponding bean definition" when trying to create "innerBean...".

I assume it is due to "lookup methods cannot get replaced on beans returned from factory methods where we can't dynamically provide a subclass for them" as is quoted in the link above.

So what I ended up doing in the spring.xml:

<bean name="proto2" class="my.package.PrototypeBean2" />
<bean name="proto1" class="my.package.PrototypeBean1" >
    <lookup-method name="createBean2" bean="proto2" />
</bean>
<bean name="singleton" class="my.package.SingletonBean" >
    <lookup-method name="createBean1" bean="proto1" />
</bean>

And this works.

For the unit tests, I had to subclass the respective classes:

class SingletonUnitTest {
    @Mock
    MyPrototypeBean1 bean1;
    @InjectMocks
    DummySingleton sut;

    @Before public void setBean1() {
        sut.bean = bean1;
    }

    static class DummySingletonBean extends MySingeton {
        MyPrototypeBean1 bean;
        protected MyPrototypeBean1 createBean1() {
            return bean;
        }
    }
}
daniu
  • 14,137
  • 4
  • 32
  • 53