The problem

In Spring, when a singleton Bean depends on a prototype Bean, the prototype Bean is injected only once, so the scope of the prototype Bean is invalidated and not what the code expects, causing problems.

<bean id="singletonObject" class="com.example.demo.application.data.SingletonObject" scope="singleton">
    <property name="prototypeObject" ref="prototypeObject" />
</bean>

<bean id="prototypeObject" class="com.example.demo.application.data.PrototypeObject" scope="prototype"/>
Copy the code

Obtaining prototypeObject multiple times yields the same Bean

To solve

  • Method one: implement org. Springframework. Context. ApplicationContextAware interface, at every time of using prototypeObject, call getBean method again.
public class SingletonObject implements ApplicationContextAware {
    private PrototypeObject prototypeObject;
    private ApplicationContext context;
    // getter,setter and constructor
    public PrototypeObject getPrototypeObject(a) {
        PrototypeObject prototypeObject = context.getBean("prototypeObject", PrototypeObject.class);
        this.prototypeObject = prototypeObject;
        return this.prototypeObject;
    }
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { context = applicationContext; }}public class PrototypeObject {
    public void check(a){
        System.out.println(this.hashCode());
    }
    @Override
    public int hashCode(a) {
        return super.hashCode(); }}Copy the code

The spring official website comments on this approach as follows: The preceding is not desirable, because the business code is aware of and coupled to the Spring Framework. Method Injection, a somewhat advanced feature of the Spring IoC container, lets you handle this use case cleanly. The previous approach is undesirable because the business code captures and couples the Spring framework. Method Inject is a somewhat advanced feature of the Spring IoC container that lets you handle this situation easily.

  • Method 2: Lookup-Method label
<bean id="singletonObject" class="com.example.demo.application.data.SingletonObject" scope="singleton">
    <lookup-method name="createPrototypeObject" bean="prototypeObject"/>
</bean>

<bean id="prototypeObject" class="com.example.demo.application.data.PrototypeObject" scope="prototype"/>
Copy the code
public abstract  class SingletonObject {
    private PrototypeObject prototypeObject;
    // getter, setter and constructor
    public abstract PrototypeObject createPrototypeObject(a);
}

public class PrototypeObject {
    public void check(a){
        System.out.println(this.hashCode());
    }
    @Override
    public int hashCode(a) {
        return super.hashCode(); }}Copy the code
PrototypeObject prototypeObject1 = singletonObject.createPrototypeObject();
PrototypeObject prototypeObject2 = singletonObject.createPrototypeObject();
System.out.println("PrototypeObject1 has the same address as prototypeObject2? " + (prototypeObject1 == prototypeObject2));
Copy the code

The return result is false, as expected.

  • Aop: Scoped -proxy tag
` ` `<bean id="singletonObject" class="com.example.demo.application.data.SingletonObject" scope="singleton">
    <property name="prototypeObject" ref="prototypeObject"/>
</bean>

<bean id="prototypeObject" class="com.example.demo.application.data.PrototypeObject" scope="prototype">
    <aop:scoped-proxy />
</bean>
Copy the code
public class SingletonObject {
    private PrototypeObject prototypeObject;

    public PrototypeObject getPrototypeObject(a) {
        return prototypeObject;
    }

    public void setPrototypeObject(PrototypeObject prototypeObject) {
        this.prototypeObject = prototypeObject; }}public class PrototypeObject {
    public void check(a){
        System.out.println(this.hashCode());
    }
    @Override
    public int hashCode(a) {
        return super.hashCode(); }}Copy the code
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("classpath:/spring.xml");
context.refresh();
SingletonObject singletonObject = context.getBean("singletonObject", SingletonObject.class);
PrototypeObject prototypeObject = singletonObject.getPrototypeObject();
prototypeObject.check();
prototypeObject.check();
Copy the code

The two generated Hashcodes are 597190999 and 603443293, respectively, and the two objects are significantly different. As expected.

thinking

Method two and method three are actually implemented through the proxy model. Both JDK and CGLIB proxies for static and dynamic proxies are done in the form of composition, which is treated as a member property of the proxy object. Suppose: A singleton object depends on B prototype object: method two is through proxy A object (may be rewrite, A lookup method has been implemented, Spring can complete this function, specific can refer to the official sprnig document) method three is through proxy B object to achieve. When a method of a B object is called, B’s proxy object can be regenerated through ApplicationContext. In fact, Method 3 can handle more application scenarios, please refer to the official documentation for details.