`

Spring基本用法6——协调作用域不同步的Bean

阅读更多

         前言:之前看到Spring中一个有趣的、但值得注意的问题,就是Spring中关于如何协调Bean作用域不同步?正常来讲,两个singleton作用域的bean存在依赖关系时,或者当prototype作用域的bean依赖singleton作用域的bean时,使用Spring默认提供的依赖注入管理即可,但是如果出现这种情况:singleton作用域的Bean依赖prototype作用域Bean时,会出现使用单例Bean时,每次其依赖的多例Bean都是同一个,这与多例Bean的设计初衷相违背。

本篇文章只关注以下问题:

  • 如何将多例Bean正确注入单例Bean中

1. 问题描述

        singleton作用域的Bean只有一次实例化机会,它的依赖关系也只在初始化阶段被设置,当singleton作用域的Bean依赖prototype作用域的Bean时,Spring容器会在初始化singleton作用域的Bean之前,先创建被依赖的prototype Bean,然后才初始化singleton Bean,并将prototype Bean注入到singleton Bean,这会导致以后无论何时通过singleton Bean去访问prototype Bean时,得到的永远是最初那个prototype Bean——这就相当于singleton Bean把它所依赖的prototype Bean变成了singleton行为

       由上产生出问题:如果客户端通过singleton Bean去调用prototype Bean的方法时,始终都是调用同一个prototype Bean实例,这就违背了设置prototype Bean的初衷——本来希望它具有prototype行为,但实际上它却表现出了singleton行为

2. 解决思路

  1. 放弃依赖注入:singleton作用域的Bean每次需要prototype作用域的Bean时,主动向容器请求新的Bean实例,即可保证每次注入的prototype Bean实例都是最新的;
  2. 利用Spring提供的方法注入

       第一种方式实际上是放弃Spring带来的控制反转的优势,代码主动请求新的Bean实例,必然导致程序代码与SpringAPI耦合,造成代码污染。

       方法注入通常使用lookup方法注入,使用lookup方法注入可以让Spring容器重写容器中Bean的抽象具体方法,返回查找容器中其他Bean的结果,被查找的Bean通常是一个not-singleton Bean。Spring通过使用JDK动态代理或cglib库修改客户端的二进制代码,从而达到上述要求。

3. Demo输出

        假设有一个Chinese类型的Bean,该Bean包含一个signName()方法,执行该方法时需要依赖于Pen的方法——而且程序希望每次执行signName()方法时都使用不同的Pen Bean,因此首先需要将Pen Bean设置为prototype作用域。

       除此之外,不能直接使用普通依赖注入将Pen Bean注入Chinese Bean中,还需要使用lookup方法注入来管理Pen Bean与Chinese Bean之间的依赖关系。其大致需要两步:

  1. 将调用者Bean的实现类定义为抽象类,并定义一个抽象方法来获取被依赖的Bean;(经测试,具体类也能成功)
  2. 在<bean../>元素中添加<lookup-method../>子元素,让Spring为调用者Bean的实现类实现指定的抽象方法。

 3.1 实现被依赖Bean

          被依赖的Bean为多例模式:

package com.wj.chapter5.life.lookup;

public class Pencil implements Pen {
    
    @Override
    public String signName(String name) {
        return "我的名字是" + name;
    }
}

3.2 实现主调Bean

          主调Bean(Chinese Bean)为单例模式,其依赖于多例Bean(Pen):

package com.wj.chapter5.life.lookup;

/**
 * Chinese为单例模式,其依赖的Pen为多例
 */
public abstract class Chinese implements Person {
    private String name;
    private Pen    pen;
    
    public void setName(String name) {
        this.name = name;
    }

    // 定义抽象方法,该方法用于获取被依赖Bean
    public abstract Pen getPen();

    @Override
    public void signName() {
        pen = getPen();
        System.out.println("签名的笔型号:" + pen);
        System.out.println("我是中国人," + pen.signName(name));
    }
}

 3.3 XML配置文件

       <lookup-method../>元素需要指定两个属性:

  • name:指定需要让Spring实现的方法
  • bean:指定Spring实现该方法的返回值
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                           http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
    
    <bean id="chinese" class="com.wj.chapter5.life.lookup.Chinese">
        <property name="name" value="熊燕子"></property>
        <!-- Spring只要检测到lookup-method元素,Spring会自动为该元素的name属性所指定的方法提供实现体。-->
        <lookup-method name="getPen" bean="pencil"/>
    </bean>
    <!-- 指定Pencil Bean的作用域为prototype,希望程序每次使用该Bean时用到的总是不同的实例 -->
    <bean id="pencil" class="com.wj.chapter5.life.lookup.Pencil" scope="prototype"></bean>
    
</beans>

3.4 测试代码

package com.wj.chapter5.life.lookup;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringTest {
    
    // 1.指明xml配置文件位置,便于Spring读取,从而知道Bean的相关信息
    private static final String PATH_XML = "com/wj/chapter5/life/lookup/applicationContext-lookup.xml";
    
    @SuppressWarnings("resource")
    public static void main(String[] args) {
        // 2.以类加载路径下的xml文件作为配置文件,创建Spring容器
        ApplicationContext ctx = new ClassPathXmlApplicationContext(PATH_XML);
        
        // 3.两次获取单例Person对象
        Person p1 = ctx.getBean("chinese" , Person.class);
        Person p2 = ctx.getBean("chinese" , Person.class);
        // 4. 首先验证两次获取的单例Person是否是同一个对象
        System.out.println("两次获取的单例Person是否是同一对象:" + (p1 == p2));
        // 5. 验证依赖的多例Pencil对象是否确实是多例的
        p1.signName();
        p2.signName();
    }
}

      测试结果如下:

       可见,lookup方法注入可以正确实现singleton Bean依赖prototype Bean。

3.5  补充说明

       前面实现Person接口时,将Chinese定义为抽象类,并在里面定义抽象方法getPen()用于获取多例Pen。但这与我们习惯不符:Chinese Bean是业务上需要使用的,并不需要定为abstract抽象类,我们在开发中也确实没明确拿到抽象类Chinese的实现类(实际上Spring给我们返回的Bean就是Chinese的实现,只不过对我们透明)。

       经过测试,可以将Chinese定义为具体类,getPen()定义为空实现即可。Spring会自动帮我们实现getPen()方法的逻辑:

package com.wj.chapter5.life.lookup;

/**
 * Chinese为单例模式,其依赖的Pen为多例
 * 此处将Chinese定义为具体类,getPen为空实现
 */
public class Chinese2 implements Person {
    private String name;
    private Pen    pen;
    
    public void setName(String name) {
        this.name = name;
    }

    // 该方法用于获取被依赖Bean,此处未空实现
    public Pen getPen() {
        return null;
    }

    @Override
    public void signName() {
        pen = getPen();
        System.out.println("签名的笔型号:" + pen);
        System.out.println("我是中国人," + pen.signName(name));
    }
}

       测试结果与之前一样。

 

代码地址链接:http://pan.baidu.com/s/1dFJ5UQt 密码:dnjf

  • 大小: 35 KB
分享到:
评论

相关推荐

    Spring实战之协调作用域不同步的Bean操作示例

    Spring实战之协调作用域不同步的Bean操作示例 .spring框架是Java领域中最流行的轻量级框架之一,具有强大而灵活的IOC(控制反转)容器和AOP(面向切面编程)功能。今天,我们将深入探讨Spring实战之协调作用域不...

    spring的bean作用域

    在Spring框架中,Bean的作用域是管理Bean实例创建和存活范围的重要概念。Bean的作用域决定了在特定上下文中,Spring容器如何管理和提供Bean的实例。在Spring中,有五种主要的Bean作用域: 1. **Singleton作用域**:...

    详解Spring中bean的作用域

    对于 prototype 作用域的 bean,有一点非常重要,那就是 Spring 不能对一个 prototype bean 的整个生命周期负责,容器在初始化、配置、装饰或者是装配完一个 prototype 实例后,将它交给客户端,随后就对该 ...

    Spring 的bean的作用域总结

    Spring 的bean的作用域总结,详细的总结了 Spring 的bean的作用域

    详解Spring中Bean的生命周期和作用域及实现方式

    Spring中Bean的生命周期和作用域及实现方式 Spring是一个非常流行的Java应用程序框架,它提供了一个灵活的机制来管理Bean的生命周期和作用域。Bean的生命周期和作用域是Spring框架中两个非常重要的概念,它们决定了...

    Spring实战之Bean的作用域singleton和prototype用法分析

    在Spring框架中,Bean的作用域是决定如何管理和创建Bean实例的关键概念。本篇文章将深入探讨两种主要的作用域:singleton和...理解并正确使用这两个作用域,能够帮助开发者更有效地设计和管理Spring应用中的Bean。

    spring Bean的作用域之间有什么区别1

    Spring Bean 的作用域之间有什么区别:Bean的作用域: 可以通过scope 属性来指定bean的作用域 ①singleton: 默认值。当IOC容器

    spring bean 的作用域(scope)

    spring bean 的作用域(scope), SPringle bean的作用域

    Spring框架中Bean的生命周期 Spring中Bean有几种作用域

    在Spring框架中,Bean的生命周期管理和作用域是其核心特性之一,它们对于理解Spring如何管理对象的创建、初始化、使用以及销毁至关重要。首先,我们来深入探讨Bean的生命周期。 Spring中的Bean生命周期主要分为两个...

    spring-aware接口实现与bean作用域(spring多容器层面)

    在Spring框架中,`Spring-Aware`接口是一个重要的概念,它允许我们与Spring的应用上下文(ApplicationContext...通过正确地配置和使用`Spring-Aware`接口以及理解Bean的作用域,可以有效地在这些模块间共享和协同工作。

    JSP 中Spring Bean 的作用域详解

    JSP 中Spring Bean 的作用域详解 Bean元素有一个scope属性,用于定义Bean的作用域,该属性有如下五个值: 1&gt;singleton: 单例模式,在整个spring IOC容器中,单例模式作用域的Bean都将只生成一个实例。一般Spring...

    Spring容器中Bean的作用域编程开发技术共3页.pd

    在Spring框架中,Bean的作用域是其生命周期管理的关键部分,它决定了Bean的创建、共享以及销毁方式。本篇内容将深入探讨Spring容器中Bean的作用域编程开发技术,以帮助开发者更好地理解和利用这些特性来优化应用的...

    Spring Bean的作用域.docx

    Spring提供了五种不同的Bean作用域,每种都有其特定的使用场景和行为。 1. **Singleton作用域**:这是Spring的默认作用域,意味着无论何时从容器中请求一个特定的Bean,都会返回同一个实例。在配置文件中,可以使用...

    Spring实战之Bean的作用域request用法分析

    主要介绍了Spring实战之Bean的作用域request用法,结合实例形式分析了spring中Bean的request作用域相关使用技巧与操作注意事项,需要的朋友可以参考下

    Spring中Bean的作用域

    NULL 博文链接:https://huangminwen.iteye.com/blog/1486717

    Spring从入门到入土——Bean的作用域

    在Spring框架中,Bean的作用域是管理Bean实例生命周期的关键概念,它决定了Bean如何在应用程序中创建、使用和销毁。Bean的作用域主要有四种:Singleton、Prototype、Request和Session,每种都有其特定的适用场景和...

    Spring Bean 作用域.pdf

    spring Bean 作用域.pdf

    详解Spring中bean的scope以后使用

    ### Spring框架中Bean的作用域详解 #### 一、引言 在Spring框架中,Bean的作用域(scope)是一项非常重要的特性,它决定了Bean实例的生命周期和管理方式。正确理解和运用Bean的作用域对于优化应用程序性能、简化...

Global site tag (gtag.js) - Google Analytics