理解Spring(一):Spring 与 IoC
什么是 Spring
Spring 是一个轻量级的企业级软件开发框架,它于2004年发布第一个版本,其目的是用于简化企业级应用程序的开发。
在传统应用程序开发中,一个完整的应用是由一组相互协作的对象组成,开发一个应用除了要开发业务逻辑之外,更多的是关注如何使这些对象协作来完成所需功能,而且要高内聚,低耦合。虽然一些设计模式可以帮我们达到这个目的,可是这又徒增了我们的负担。如果能通过配置的方式来创建对象,管理对象之间依赖关系,那么就能够减少许多工作,提高开发效率。Spring 框架主要就是来完成这个功能的。
Spring 框架除了帮我们管理对象及其依赖关系之外,还提供了面向切面编程的能力,在此基础上,允许我们对一些通用任务如日志记录、安全控制、异常处理等进行集中式管理,还能帮我们管理最头疼的数据库事务。此外,它还提供了粘合其他框架的能力,使我们可以方便地与各种第三方框架进行集成,而且不管是 Java SE 应用程序还是JavaEE应用程序都可以使用这个平台进行开发。
Spring 是基于 IoC 和 AOP 两大思想理论衍生而来的,可以说,Spring是一个同时实现了 IoC 和 AOP 的框架。
Spring 的整体架构
Spring 的整体架构如图所示:

核心模块只有3个:Beans、Core、和 Context ,它们构建起了整个 Spring 的骨架,没有它们就不可能有 AOP、Web 等上层的特性功能。如果在它们3个中选出一个最核心的模块的话,那就非 Beans 模块莫属了,其实 Spring 就是面向 Bean 的编程(BOP, Bean Oriented Programming),Bean 在 Spring 中才是真正的主角。关于 Bean 的观念,会在后面进行介绍。
什么是 IoC
IoC(Inverse Of Control,控制反转)是一种设计思想,目标是实现解耦。所谓控制反转,是指对资源的控制方式反转了。这里说的资源主要指我们的业务对象,对象之间往往会存在某种依赖关系,当一个对象依赖另一个对象时,传统的做法是在它内部直接 new 一个出来,即由对象自己负责创建并管理它所依赖的资源,这是传统的对资源的控制方式。IoC 就是将其颠倒过来,对象由主动控制所需资源变成了被动接受,由第三方(IoC 容器)对资源进行集中管理,对象需要什么资源就从IoC容器中取,或者让容器主动将所需资源注入进来。
IoC 之后,对象与所需资源之间不再具有强耦合性,资源可以被直接替换,而无需改动需求方的代码。举个例子,董事长需要一个秘书,传统的做法是,董事长自己去指定一个秘书,控制权在他自己手上,但是这会导致他与秘书之间的耦合性较强,一旦想换秘书了,就得修改自己的代码。IoC 的做法是,董事长声明自己需要一个秘书,由IoC 容器为他指定一个秘书,至于是哪个秘书,男的还是女的,一切由容器说了算,如果要换秘书,也是修改容器的配置文件,与董事长无关,这样就实现了两者间的解耦。
IoC 的两种实现方式:
- DI(Dependency Injection,依赖注入)。所谓依赖注入,是指对象所依赖的资源通过被动注入的方式得到,换言之,容器会主动地根据预先配置的依赖关系将资源注入进来。
- DL(Dependency Lookup,依赖查找)。依赖查找是早先的一种 IoC 实现方式,现已过时。对象需要调用容器的API查找它所依赖的资源。
Bean 的概念
- 在 Java 中,“Bean”是对用Java语言编写的“可重用组件”的惯用叫法。可以从字面意思去理解,Java 原本指爪哇咖啡,bean 指咖啡豆,而咖啡的“可重用组件”就是咖啡豆嘛。官方并没有说明所谓的“组件”具体指的是什么,因为“组件”本身就是一个抽象的概念,是对软件组成部分的抽象,因此,Bean作为可重用组件的代称,既可指类,也可指对象。
- 在 Spring 中,Bean 的概念同上,有时也称 Component。 由 Spring 的 IoC容器所管理的 Bean 称作 Spring Bean。
- 扩展:
Java Bean 的概念不同于 Bean,Java Bean 是指符合 JavaBeans 规范的一种特殊的 Bean,即:所有属性均为 private,提供 getter 和 setter,提供默认构造方法。JavaBean 也可以认为是遵循特定约定的POJO。
POJO(Plain Ordinary Java Object)是指简单和普通的 Java 对象。严格来说,它不继承类,不实现接口,不处理业务逻辑,仅用于封装数据。
Spring 的基本使用
首先配置 Bean 信息,向 Spring 的 IoC 容器(或简称 Spring 容器)中注册 Bean。以 XML 方式为例,如下配置了两个 Bean,其中第一个依赖第二个:
<bean id="John" class="Person">
<property name="lover">
<ref bean="Mary"/>
</property>
</bean>
<bean id="Mary" class="Person"/>
然后创建 Spring 容器,同时绑定配置文件。如下:
ApplicationContext container = new ClassPathXmlApplicationContext("bean-config.xml");
然后通过容器的 getBean 方法即可得到我们在配置文件中所配置的 Bean 的实例。如下:
Person John = container.getBean("John");
Spring 的两种 IoC 容器
Spring 提供了两种 IoC 容器: BeanFactory 和 ApplicationContext 。
- BeanFactory 提供基本的 IoC 服务支持。
- ApplicationContext 对 BeanFactory 进行了扩展与增强,除了拥有 BeanFactory 的所有能力外,还提供了许多高级特性,如事件发布、资源加载、国际化消息等。ApplicationContext 接口继承了 BeanFactory 接口,它的实现类中也是直接复用了 BeanFactory,因此可以说,ApplicationContext 是 BeanFactory 的增强版。
两者在核心功能上的区别主要是默认的加载策略不同,这点区别几乎可以忽略不计,通常情况下,我们总是使用更为强大的 ApplicationContext,很少会直接使用 BeanFactory。
以下是几个最常用的 ApplicationContext 实现类:
- ClassPathXmlApplicationContext
- AnnotationConfigApplicationContext
- AnnotationConfigWebApplicationContext
Spring 容器的基本工作原理
既然是容器,那它最底层肯定是一个数据结构。通过跟踪 getBean 方法,我们发现它是从一个叫作 singletonObjects 的 Map 集合中获取 Bean 实例的。singletonObjects 的定义如下:

可以断定,它就是 Spring 容器的核心,凡是作用域为单例的 Bean 的实例都保存在该 Map 集合中,我把它称之为单例池。
那么 getBean 方法做了哪些事情呢?
getBean 方法首先会从单例池中获取 Bean 实例,如果取到了就直接返回,否则,如果有父容器,尝试从父容器中获取,如果也没获取到,则创建实例。创建实例之前先确保该 Bean 所依赖的 Bean 全部初始化,然后,如果是原型 Bean,创建好实例后直接返回,如果是单例 Bean,创建好实例后将其放进单例池,然后再从单例池中获取并返回。
当 Spring 容器被创建时,它又是如何完成初始化的呢?
以 ClassPathXmlApplicationContext为例,它的构造方法主要做的事情就是调用 refresh() 方法。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 准备好自己
prepareRefresh();
// 创建并初始化BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备好要使用的BeanFactory
prepareBeanFactory(beanFactory);
try {
// 对BeanFactory进行后置处理
postProcessBeanFactory(beanFactory);
// 调用BeanFactory的后置处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册Bean的后置处理器
registerBeanPostProcessors(beanFactory);
// 初始化消息源
initMessageSource();
// 初始化事件多播器
initApplicationEventMulticaster();
// 初始化其他特殊的bean
onRefresh();
// 检测并注册监听器Bean
registerListeners();
// 实例化其余所有(非懒加载)的单例Bean
finishBeanFactoryInitialization(beanFactory);
// 最后一步:发布相应的事件
finishRefresh();
refresh() 方法的主要执行流程:
- 调用
refreshBeanFactory()方法,该方法会首先新建或重建 BeanFactory 对象,然后使用相应的BeanDefinitionReader读取并解析 Bean 定义信息,将每个 Bean 定义信息依次封装成BeanDefinition对象,并将这些BeanDefinition对象注册到BeanDefinitionRegistery。这一步,完成了 BeanFactory 的创建,以及 Bean 定义信息的加载。 - 配置 BeanFactory,对 BeanFactory 做一些后置处理,注册 Bean 的后置处理器,初始化消息源和事件多播器,注册监听器等。这一步,完成了 Spring 容器的配置工作。
- 调用
finishBeanFactoryInitialization()方法,该方法会遍历之前注册到BeanDefinitionRegistery中的所有BeanDefinition,依次实例化那些非抽象非懒加载的单例 Bean,并将其加入单例池。这一步,完成了 Bean 的实例化。
Spring 容器的几个核心类:
DefaultListableBeanFactory是一个通用的BeanFactory实现类,它还同时实现了BeanDefinitionRegistry接口。从 ApplicationContext 实现类的源码中可以看到,在它内部维护着一个DefaultListableBeanFactory的实例,所有的 IoC 服务都委托给该 BeanFactory 实例来执行。BeanDefinitionRegistry负责维护BeanDefinition实例,该接口主要定义了registerBeanDefinition()、getBeanDefinition()等方法,用于注册和获取 Bean 信息。BeanDefinition用于封装 Bean 的信息,包括类名、是否是单例Bean、构造方法参数等信息。每一个 Spring Bean 都会有一个BeanDefinition的实例与之相对应。BeanDefinitionReader负责读取相应配置文件中的内容并将其映射到BeanDefinition,然后将映射后的BeanDefinition实例注册到BeanDefinitionRegistry,由BeanDefinitionRegistry来保管它们。
Spring Bean 的注册与装配
个人理解,注册与装配是不同的两个过程。注册指的是将 Bean 纳入 IoC 容器。装配指的是建立 Bean 之间的依赖关系。
Bean 的注册方式有以下三种:
- 在 XML文件中配置
- 在 JavaConfig 中配置
- 使用
@ComponentScan、@Component等注解进行配置
Bean 的装配分为手动装配和自动装配。
手动装配同样有三种方式:
- 在 XML文件 中配置
- 在 JavaConfig 中配置
- 使用
@Resource等注解来配置。- 这种方式既可以算作手动装配,也可以算作自动装配。当我们在 @Resource 注解中明确指定注入哪一个 Bean 时,我们称这是手动装配,而当我们不进行指定时,则算作自动装配。
自动装配也称自动注入,有两种开启方式:
- 开启粗粒度的自动装配,即开启 Bean 的默认自动装配。在
<bean>标签中配置default-autowire属性,或在@Bean注解中配置autowire属性。开启了默认自动装配的 Bean,Spring 会对它的全部属性都尝试注入值,这种方式不安全,因此很少使用。 - 开启细粒度的自动装配,即在组件类中使用
@Autowired等注解对单个属性开启自动装配。
Spring 支持以下四种用于自动装配的注解:
- Spring 自带的
@Autowired注解 - JSR-330 的
@Inject注解 - JSR-250 的
@Resource注解 - Spring 新增的
@Value注解,用于装配 String 和基本类型的值。@Value注解经常配合 SpEL 表达式一起使用。
SpEL 表达式的主要语法:
${},表示从 Properties 文件中读取相应属性的值。通常需要同@PropertySource注解配合使用,该注解用于指定从哪个 Properties 文件中读取属性。#{},表示从 Spring Bean 中读取相应属性的值。如,#{user1.name}表示从名称为 user1 的 Bean 中 读取 name 属性值。- 冒号
:用于指定默认值,如${server.port:80}。
Spring Bean 的作用域与生命周期
未完待续
理解Spring(一):Spring 与 IoC的更多相关文章
- spring常用注解以IOC理解
使用注解来构造IoC容器 用注解来向Spring容器注册Bean.需要在applicationContext.xml中注册<context:component-scan base-package ...
- Spring(一):Spring入门程序和IoC初步理解
本文是按照狂神说的教学视频学习的笔记,强力推荐,教学深入浅出一遍就懂!b站搜索狂神说或点击下面链接 https://space.bilibili.com/95256449?spm_id_from=33 ...
- [Spring框架]Spring IOC的原理及详解。
这里感谢 CSDN 的原博客:http://blog.csdn.net/m13666368773/article/details/7802126 看后 受益匪浅,这里再重温一遍Spring IOC ...
- spring源码浅析——IOC
=========================================== 原文链接: spring源码浅析--IOC 转载请注明出处! ======================= ...
- Spring MVC -- Spring框架入门(IoC和DI)
Spring MVC是Spring框架中用于Web应用开发的一个模块.Spring MVC的MVC是Model-View-Controller的缩写.它是一个广泛应用于图像化用户交互开发中的设计模式, ...
- 【转】Spring学习---Spring IoC容器的核心原理
[原文] Spring的两个核心概念:IoC和AOP的雏形,Spring的历史变迁和如今的生态帝国. IoC和DI的基本概念 IoC(控制反转,英文含义:Inverse of Control)是Spr ...
- spring源码解读-ioc
本系列博客结合我的理解,对spring的ioc进行简单分析,欢迎大家批评指正. beanfactory 我们从beanfactory开始,beanfactory是最根部的容器,描述了整个ioc的一些规 ...
- Spring升级案例之IOC介绍和依赖注入
Spring升级案例之IOC介绍和依赖注入 一.IOC的概念和作用 1.什么是IOC 控制反转(Inversion of Control, IoC)是一种设计思想,在Java中就是将设计好的对象交给容 ...
- Spring(三) Spring IOC
Spring 核心之 IOC 容器 再谈 IOC 与 DI IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建.依赖的代码,反转给容器 ...
- 【Spring】重新认识 IoC
前言 IoC (Inversion of control) 并不是Spring特有的概念. IoC 维基百科的解释: In software engineering, inversion of con ...
随机推荐
- 大众点评cat实时监控简介及部署
简介 背景 CAT(Central Application Tracking)是由吴其敏(前大众点评首席架构师,现携程架构负责人)主导设计基于Java开发打造的实时应用监控平台,为大众点评网提供了全面 ...
- 【K8S】Service服务详解,看这一篇就够了!!
k8s用命名空间namespace把资源进行隔离,默认情况下,相同的命名空间里的服务可以相互通讯,反之进行隔离. 1.1 Service Kubernetes中一个应用服务会有一个或多个实例(Pod, ...
- Python Selenium百度搜索翻页查找文字
#!/usr/bin/python # -*- coding: utf- -*- from selenium import webdriver import time # browser = webd ...
- 使用Jmeter如何测试下载接口
性能测试过程中,有时候需要对下载类的功能做压测,有些同学没有这方面的测试经验,比较迷茫,本文简单介绍下如何测试下载类的请求1.首先使用fiddler抓包,知道是一个http类型的请求,有一个post请 ...
- JavaWeb网上图书商城完整项目--day02-3.regist页面输入框失去焦点进行校验
当输入框输入数据之后,当输入框失去焦点的时候,我们需要对输入的数据进行校验 l 用户名校验: 用户名不能为空: 用户名长度必须在3 ~ 20之间: 用户名已被注册(需要异步访问服务器). l 登录 ...
- 前后端分层架构MVC&MVVM
早期 特点 页面由 JSP.PHP 等工程师在服务端生成 JSP 里揉杂大量业务代码 浏览器负责展现,服务端给什么就展现什么,展现的控制在 Web Server 层 优点 简单明快,本地起一个 Tom ...
- Freemarker在replace替换是对NULL值的处理
freemarker的对象调用内建函数时,比如userInfo对象的birthDay函数,页面${userInfo.birthDay}调用,当我想将birthDay值中的“-”替换为“/”时,${us ...
- Spring IoC BeanDefinition 的加载和注册
前言 本系列全部基于 Spring 5.2.2.BUILD-SNAPSHOT 版本.因为 Spring 整个体系太过于庞大,所以只会进行关键部分的源码解析. 本篇文章主要介绍 Spring IoC 容 ...
- Python方法函数记录
目录 python 控制台输出的内容保存到txt 文件 eval函数使用 python 控制台输出的内容保存到txt 文件 import sys class Logger(object): def _ ...
- 入门大数据---Hbase容灾与备份
一.前言 本文主要介绍 Hbase 常用的三种简单的容灾备份方案,即CopyTable.Export/Import.Snapshot.分别介绍如下: 二.CopyTable 2.1 简介 CopyTa ...