理解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 ...
随机推荐
- Andrew Ng - 深度学习工程师 - Part 1. 神经网络和深度学习(Week 4. 深层神经网络)
=================第2周 神经网络基础=============== ===4.1 深层神经网络=== Although for any given problem it migh ...
- NodeJs通过HTTP模块发起GET|POST请求
[本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] Node.js ...
- 八张图彻底了解JDK8 GC调优秘籍-附PDF下载
目录 简介 分代垃圾回收器的内存结构 JDK8中可用的GC 打印GC信息 内存调整参数 Thread配置 通用GC参数 CMS GC G1参数 总结 简介 JVM的参数有很多很多,根据我的统计JDK8 ...
- Java并发编程:Callable、Future和FutureTask 实现龟兔赛跑
1.不清楚的看博客http://www.cnblogs.com/dolphin0520/p/3949310.html 我们使用上面的代码来实现一个龟兔赛跑 package com.weiyuan.te ...
- Oracle 导入数据库dmp文件
场景:windows2008 R2系统 ,往新安装的oracle11g数据库导入同事给的dmp文件到指定的新建的用户. 1.创建表空间 在导入dmp文件之前,先打开查看dmp文件的表空间名称(tabl ...
- 入门大数据---SparkSQL外部数据源
一.简介 1.1 多数据源支持 Spark 支持以下六个核心数据源,同时 Spark 社区还提供了多达上百种数据源的读取方式,能够满足绝大部分使用场景. CSV JSON Parquet ORC JD ...
- mybatis源码配置文件解析之五:解析mappers标签
在上篇博客中分析了plugins标签,<mybatis源码配置文件解析之四:解析plugins标签>,了解了其使用方式及背后的原理.现在来分析<mappers>标签. 一.概述 ...
- SpringBoot-多数据源配置-Mysql-SqlServer-Oracle
Maven依赖 <!-- mysql的jdbc依赖 --> <dependency> <groupId>mysql</groupId> <arti ...
- Python3笔记003 - 1.3 python开发工具
第1章 认识python 1.3 python开发工具 IDLE(python自带的python shell) Pycharm(python开发的,选择专业版) 1.进入IDLE模式: C:\Prog ...
- 【Python3爬虫】破解时光网登录加密参数并实现模拟登录
一.站点分析 MTime 时光网是一个电影媒体与电商服务平台,而这次做的模拟登录则是依靠其手机端站点,站点地址为:https://m.mtime.cn/#.切换到登录页面,再分别输入账号和错误的密码, ...