在使用Spring时,可能会遇到这种情况:一个单例的Bean依赖另一个非单例的Bean。如果简单的使用自动装配来注入依赖,就可能会出现一些问题,如下所示:

单例的Class A

@Component
public class ClassA {
@Autowired
private ClassB classB; public void printClass() {
System.out.println("This is Class A: " + this);
classB.printClass();
}
}

非单例的Class B

@Component
@Scope(value = SCOPE_PROTOTYPE)
public class ClassB {
public void printClass() {
System.out.println("This is Class B: " + this);
}
}

这里Class A采用了默认的单例scope,并依赖于Class B, 而Class B的scope是prototype,因此不是单例的,这时候跑个测试就看出这样写的问题:


@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {ClassA.class, ClassB.class})
public class MyTest {
@Autowired
private ClassA classA; @Test
public void simpleTest() {
for (int i = 0; i < 3; i++) {
classA.printClass();
}
}
}

输出的结果是:

This is Class A: ClassA@282003e1
This is Class B: ClassB@7fad8c79
This is Class A: ClassA@282003e1
This is Class B: ClassB@7fad8c79
This is Class A: ClassA@282003e1
This is Class B: ClassB@7fad8c79

可以看到,两个类的Hash Code在三次输出中都是一样。Class A的值不变是可以理解的,因为它是单例的,但是Class B的scope是prototype却也保持Hash Code不变,似乎也成了单例?

产生这种的情况的原因是,Class A的scope是默认的singleton,因此Context只会创建Class A的bean一次,所以也就只有一次注入依赖的机会,容器也就无法每次给Class A提供一个新的Class B

不那么好的解决方案

要解决上述问题,可以对Class A做一些修改,让它实现ApplicationContextAware

@Component
public class ClassA implements ApplicationContextAware {
private ApplicationContext applicationContext; public void printClass() {
System.out.println("This is Class A: " + this);
getClassB().printClass();
} public ClassB getClassB() {
return applicationContext.getBean(ClassB.class);
} public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}

这样就能够在每次需要到Class B的时候手动去Context里找到新的bean。再跑一次测试后得到了以下输出:

This is Class A: com.devhao.ClassA@4df828d7
This is Class B: com.devhao.ClassB@31206beb
This is Class A: com.devhao.ClassA@4df828d7
This is Class B: com.devhao.ClassB@3e77a1ed
This is Class A: com.devhao.ClassA@4df828d7
This is Class B: com.devhao.ClassB@3ffcd140

可以看到Class AHash Code在三次输出中保持不变,而Class B的却每次都不同,说明问题得到了解决,每次调用时用到的都是新的实例。

但是这样的写法就和Spring强耦合在一起了,Spring提供了另外两种方法来降低侵入性。

@Lookup

Spring提供了一个名为@Lookup的注解,这是一个作用在方法上的注解,被其标注的方法会被重写,然后根据其返回值的类型,容器调用BeanFactorygetBean()方法来返回一个bean。

@Component
public class ClassA {
public void printClass() {
System.out.println("This is Class A: " + this);
getClassB().printClass();
} @Lookup
public ClassB getClassB() {
return null;
}
}

可以发现简洁了很多,而且不再和Spring强耦合,再次运行测试依然可以得到正确的输出。

被标注的方法的返回值不再重要,因为容器会动态生成一个子类然后将这个被注解的方法重写/实现,最终调用的是子类的方法。

使用的@Lookup的方法需要符合如下的签名:

<public|protected> [abstract] <return-type> theMethodName(no-arguments);

作用域代理

Spring还提供了另外一种方法来解决这个问题。简单来说就是如果一个bean A对另外一个作用域更短的bean B有依赖,那么在实例化bean A并注入依赖时,注入的不是bean B本身,而是一个AOP代理,这个代理可以找到实际的bean

@Component
public class ClassA {
@Autowired
private ClassB classB; public void printClass() {
System.out.println("This is Class A: " + this);
classB.printClass();
}
}
@Component
@Scope(value = SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class ClassB {
public void printClass() {
System.out.println("This is Class B: " + this);
}
}

可以看出,使用这种方法的好处是仅需对bean B进行简单的配置,并且bean A根本不用意识到代理的存在,将bean B当做一个正常的bean来装载就好。

Spring中的Lookup(方法注入)的更多相关文章

  1. Spring应用教程-2 方法注入

    作者:禅楼望月(http://www.cnblogs.com/yaoyinglong) 我们通常使用lookup方法注入,它可使Spring替换一个Bean的抽象或具体方法,返回查找容器中,其他Bea ...

  2. [置顶] Spring中DI设置器注入

    Java的反射机制可以说是在Spring中发挥的淋漓尽致,下面要看的代码就是通过反射机制来实现向一个类注入其实际依赖的类型,这个过程的实现会交由Spring容器来帮我们完成. JavaBean中针对属 ...

  3. 【坑】Spring中抽象父类属性注入,子类调用父类方法使用父类注入属性

    运行环境 idea 2017.1.1 spring 3.2.9.RELEASE 需求背景 需要实现一个功能,该功能有2个场景A.B,大同小异 抽象一个抽象基类Base,实现了基本相同的方法BaseMe ...

  4. 关于spring中无法将service注入到servlet中的问题

    首先,servlet是动态网页项目区别于普通的java项目的,是动态网页项目中web.xml主要配置文件管理的,而spring只能管理普通的pojo,而没办法直接注入,尽管你的注入方式和配置方式都没有 ...

  5. Spring中Ioc容器的注入方式

    1 通过setter方法注入 bean类: package com.test; public class UserServiceImplement implements IUserService { ...

  6. spring中bean配置和注入场景分析

    bean与spring容器的关系 Bean配置信息定义了Bean的实现及依赖关系,Spring容器根据各种形式的Bean配置信息在容器内部建立Bean定义注册表,然后根据注册表加载.实例化Bean,并 ...

  7. Spring 自动装配;方法注入

    通过配置defalut—autowire属性,Spring IOC容器可以自动为程序注入Bean:默认是no(不启用自动装配). default—autowire的类型有: byName:通过名称自动 ...

  8. Spring中的destroy-method方法

    1. Bean标签的destroy-method方法 配置数据源的时候,会有一个destroy-method方法 <bean id = "dataSource" class  ...

  9. Spring中如何向 Bean注入系统属性或环境变量

    [转自] http://unmi.cc/spring-injection-system-properties-env/ 在 Spring 中为 javabean 注入属性文件中的属性值一般人都知道的, ...

随机推荐

  1. 优秀设计师应当知道的20大UI设计原则

    世界级图形设计大师Paul Rand(保罗.兰德)曾经说过:"设计绝不是简单的排列组合与简单地再编辑,它应当充满着价值和意义,去说明道理,去删繁就简,去阐明演绎,去修饰美化,去赞美褒扬,使其 ...

  2. myfirstBI项目总结

    app 应用信息统计: saiku安装:http://blog.csdn.net/longshenlmj/article/details/17359645 workbench解压即用,http://b ...

  3. BT币(金融有风险,投资需谨慎)哥的失败投资

    谁都知道bt币是一个旁氏骗局, 而进去的人,就必须保证自己不赔钱,所以只能随着大潮往前走,谁也不能让它跌 压垮骆驼的最后一根稻草, 还是幕后有个 推手, 在炒作 BT币, 事实上,作为新的投资项目,B ...

  4. SQL Queries and Multi-Org Architecture in Release 12

    In this Document   Abstract   History   Details   Previous Releases   Release 12   Multi-Org Session ...

  5. 关于IOS中使用支付功能(以支付宝为例)

    支付宝是第三方支付平台,简单来说就是协调客户,商户,银行三者关系的方便平台 使用支付宝进行一个完整的支付功能,大致有以下步骤: a 与支付宝进行签约,获得商户ID(partner)和账号ID(sell ...

  6. rails中select不能响应多选的解决办法

    在rails4.2中如果你写如下代码,post的select无法传回多选内容,即使你select设置为多选: <select id='id_size' name='name_size' mult ...

  7. Oracle 远程访问配置

    服务端配置 如果不想自己写,可以通过 Net Manager 来配置. 以下配置文件中的 localhost 改为 ip 地址,否则,远程不能访问. 1.网络监听配置 # listener.ora N ...

  8. HttpWebRequest类库注意事项以及常见问题

    ServicePoint: 提供HTTP连接的连接管理ServicePointManager: 管理ServicePoint 对象的集合 注意事项:1. 将http的request的keepAlive ...

  9. 如何在Visual Studio 2017中使用C# 7+语法

    前言 之前不知看过哪位前辈的博文有点印象C# 7控制台开始支持执行异步方法,然后闲来无事,搞着,搞着没搞出来,然后就写了这篇博文,不喜勿喷,或许对您有帮助. 在Visual Studio 2017配置 ...

  10. hive的高级查询(group by、 order by、 join 、 distribute by、sort by、 clusrer by、 union all等)

    查询操作 group by. order by. join . distribute by. sort by. clusrer by. union all 底层的实现 mapreduce 常见的聚合操 ...