博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring中不同生命周期Bean的依赖管理
阅读量:7014 次
发布时间:2019-06-28

本文共 3388 字,大约阅读时间需要 11 分钟。

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

单例的Class A

@Componentpublic 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@282003e1This is Class B: ClassB@7fad8c79This is Class A: ClassA@282003e1This is Class B: ClassB@7fad8c79This is Class A: ClassA@282003e1This 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

@Componentpublic 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@4df828d7This is Class B: com.devhao.ClassB@31206bebThis is Class A: com.devhao.ClassA@4df828d7This is Class B: com.devhao.ClassB@3e77a1edThis is Class A: com.devhao.ClassA@4df828d7This is Class B: com.devhao.ClassB@3ffcd140

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

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

@Lookup

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

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

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

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

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

[abstract]
theMethodName(no-arguments);

作用域代理

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

@Componentpublic 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来装载就好。

转载于:https://www.cnblogs.com/xz816111/p/9053696.html

你可能感兴趣的文章
Python3.5+selenium(11)脚本模块化&参数化
查看>>
Jenkins几个静态检查结果显示插件
查看>>
Java是如何处理别名(aliasing)的
查看>>
ArcGIS API for javascript开发笔记(四)——GP服务调用之GP模型的规范化制作详解...
查看>>
halcon算子翻译——region_to_bin
查看>>
巨杉数据库助力民生银行、广发银行前台智慧化业务
查看>>
DOM
查看>>
http是什么?
查看>>
Linux上iptables防火墙的基本应用教程
查看>>
[LeetCode]Symmetric Tree
查看>>
[LeetCode]Missing Number
查看>>
树莓派 基础系列 | 软件源收集 更改
查看>>
[Swust OJ 403]--集合删数
查看>>
利用正则表达式分离汉字、英文、数字
查看>>
扩展 Entity Farmework 支持随机排序
查看>>
EF的BeginTransaction 用法
查看>>
MVC 4 中编译时,让View 也弹出异常
查看>>
项目管理的“三边六拍”!(转)
查看>>
Delphi XE5 for Android之Orientation的问题
查看>>
map() Filter() lambda函数说明
查看>>