深入理解Spring中的Lookup(方法注入)
前言
本文主要给大家介绍了关于Spring中Lookup(方法注入)的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍:
在使用Spring时,可能会遇到这种情况:一个单例的Bean依赖另一个非单例的Bean。如果简单的使用自动装配来注入依赖,就可能会出现一些问题,如下所示:
单例的ClassA
@Component publicclassClassA{ @Autowired privateClassBclassB; publicvoidprintClass(){ System.out.println("ThisisClassA:"+this); classB.printClass(); } }
非单例的ClassB
@Component @Scope(value=SCOPE_PROTOTYPE) publicclassClassB{ publicvoidprintClass(){ System.out.println("ThisisClassB:"+this); } }
这里ClassA采用了默认的单例scope,并依赖于ClassB,而ClassB的scope是prototype,因此不是单例的,这时候跑个测试就看出这样写的问题:
@RunWith(SpringRunner.class) @ContextConfiguration(classes={ClassA.class,ClassB.class}) publicclassMyTest{ @Autowired privateClassAclassA; @Test publicvoidsimpleTest(){ for(inti=0;i<3;i++){ classA.printClass(); } } }
输出的结果是:
ThisisClassA:ClassA@282003e1
ThisisClassB:ClassB@7fad8c79
ThisisClassA:ClassA@282003e1
ThisisClassB:ClassB@7fad8c79
ThisisClassA:ClassA@282003e1
ThisisClassB:ClassB@7fad8c79
可以看到,两个类的HashCode在三次输出中都是一样。ClassA的值不变是可以理解的,因为它是单例的,但是ClassB的scope是prototype却也保持HashCode不变,似乎也成了单例?
产生这种的情况的原因是,ClassA的scope是默认的singleton,因此Context只会创建ClassA的bean一次,所以也就只有一次注入依赖的机会,容器也就无法每次给ClassA提供一个新的ClassB。
不那么好的解决方案
要解决上述问题,可以对ClassA做一些修改,让它实现ApplicationContextAware。
@Component publicclassClassAimplementsApplicationContextAware{ privateApplicationContextapplicationContext; publicvoidprintClass(){ System.out.println("ThisisClassA:"+this); getClassB().printClass(); } publicClassBgetClassB(){ returnapplicationContext.getBean(ClassB.class); } publicvoidsetApplicationContext(ApplicationContextapplicationContext)throwsBeansException{ this.applicationContext=applicationContext; } }
这样就能够在每次需要到ClassB的时候手动去Context里找到新的bean。再跑一次测试后得到了以下输出:
ThisisClassA:com.devhao.ClassA@4df828d7
ThisisClassB:com.devhao.ClassB@31206beb
ThisisClassA:com.devhao.ClassA@4df828d7
ThisisClassB:com.devhao.ClassB@3e77a1ed
ThisisClassA:com.devhao.ClassA@4df828d7
ThisisClassB:com.devhao.ClassB@3ffcd140
可以看到ClassA的HashCode在三次输出中保持不变,而ClassB的却每次都不同,说明问题得到了解决,每次调用时用到的都是新的实例。
但是这样的写法就和Spring强耦合在一起了,Spring提供了另外一种方法来降低侵入性。
@Lookup
Spring提供了一个名为@Lookup的注解,这是一个作用在方法上的注解,被其标注的方法会被重写,然后根据其返回值的类型,容器调用BeanFactory的getBean()方法来返回一个bean。
@Component publicclassClassA{ publicvoidprintClass(){ System.out.println("ThisisClassA:"+this); getClassB().printClass(); } @Lookup publicClassBgetClassB(){ returnnull; } }
可以发现简洁了很多,而且不再和Spring强耦合,再次运行测试依然可以得到正确的输出。
被标注的方法的返回值不再重要,因为容器会动态生成一个子类然后将这个被注解的方法重写/实现,最终调用的是子类的方法。
使用的@Lookup的方法需要符合如下的签名:
[abstract] theMethodName(no-arguments);
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。