spring中向一个单例bean中注入非单例bean的方法详解

目录
  • 前言
  • 错误实例演示
  • 实现ApplicationContextAware接口
  • lookup method
  • lookup method签名
  • 总结

前言

看到这个题目相信很多小伙伴都是懵懵的,平时我们的做法大都是下面的操作

@Component
public class People{

@Autowired
private Man man;
}

这里如果Man是单例的,这种写法是没有问题的,但如果Man是原型的,这样是否会存在问题。

错误实例演示

这里有一个原型(生命周期为prototype)的类

package com.example.myDemo.component;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

@Component
@Scope(value = "prototype")
public class Man  {

    public void eat() {
        System.out.println("I like beef");
    }
}

有一个单例(生命周期为singleton)的类

package com.example.myDemo.component;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.stereotype.Component;

@Component
public class Woman {
    //使用依赖注入的方式,注入原型的Man    @Autowired
    private Man man;

    public void eat() {
        System.out.println("man:"+man);
        System.out.println("I like fruits");
    }

}

下面看测试方法,

package com.example.myDemo;

import com.example.myDemo.component.MyFactoryBean;
import com.example.myDemo.component.Woman;
import com.example.myDemo.po.Student;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration;
import org.springframework.context.ApplicationContext;

@SpringBootApplication(exclude={DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class MyDemoApplication {

    public static void main(String[] args) {
        ApplicationContext ac=SpringApplication.run(MyDemoApplication.class, args);

        Woman woman=(Woman)ac.getBean("woman");
        for(int i=0;i<5;i++){
            woman.eat();
        }

    }

}

看下测试结果,

上面的结果显示Woman中的man是单例的,因为5次循环打印打出的结果是同一个对象,发生了什么,

Woman是单例的,Man是原型的,我们使用常规的@Autowired注解注入的却是同一个实例,这里想下为什么Man是一个对象,Woman是单例的,意味着在整个spring容器中只有一个实例,在属性注入的时候肯定也只会注入一次,所以其中Man属性也只能是一个实例,出现上图的结果也就不稀奇了。

现在有这样一个需求要向单例bean中注入原型bean,要怎么实现这样的需求

实现ApplicationContextAware接口

都知道ApplicationContextAware接口是spring提供的一个扩展点,实现该接口的类可以获得ApplicationContext

Woamn类改成下面的样子

package com.example.myDemo.component;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class Woman implements ApplicationContextAware {

    private Man man;

    private ApplicationContext ac;

    public void eat() {
        this.man = (Man) ac.getBean("man");
        System.out.println("man:" + man);
        System.out.println("I like fruits");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ac = applicationContext;
    }
}

Woman实现了ApplicationContextAware接口,注入了ApplicaitonContext对象,然后再eat()方法中通过AppicationContext获得Man的实例,看测试结果,

可以看到man属性是多例的也就是符合原型模式的定义。

思考下为什么采用这种方式可以达到注入原型bean的目的

在eat()方法中使用ApplicationContext的getBean方法获取Man,eat()方法每执行一次均会调用一次getBean方法,getbean方法在执行的时候的时候会判断Man的生命周期,如果是原型(prototype)的,那么每调用一次就会重新实例化一个Man,所以会出现上述的结果。

该方法有一个很大的缺点那就是和spring耦合度太高,不符合降低系统的耦合度的要求。

lookup method

spring也考虑了向一个单例bean中注入原型bean的情况,提供了@Lookup注解,在XML配置方式下是<lookup-method>标签,这里仅使用注解的方式演示,

Woman类修改如下,

package com.example.myDemo.component;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Lookup;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;

@Component
public class Woman  {

    private Man man;

    public void eat() {
        this.man = createMan();
        System.out.println("man:" + man);
        System.out.println("I like fruits");
    }

    @Lookup
    public Man createMan(){
        return null;
    }

}

看下测试结果,

上图显示man是一个多例的,也就是向单例bean中注入了原型bean,其作用的是@Lookup注解。

通过@Lookup注解便完成了注入原型bean的目的,留个思考问题spring是如何做到的?

lookup method签名

被@Lookup注解或<lookup-method>配置的方法有如下要求,

public|protected [abstract] return-type methodName(no-argments)

  • 方法可以是public也可以是protected;
  • 方法可以是抽象的也可以是非抽象的;
  • 方法的返回值是要注入的类型,这里是prototype类型的类;
  • 方法没有入参;
  • 方法体可以是空的。具体返回值可以是null或任何类型,对结果没有影响;

总结

分享了向单例bean中注入原型bean的方式,使用lookup的方式会更简洁些。

这还可能是道面试题哦,各位小伙伴注意喽。lookup的原理下次分享,敬请关注

到此这篇关于spring中向一个单例bean中注入非单例bean的文章就介绍到这了,更多相关spring注入非单例bean内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Spring 多线程下注入bean问题详解

    本文介绍了Spring 多线程下注入bean问题详解,分享给大家,具体如下: 问题 Spring中多线程注入userThreadService注不进去,显示userThreadService为null异常 代码如下: public class UserThreadTask implements Runnable { @Autowired private UserThreadService userThreadService; @Override public void run() { AdeUs

  • 详解Spring中bean的几种注入方式

    首先,要学习Spring中的Bean的注入方式,就要先了解什么是依赖注入.依赖注入是指:让调用类对某一接口的实现类的实现类的依赖关系由第三方注入,以此来消除调用类对某一接口实现类的依赖. Spring容器中支持的依赖注入方式主要有属性注入.构造函数注入.工厂方法注入.接下来将为大家详细介绍这三种依赖注入的方式以及它们的具体配置方法. 1.属性注入 属性注入即通过setXXX( )方法注入bean的属性值或依赖对象.由于属性注入方式具有可选择性和灵活性高的特点,因此它也是实际开发中最常用的注入方式

  • Spring Bean三种注入方式详解

    在Spring容器中为一个bean配置依赖注入有三种方式: 使用属性的setter方法注入  这是最常用的方式: 使用构造器注入: 使用Filed注入(用于注解方式). Field注入是最常见的一种方式,可以采用 @Autowired 对Bean类的接口进行初始化,代码如下 @ContextConfiguration({"/META-INF/spring/amazing-base.xml"}) @RunWith(SpringJUnit4ClassRunner.class) public

  • Spring中如何动态注入Bean实例教程

    前言 在Spring中提供了非常多的方式注入实例,但是由于在初始化顺序的不同,基于标注的注入方式,容易出现未被正确注入成功的情况. 本文将介绍一种在实际项目中基于动态的方式来提取Spring管理的Bean. 下面话不多说了,来一起看看详细的介绍吧. 一.基于标注的方式注入实例 需要在Bean初始化之时,其依赖的对象必须初始化完毕.如果被注入的对象初始化晚于当前对象,则注入的对象将为null. 1.1 @Autowired 按照类型来加载Spring管理的Bean.默认情况下要求其Bean必须存在

  • 详解Java的Spring框架中bean的注入集合

    使用value属性和使用<property>标签的ref属性在你的bean配置文件中的对象引用,这两种情况下可以处理单值到一个bean,如果你想通过多元值,如Java Collection类型List, Set, Map 及 Properties.要处理这种情况,Spring提供了四种类型的如下集合的配置元素: 可以使用<list> 或<set> 来连接任何实现java.util.Collection或数组. 会遇到两种情况(a)将收集的直接的值及(b)传递一个bean

  • C#中调用DLL时未能加载文件或程序集错误的处理方法(详解)

    在加载DLL时,出现了如下的异常:未能加载文件或程序集"DMC3000, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"或它的某一个依赖项. 试图加载格式不正确的程序. 经上网查询后,其原因是x64和x86不兼容的问题.即DLL是x64的,但是VS默认生成的目标平台是x86的,因此,两者的不一致导致异常的出现. 其解决办法如下: 项目->属性->生成->目标平台->x64(与dll平台一致) 以上这篇

  • js表单元素checked、radio被选中的几种方法(详解)

    0.环境 <input type="checkbox"  value="lol"/>lol var lol = document.getElementsByTagName("input")[0]; 1.HTML DOM a.lol.checked = true; 属性的值可以不是lol,只要转为布尔值的时候为true就可以,取值时只有true.false两种 不会增加checked标记 b.lol.click(); 不会增加check

  • iOS中生成指定大小、指定颜色的二维码和条形码方法详解

    iOS7.0之后可以利用系统原生 API 生成二维码, iOS8.0之后可以生成条形码, 系统默认生成的颜色是黑色. 在这里, 利用以下方法可以生成指定大小.指定颜色的二维码和条形码, 还可以添加背景颜色.阴影效果, 以下是具体方法. 一. 生成二维码 Avilable in iOS 7.0 and later 方法如下: #pragma mark - 生成二维码 //Avilable in iOS 7.0 and later + (UIImage *)qrCodeImageWithConten

  • spring中向一个单例bean中注入非单例bean的方法详解

    目录 前言 错误实例演示 实现ApplicationContextAware接口 lookup method lookup method签名 总结 前言 看到这个题目相信很多小伙伴都是懵懵的,平时我们的做法大都是下面的操作 @Component public class People{ @Autowired private Man man; } 这里如果Man是单例的,这种写法是没有问题的,但如果Man是原型的,这样是否会存在问题. 错误实例演示 这里有一个原型(生命周期为prototype)的

  • Spring中bean的初始化和销毁几种实现方式详解

    Bean的生命周期 : 创建bean对象 – 属性赋值 – 初始化方法调用前的操作 – 初始化方法 – 初始化方法调用后的操作 – --- 销毁前操作 – 销毁方法的调用. [1]init-method和destroy-method 自定义初始化方法和销毁方法两种方式:xml配置和注解. ① xml配置 <bean id="person" class="com.core.Person" scope="singleton" init-meth

  • c++中的单例类模板的实现方法详解

     1.什么是单例模式 在架构设计时,某些类在整个系统生命周期中最多只能有一个对象存在 ( Single Instance ).如超市收银系统,其外观主要由显示器(1个).扫描枪(1个).收款箱(1个)组成,在系统正常运行期间这三部分都是唯一存在的:也就是说,显示器.扫描枪.收款箱这三部分都应该有各自的类,并且每个类只能唯一地实例化一个对象,基于这种模式的程序设计,称为单例模式. !!!单例模式只能创建一个对象,且该对象的生命周期伴随系统的整个运行期间. 2.怎么实现单例模式 思考:如何定义一个类

  • Spring中bean集合注入的方法详解

    目录 Map注入 List注入 Set注入 数组注入 应用 哈喽大家好啊,我是Hydra. Spring作为项目中不可缺少的底层框架,提供的最基础的功能就是bean的管理了.bean的注入相信大家都比较熟悉了,但是有几种不太常用到的集合注入方式,可能有的同学会不太了解,今天我们就通过实例看看它的使用. 首先,声明一个接口: public interface UserDao { String getName(); } 然后定义两个类来分别实现这个接口,并通过@Component注解把bean放入s

  • Spring中自定义数据类型转换的方法详解

    目录 类型转换服务 实现Converter接口 实现ConverterFactory接口 实现GenericConverter接口 环境:Spring5.3.12.RELEASE. Spring 3引入了一个core.onvert包,提供一个通用类型转换系统.系统定义了一个SPI来实现类型转换逻辑,以及一个API来在运行时执行类型转换.在Spring容器中,可以使用这个系统作为PropertyEditor实现的替代,将外部化的bean属性值字符串转换为所需的属性类型.还可以在应用程序中需要类型转

  • Spring Boot项目中定制拦截器的方法详解

    这篇文章主要介绍了Spring Boot项目中定制拦截器的方法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 Servlet 过滤器属于Servlet API,和Spring关系不大.除了使用过滤器包装web请求,Spring MVC还提供HandlerInterceptor(拦截器)工具.根据文档,HandlerInterceptor的功能跟过滤器类似,但拦截器提供更精细的控制能力:在request被响应之前.request被响应之后.视

  • Spring-IOC容器中的常用注解与使用方法详解

    Spring是什么? Spring是一个轻量级Java开发框架,最早有Rod Johnson创建,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题.它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构支持.Spring负责基础架构,因此Java开发者可以专注于应用程序的开发. 体系结构 核心容器(Core Container):Spring的核心容器是其他模块建立的基础,有Spring-core.Spring

随机推荐