通常情况下,我们使用的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实例。
能不能有这样的功能,当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路人甲