Spring 是一个轻量级的企业级应用开发框架,它于2004年发布第一个版本,其目的是用于简化企业级应用程序的开发。
在传统应用程序开发中,一个完整的应用是由一组相互协作的对象组成,开发一个应用除了要开发业务逻辑之外,更多的是关注如何使这些对象协作来完成所需功能,而且要高内聚,低耦合。虽然一些设计模式可以帮我们达到这个目的,可是这又徒增了我们的负担。如果能通过配置的方式来创建对象,管理对象之间依赖关系,那么就能够减少许多工作,提高开发效率。Spring 框架主要就是来完成这个功能的。
Spring 框架除了帮我们管理对象及其依赖关系之外,还提供了面向切面编程的能力,在此基础上,允许我们对一些通用任务如日志记录、安全控制、异常处理等进行集中式管理,还能帮我们管理最头疼的数据库事务。此外,它还提供了粘合其他框架的能力,使我们可以方便地与各种第三方框架进行集成,而且不管是 Java SE 应用程序还是 JavaEE 应用程序都可以使用这个平台进行开发。
Spring 是基于 IoC 和 AOP 两大思想理论衍生而来的,可以说,Spring是一个同时实现了 IoC 和 AOP 的框架。
Spring 的整体架构如图所示:
核心模块只有3个:Beans、Core、和 Context ,它们构建起了整个 Spring 的骨架,没有它们就不可能有 AOP、Web 等上层的特性功能。如果在它们3个中选出一个最核心的模块的话,那就非 Beans 模块莫属了,其实 Spring 就是面向 Bean 的编程(BOP, Bean Oriented Programming),Bean 在 Spring 中才是真正的主角。关于 Bean 的观念,会在后面进行介绍。
IoC(Inverse Of Control,控制反转)是一种设计思想,目标是实现解耦。所谓控制反转,是指对资源的控制方式反转了。这里说的资源主要指我们的业务对象,对象之间往往会存在某种依赖关系,当一个对象依赖另一个对象时,传统的做法是在它内部直接 new 一个出来,即由对象自己负责创建并管理它所依赖的资源,这是传统的对资源的控制方式。IoC 就是将其颠倒过来,对象由主动控制所需资源变成了被动接受,由第三方(IoC 容器)对资源进行集中管理,对象需要什么资源就从IoC容器中取,或者让容器主动将所需资源注入进来。
IoC 之后,对象与所需资源之间不再具有强耦合性,资源可以被直接替换,而无需改动需求方的代码。举个例子,董事长需要一个秘书,传统的做法是,董事长自己去指定一个秘书,控制权在他自己手上,但是这会导致他与秘书之间的耦合性较强,一旦想换秘书了,就得修改自己的代码。IoC 的做法是,董事长声明自己需要一个秘书,由IoC 容器为他指定一个秘书,至于是哪个秘书,男的还是女的,一切由容器说了算,如果要换秘书,也是修改容器的配置文件,与董事长无关,这样就实现了两者间的解耦。
IoC 的两种实现方式:
Java Bean 的概念不同于 Bean,Java Bean 是指符合 JavaBeans 规范的一种特殊的 Bean,即:所有属性均为 private,提供 getter 和 setter,提供默认构造方法。JavaBean 也可以认为是遵循特定约定的POJO。
POJO(Plain Ordinary Java Object)是指简单和普通的 Java 对象。严格来说,它不继承类,不实现接口,不处理业务逻辑,仅用于封装数据。
首先配置 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 容器: BeanFactory 和 ApplicationContext 。
两者在核心功能上的区别主要是默认的加载策略不同,这点区别几乎可以忽略不计,通常情况下,我们总是使用更为强大的 ApplicationContext,很少会直接使用 BeanFactory。
以下是几个最常用的 ApplicationContext 实现类:
既然是容器,那它最底层肯定是一个数据结构。通过跟踪 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 定义信息的加载。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 来保管它们。个人理解,注册与装配是不同的两个过程。注册指的是将 Bean 纳入 IoC 容器。装配指的是建立 Bean 之间的依赖关系。
Bean 的注册方式有以下三种:
@ComponentScan、@Component等注解进行配置Bean 的装配分为手动装配和自动装配。
手动装配同样有三种方式:
@Resource 等注解来配置。
自动装配也称自动注入,有两种开启方式:
<bean>标签中配置default-autowire属性,或在@Bean注解中配置autowire属性。开启了默认自动装配的 Bean,Spring 会对它的全部属性都尝试注入值,这种方式不安全,因此很少使用。@Autowired等注解对单个属性开启自动装配。Spring 支持以下四种用于自动装配的注解:
@Autowired 注解@Inject 注解@Resource 注解@Value 注解,用于装配 String 和基本类型的值。@Value注解经常配合 SpEL 表达式一起使用。SpEL 表达式的主要语法:
${},表示从 Properties 文件中读取相应属性的值。通常需要同@PropertySource注解配合使用,该注解用于指定从哪个 Properties 文件中读取属性。#{},表示从 Spring Bean 中读取相应属性的值。如,#{user1.name}表示从名称为 user1 的 Bean 中 读取 name 属性值。:用于指定默认值,如${server.port:80}。未完待续
原文:https://www.cnblogs.com/yonghengzh/p/13311192.html