Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >Spring IoC 容器基础使用

Spring IoC 容器基础使用

作者头像
李鸿坤
发布于 2020-07-18 10:40:57
发布于 2020-07-18 10:40:57
61200
代码可运行
举报
文章被收录于专栏:泛泛聊后端泛泛聊后端
运行总次数:0
代码可运行

Spring IoC 容器Java世界对于IoC实现的事实上的工业标准。基本上大型 Java应用都绕不过它。以至于滴滴在转型golang的时候搞了一个go-spring出来。本文介绍了Spring IoC 容器的基本使用。

基本概念

org.springframework.beans和org.springframework.context两个包是Spring框架IoC容器的基础。它们分别提供了BeanFactory和ApplicationContext。顾名思义BeanFactory是一个bean的工厂,它提供了一种配置的机制,可以管理任何对象。而ApplicationContext是它的子接口,对他实现了一些增强,如:

  • 更容易与Spring的AOP特性集成
  • 消息资源处理,可以用于国际化
  • 容器事件的发布
  • 特定于应用程序层的上下文,如用于web应用程序的WebApplicationContext。

简而言之,BeanFactory提供了配置框架和基本功能,ApplicationContext添加了更多增强功能。ApplicationContext是BeanFactory的完整超集。

Spring IoC容器管理的对象称为bean。bean是由Spring IoC容器实例化、组装和管理的对象。否则,bean只是应用程序中许多对象中的一个。bean以及它们之间的依赖关系反映在容器使用的配置元数据中。

容器使用
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// beans.xml
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="hhdDevice" class="com.lihongkun.labs.spring.container.devicewriter.HhdWriter"></bean>
    <bean id="ssdDevice" class="com.lihongkun.labs.spring.container.devicewriter.SsdWriter"></bean>
    <bean id="deviceWriter" class="com.lihongkun.labs.spring.container.devicewriter.DeviceWriter">
        <property name="deviceWriter" ref="hhdDevice" />
    </bean>
</beans>

先前举过一个例子,如果托管给spring容器则配置如上。每个对象都是一个bean,用bean的标签来声明,id即其唯一标识,class 为其实现类,容器会通过这个类型去初始化一个对象。上述配置文件中的deviceWriter依赖了一个IDeviceWriter接口的实现,使用hhdDevice注入其中,DeviceWriter的实现中并不知道其存在。

使用BeanFactory来实例化一个IoC容器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.lihongkun.labs.spring.container;

import com.lihongkun.labs.spring.container.devicewriter.DeviceWriter;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;

public class BeanFactoryLab {
    public static void main(String[] args) {
        DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
        reader.loadBeanDefinitions(new ClassPathResource("beans.xml"));

        DeviceWriter deviceWriter = beanFactory.getBean("deviceWriter", DeviceWriter.class);
        deviceWriter.saveToDevice();
    }
}

使用ApplicationContext来实例化一个IoC容器

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.lihongkun.labs.spring.container;

import com.lihongkun.labs.spring.container.devicewriter.DeviceWriter;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ApplicationContextLab {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
        DeviceWriter deviceWriter = applicationContext.getBean("deviceWriter", DeviceWriter.class);
        deviceWriter.saveToDevice();
    }
}

实现同样的功能,使用方式类似,bean定义文件可以互通。

配置引用

简单的场景使用一个bean配置文件可以解决问题,如果项目比较复杂,可以按bean的领域进行分类来划分配置文件。这时候就需要使用到import的功能。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<beans>
    <import resource="services.xml"/>
    <import resource="resources/messageSource.xml"/>
    <import resource="/resources/themeSource.xml"/>

    <bean id="bean1" class="..."/>
    <bean id="bean2" class="..."/>
</beans>
命名和别名

bean除了id 还可以使用name为其命名,name必须是容器中唯一。不仅如此,还能为其使用别名,即alias标签的使用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<alias name="fromName" alias="toName"/>

这么做只是为了拥有更好的语义性。假设有两个子系统,它们都各自声明了数据源,其实是同一个。在一个应用中集成了两个子系统。则不需要初始化两个数据源。但是又改变其中的配置实现。那么只需要初始化myApp-dataSource,将其设置别名,两个不同的子系统就分别能够引用到了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<alias name="myApp-dataSource" alias="subsystemA-dataSource"/>
<alias name="myApp-dataSource" alias="subsystemB-dataSource"/>

一致地命名bean使配置更易于阅读和理解。另外,如果使用Spring AOP,按名称相关的bean进行配置,语义性的作用就更加明显了。

Bean实例化方式

Spring容器的初始化bean可以通过几种方式:

构造函数
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<bean id="exampleBean" class="examples.ExampleBean"/>

实际使用的是反射,容器在进行实例化的时候直接使用反射调用了ExampleBean的构造函数

静态工厂方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// application.xml
<bean id="clientService"
    class="examples.ClientService"
    factory-method="createInstance"/>

// static factory method
public class ClientService {
    private static ClientService clientService = new ClientService();
    private ClientService() {}

    public static ClientService createInstance() {
        return clientService;
    }
}

class的指定没有区别,只是使用factory-method来告诉容器实例化的时候调用此方法就能实例化,而不同通过反射的方式去创建类对象。

实例工厂方法
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// application.xml
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
    <!-- inject any dependencies required by this locator bean -->
</bean>

<bean id="clientService"
    factory-bean="serviceLocator"
    factory-method="createClientServiceInstance"/>

<bean id="accountService"
    factory-bean="serviceLocator"
    factory-method="createAccountServiceInstance"/>

// instance
public class DefaultServiceLocator {

    private static ClientService clientService = new ClientServiceImpl();

    private static AccountService accountService = new AccountServiceImpl();

    public ClientService createClientServiceInstance() {
        return clientService;
    }

    public AccountService createAccountServiceInstance() {
        return accountService;
    }
}

实例工厂的方式需要先实例化工厂类,然后使用factory-bean来引用。进而使用factory-method来指定使用factory-bean的哪个方法来实例化bean。

依赖注入

Spring容器的依赖注入方式分为以下几种

构造函数
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<beans>
    <bean id="beanOne" class="x.y.ThingOne">
        <constructor-arg index="0" ref="beanTwo"/>
        <constructor-arg index="1" ref="beanThree"/>
    </bean>

    <bean id="beanTwo" class="x.y.ThingTwo"/>

    <bean id="beanThree" class="x.y.ThingThree"/>
</beans>

beanOne使用constructor-arg指定了构造函数的参数进行依赖bean的注入。

如果是一些原始类型常量可以使用type指定类型进行注入,因为有时候类型推断是一件不靠谱的事情。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<bean id="exampleBean" class="examples.ExampleBean">
    <constructor-arg type="int" value="7500000"/>
    <constructor-arg type="java.lang.String" value="42"/>
</bean>
Setter函数
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<beans>
    <bean id="hhdDevice" class="com.lihongkun.labs.spring.container.devicewriter.HhdWriter"></bean>
    <bean id="deviceWriter" class="com.lihongkun.labs.spring.container.devicewriter.DeviceWriter">
        <property name="deviceWriter" ref="hhdDevice" />
    </bean>
</beans>

如果class的实现里面有setXXX的属性 则可以使用 property 标签进行属性的注入。

如何选择

基于构造函数和基于Setter函数的依赖注入并不冲突,它们可以混合使用。选择的原则应该倾向于,如果是强依赖则使用构造函数注入,如果是可选的依赖则使用Setter函数注入。

Setter注入应该主要用于可选的依赖项,这些依赖项可以在类中分配合理的默认值。否则,非空检查必须在代码使用依赖项的任何地方执行。setter注入的一个好处是setter方法使该类的对象能够在以后重新配置或重新注入。因此,通过JMX MBeans进行管理是setter注入的一个引人注目的用例。

这个关注点其实做国内业界已经被滥用了,基本上大部分的注入都是基于Setter。

循环依赖

如果主要使用构造函数注入,则有可能创建一个无法解决的循环依赖场景。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
public class CircularBeanA {
    private CircularBeanB circularBeanB;

    public CircularBeanA(CircularBeanB circularBeanB){
        this.circularBeanB = circularBeanB;
    }
}

public class CircularBeanB {
    private CircularBeanA circularBeanA;

    public CircularBeanB(CircularBeanA circularBeanA){
        this.circularBeanA = circularBeanA;
    }
}

如上述两个类,使用基于构造函数的依赖注入,配置如下

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="circularBeanA" class="com.lihongkun.labs.spring.container.circular.CircularBeanA">
        <constructor-arg ref="circularBeanB" />
    </bean>

    <bean id="circularBeanB" class="com.lihongkun.labs.spring.container.circular.CircularBeanB">
        <constructor-arg ref="circularBeanA" />
    </bean>

</beans>

那么容器初始化的时候将抛出一个BeanCurrentlyInCreationException并且提示是不是循环依赖了。这时候如果使Setter注入则可以解决。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 需要改造下上述两个类的实现
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="circularBeanA" class="com.lihongkun.labs.spring.container.circular.CircularBeanA">
        <property name="circularBeanB" ref="circularBeanB" />
    </bean>

    <bean id="circularBeanB" class="com.lihongkun.labs.spring.container.circular.CircularBeanB">
        <property name="circularBeanA" ref="circularBeanA" />
    </bean>

</beans>
作用域

Spring Bean的作用域除了常见的Singleton 和 Prototype 还有随着扩展功能而增加的Request, Session, Application, and WebSocket Scopes

Singleton

顾名思义是一个单例模式的bean,也就是在容器中只有一个实例存在。

Prototype

每次需要的时候进行创建

Request, Session, Application, and WebSocket

Request, Session, Application, and WebSocket 作用域只存在于 XmlWebApplicationContext,通常是把Web相关的bean创建托管到Spring 容器的时候才存在。最典型的应用就是Spring MVC,其中请求或者会话对象的。

小结

Spring IoC容器 提供了一套基础配置框架,让使用者把应用程序中类的管理托管到容器中。被托管到容器中实例化的对象我们称为Bean。通常使用ApplicationContext 及其子类来实例化Spring IoC容器。

Bean可以进行命名和使用别名、指定实例化的方式、指定实例化后的作用域、设定依赖注入的方式 和 注入的Bean。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-05-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 泛泛聊后端 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
第1章: Spring 基础第1章:Spring 基础
为了体验spring,所以建立的是一个 maven quick start 的项目,建立后的 pom.xml 配置如下:
zhuanxu
2018/08/23
2690
第1章: Spring 基础第1章:Spring 基础
对IoC容器和Bean的学习笔记
The term "Spring" means different things in different contexts. It can be used to refer to the Spring Framework project itself, which is where it all started. Over time, other Spring projects have been built on top of the Spring Framework. Most often, when people say "Spring", they mean the entire family of projects. This reference documentation focuses on the foundation: the Spring Framework itself.
dongfanger
2023/08/09
2500
对IoC容器和Bean的学习笔记
Spring入门:The IoC Container,实践篇(上)
Spring 框架的 IoC 容器(Inversion of Control,Ioc)是 Spring 框架中最基础、最重要的部分。Ioc 也被称为依赖注入(Dependency Injection,DI),是一种将组件依赖项的创建和管理外部化的技术。
WEBJ2EE
2019/09/17
9140
Spring入门:The IoC Container,实践篇(上)
Spring 框架学习(三)---- IOC创建对象
  写完了第一个Spring的程序,相信已经对spring已经有所了解了,那么我们这节来了解一下,IOC是如何创建对象,什么时候创建对象的。
RAIN7
2022/06/27
4310
Spring 框架学习(三)---- IOC创建对象
Spring IoC 容器扩展
托管给Spring IoC 容器的Bean虽然不知道容器的存在,但是容器也提供了完整的扩展点,让使用者动态干预bean的定义和实例化,以及生命周期相关的钩子。
李鸿坤
2020/07/18
5790
Spring IOC 容器源码分析
Spring 最重要的概念是 IOC 和 AOP,本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器。既然大家平时都要用到 Spring,怎么可以不好好了解 Spring 呢?阅读本文并不能让你成为 Spring 专家,不过一定有助于大家理解 Spring 的很多概念,帮助大家排查应用中和 Spring 相关的一些问题。
yaphetsfang
2022/05/10
2710
Spring IOC 容器源码分析
Spring系列三:IoC 与 DI
在软件工程中,控制反转(IoC)是一种设计思想,对象之间耦合在一起,在运行时自动绑定,并且它们编译时对所需要引用的对象是不确定的。在这个spring教程中,通过示例了解ioc和spring中的依赖注入之间的区别。
java干货
2021/02/19
6620
Spring系列三:IoC 与 DI
Spring 学习笔记 - 核心容器
基础框架:Spring Framework ——是其他项目的根基
知识分子没文化
2023/07/01
1920
Spring 学习笔记 - 核心容器
Spring框架参考手册_5.0.0_中英文对照版_Part II_3.4
A typical enterprise application does not consist of a single object (or bean in the Spring parlance). Even the simplest application has a few objects that work together to present what the end-user sees as a coherent application. This next section explains how you go from defining a number of bean definitions that stand alone to a fully realized application where objects collaborate to achieve a goal.
Tyan
2022/05/09
5760
Spring-IOC实现【01-XML配置方式】
IOC概念 IoC控制反转(IoC,Inversion of Control), 是一个概念,是一种思想。控制反转就 是对对象控制权的转移,从程序代码本身反转到了外部容器。把对象的创建、初始化、 销毁等工作交给spring容器来做。由spring容器控制对象的生命周期。 DI依赖注入:Dependency Injection。 依赖注入DI是指程序运行过程中,若需要调用另 一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创 建后传递给程序。 依赖注入是目前最优秀的解耦方
用户4919348
2019/04/02
6010
Spring-IOC实现【01-XML配置方式】
Spring入门
使用setter注入时,需要给每一个依赖项设置setter方法,配置文件中的 <property> 坐标中name属性的值实为setter方法的变量名,如:setter方法为 public void setBookDao(),则name属性的值为bookDao。ref属性的值为一个bean,所以需要注入的bean首先要配置bean。
Cikian.
2023/08/09
1690
Spring入门
聊一聊 Spring 6 容器 IOC
IoC 是 Inversion of Control 的简写,译为“控制反转”,它不是一门技术,而是一种设计思想,是一个重要的面向对象编程法则,能够指导我们如何设计出松耦合、更优良的程序。
小熊学Java
2023/09/06
9900
聊一聊 Spring 6 容器 IOC
Java匹马行天下之J2EE框架开发——Spring—>用IDEA开发Spring程序(01)
*注:在IDEA中我创建的Maven项目,不了解Maven的朋友可以看我之前的博客“我们一起走进Maven——知己知彼”,了解Maven后可以看我之前的博客“Maven的安装与配置”,自行安装,行动起来吧。
泰斗贤若如
2019/06/19
8050
day33_Spring学习笔记_01
4 + 1:4个核心jar包(beans、core、context、expression)+ 1个依赖jar包(com.springsource.org.apache.commons.logging-1.1.1.jar)
黑泽君
2018/10/11
4580
day33_Spring学习笔记_01
Spring的核心之IoC容器创建对象
别先生
2018/01/02
7110
Spring之IOC容器
spring core提供了IOC,DI,Bean配置装载创建的核心实现 核心概念: Beans、BeanFactory、BeanDefinitions、ApplicationContext
冬天vs不冷
2025/01/21
2400
Spring之IOC容器
Spring IoC容器与Bean管理
从本节开始,我将要学习作为java高级阶段最最最最重要的一个框架体系,名为Spring。Spring是整个Java生态中最重要的一环。因为我也是初学,所以我的概括也不一定全面和精炼。写这一章只是为自己以后复习。
害恶细君
2022/11/22
7380
Spring IoC容器与Bean管理
Spring5参考指南:依赖注入
依赖注入就是在Spring创建Bean的时候,去实例化该Bean构造函数所需的参数,或者通过Setter方法去设置该Bean的属性。
子润先生
2021/06/21
5280
Spring5参考指南:Bean的创建
Bean在Spring中就是一个业务组件,我们通过创建各种Bean来完成最终的业务逻辑功能。
程序那些事
2020/07/07
5720
Spring 基于 XML 的 IOC
  依赖注入(Dependency Injection)是 Martin Fowler 在 2004 年提出的关于 “控制反转” 的解释。Martin Fowler 认为 “控制反转” 一词让人产生疑惑,无法直白地理解到底哪方面的控制被反转了。所以 Martin Fowler 建议采用 “依赖注入” 一词来代替 “控制反转”。“依赖注入” 和 “控制反转” 其实就是一个事物的两种不同的说法而已,本质上是一回事。“依赖注入” 是一个程序设计模式和架构模型,有些时候也称为 “控制反转”。尽管在技术上来讲,“依赖注入” 是一个 “控制反转” 的特殊实现,但 “依赖注入” 还指一个对象应用另外一个对象来提供一个特殊的能力。例如,把一个数据库连接以参数的形式传到一个对象的结构方法里,而不是在那个对象内部自行创建一个连接。“依赖注入” 和 “控制反转” 的基本思想就是把类的依赖从类内部转到外部以减少依赖。利用 “控制反转”,对象在被创建时,会由一个调控系统统一进行对象实例的管理,将该对象所依赖对象的引用通过调控系统传递给它。也可以说,依赖被注入对象中。所以 “控制反转” 是关于一个对象如何获取它所依赖对象的引用的过程,而这个过程体现为谁来传递依赖的引用这个职责的反转。控制反转一般分为依赖注入(Dependency Injection,DI)和依赖查找(Dependency Lookup)两种实现类型。其中依赖注入应用比较广泛,Spring 就是采用依赖注入这种方式来实现控制反转的。
Demo_Null
2020/09/28
3810
Spring 基于 XML 的 IOC
相关推荐
第1章: Spring 基础第1章:Spring 基础
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验