单例bean中使用多例bean,你会吗?

        通常情况下,我们使用的bean都是单例的,如果一个bean需要依赖于另一个bean的时候,可以在当前bean中声明另外一个bean引用,然后注入依赖的bean,此时被依赖的bean在当前bean中自始至终都是同一个实例。

    先来个案例回顾一下

package com.javacode2018.lesson001.demo13.normal;

public class ServiceA {
}
package com.javacode2018.lesson001.demo13.normal;

public class ServiceB {

    private ServiceA serviceA;

    public ServiceA getServiceA() {
        return serviceA;
    }

    public void setServiceA(ServiceA serviceA) {
        this.serviceA = serviceA;
    }
}

上面2个类,ServiceA和ServiceB,而ServiceB中需要用到ServiceA,可以通过setServiceA将serviceA注入到ServiceB中,spring配置如下:




    

    
        
    

  上面serviceA的scope是prototype,表示serviceA是多例的,每次从容器中获取serviceA都会返回一个新的对象。而serviceB的scope没有配置,默认是单例的,通过property元素将serviceA注入。

测试如下:

package com.javacode2018.lesson001.demo13;


import com.javacode2018.lesson001.demo13.normal.ServiceA;
import com.javacode2018.lesson001.demo13.normal.ServiceB;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!
 * lookupMethod的使用
 */
public class LookupMethodTest {

    @Test
    public void normalBean() {
        String beanXml = "classpath:/com/javacode2018/lesson001/demo13/normalBean.xml";
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);

        System.out.println(context.getBean(ServiceA.class)); //@1
        System.out.println(context.getBean(ServiceA.class)); //@2

        System.out.println("serviceB中的serviceA");
        ServiceB serviceB = context.getBean(ServiceB.class); //@3
        System.out.println(serviceB.getServiceA()); //@4
        System.out.println(serviceB.getServiceA()); //@5
    }
}

@1和@2从容器中按照类型查找ServiceA对应的bean。

@3:从容器中获取ServiceB

@4和@5:获取serviceB中的serviceA对象

运行结果:

com.javacode2018.lesson001.demo13.normal.ServiceA@5bfa9431
com.javacode2018.lesson001.demo13.normal.ServiceA@5db250b4
serviceB中的serviceA
com.javacode2018.lesson001.demo13.normal.ServiceA@223f3642
com.javacode2018.lesson001.demo13.normal.ServiceA@223f3642

从输出中可以看出,@1和@2输出了不同的ServiceA,而@4和@5输出的是同一个serviceA,这是因为serviceB是单例的,serviceB中的serviceA会在容器创建serviceB的时候,从容器中获取一个serviceA将其注入到serviceB中,所以自始至终serviceB中的serviceA都是同一个对象。

如果我们希望beanB中每次使用beanA的时候beanA都是一个新的实例,我们怎么实现呢?

我们可以在serviceB中加个方法去获取serviceA,这个方法中我们主动去容器中获取serviceA,那么每次获取到的都是不同的serviceA实例。

lookup-method方式实现

能不能有这样的功能,当serviceB中调用getServiceA的时候,系统自动将这个方法拦截,然后去spring容器中查找对应的serviceA对象然后返回,spring中的lookup-method就可以实现这样的功能。

下面我们使用lookup-method来实现一下。

package com.javacode2018.lesson001.demo13.lookupmethod;

public class ServiceA {
}
package com.javacode2018.lesson001.demo13.lookupmethod;

public class ServiceB {

    public void say() {
        ServiceA serviceA = this.getServiceA();
        System.out.println("this:" + this + ",serviceA:" + serviceA);
    }

    public ServiceA getServiceA() { //@1
        return null;
    }

}

注意上面的@1,这个方法中返回了一个null对象,下面我们通过spring来创建上面2个bean对象,然后让spring对上面的getServiceA方法进行拦截,返回指定的bean,如下:

lookupmethod.xml




    

    
        
    

        当我们调用serviceB中的getServiceA方法的时候,这个方法会拦截,然后会按照lookup-method元素中bean属性的值作为bean的名称去容器中查找对应bean,然后作为getServiceA的返回值返回,即调用getServiceA方法的时候,会从spring容器中查找id为serviceA的bean然后返回。

测试用例

LookupMethodTest中加个方法,如下:

@Test
public void lookupmethod() {
    String beanXml = "classpath:/com/javacode2018/lesson001/demo13/lookupmethod.xml";
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);

    System.out.println(context.getBean(com.javacode2018.lesson001.demo13.lookupmethod.ServiceA.class)); //@1
    System.out.println(context.getBean(com.javacode2018.lesson001.demo13.lookupmethod.ServiceA.class)); //@2

    System.out.println("serviceB中的serviceA");
    com.javacode2018.lesson001.demo13.lookupmethod.ServiceB serviceB = context.getBean(com.javacode2018.lesson001.demo13.lookupmethod.ServiceB.class); //@3
    serviceB.say();
    serviceB.say();
}

输出:

com.javacode2018.lesson001.demo13.lookupmethod.ServiceA@619713e5
com.javacode2018.lesson001.demo13.lookupmethod.ServiceA@708f5957
serviceB中的serviceA
this:com.javacode2018.lesson001.demo13.lookupmethod.ServiceB$$EnhancerBySpringCGLIB$$aca8be5a@68999068,serviceA:com.javacode2018.lesson001.demo13.lookupmethod.ServiceA@7722c3c3
this:com.javacode2018.lesson001.demo13.lookupmethod.ServiceB$$EnhancerBySpringCGLIB$$aca8be5a@68999068,serviceA:com.javacode2018.lesson001.demo13.lookupmethod.ServiceA@2ef3eef9

        注意最后2行的输出,serviceA是调用this.getServiceA()方法获取 ,源码中这个方法返回的是null,但是spring内部对这个方法进行了拦截,每次调用这个方法的时候,都会去容器中查找serviceA,然后返回,所以上面最后2行的输出中serviceA是有值的,并且是不同的serviceA实例。

       lookup-method:看其名字,就知道意思:方法查找,调用name属性指定的方法的时候,spring会对这个方法进行拦截,然后去容器中查找lookup-method元素中bean属性指定的bean,然后将找到的bean作为方法的返回值返回,这样就达到了单例中使用多例。

本文内容来:java路人甲

你可能感兴趣的:(Spring系列)