- java垃圾回收机制帮助我们实现了这个过程;
- 而ioc则是让我们无需考虑对象的创建过程,由ioc容器帮我们实现对象的创建、注入等过程。
【说明】:
Context
- 对于IoC和DI的理解
- BeanFactory
- ApplicationContext
Bean
- SpEL(#)
- 引入外部的属性文件($)
基于XML的Bean配置
配置Bean
- 通过全类名
- 通过工厂方法
- 通过 FactoryBean
装配Bean的属性
- 通过属性
- 通过构造器
基于注解的Bean配置
装配Bean的属性
- Autowired等注解
配置Bean
- Context:componentScan节点
一、控制反转(Spring IoC)
图片描述
【说明】:
IoC 概念
- 应用控制反转,对象在创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用,传递给它。也可以说,依赖注入到对象中。所以,控制反转是,关于一个对象如何获取他所依赖的对象的引用,这个责任的反转。
- 控制反转(Inversion of Controll)是一个重要的面向对象编程的法则,来削弱计算机程序的耦合问题,也是轻量级 Spring 框架的核心。
- 控制反转一般分为两种类型,依赖注入(Dependency Injection,DI)和依赖查找(Dependency Lookup),依赖注入应用广泛。
深入分析
- 依赖于谁?应用程序依赖于 IoC 容器
- 为什么要依赖?应用程序需要 IoC 容器来提供对象需要的外部资源
- 谁注入谁?IoC容器注入应用程序某个对象,应用程序依赖的对象
- 注入了社么?注入某个对象所需要的外部资源(包括对象、资源、常量对象)
与new对象的区别
- 正转与反转,传统应用程序是由我们将自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮助创建及注入依赖对象。
IoC优缺点
- 优点:实现组件之间的解耦,提供程序的灵活性和可维护性
- 缺点:对象生成因为是使用反射编程,在效率上有损耗
二、Spring IoC 容器
【Spring核心组件】:它们构建起了整个Spring的骨骼架构,没有它们就不可能有AOP、Web等特性功能。
- Core
- Context
- Bean
Spring解决了一个非常关键的问题,它可以让你对对象之间的关系转而用配置文件来管理,或者注解,也就是它的依赖注入机制。而这个注入关系在一个叫Ioc的容器中管理。Ioc容器就是被Bean包裹的对象。Spring正是通过把对象包装在Bean中从而达到管理这些对象及做一些列额外操作的目的。
1、IoC 容器
- 概念:
在每个框架中都有一个容器的概念,所谓的容器就是将常用的服务封装起来,然后,用户只需要遵循一定的规则,就可以达到统一、灵活、安全、方便、快速的目的
具有依赖注入功能的容器,负责实例化、定位、配置应用程序中的对象及建立这些对象间的依赖
- Bean的概念:
由IoC容器管理的那些组成应用程序的对象就叫Bean
Bean就是由Spring容器初始化、装配及管理的对象,除此之外,Bean就与应用程序中的其他对象没什么区别了
- 元数据 BeanDefinition
确定如何实例化Bean、管理Bean之间的依赖关系及管理Bean,就需要配置元数据,在Spring中由BeanDefintion代表
如何工作(以XML配置方式解释)
- 准备配置文件:配置文件中声明 Bean 定义也就是 Bean 配置元数据。
- 由 IoC 容器进行解析元数据:IoC 容器的 Bean Reader 读取并解析配置文件,根据定义生成 BeanDefintion 配置元数据对象,IoC 容器根据 BeanDefintion 进行实例化、配置及组装 Bean。
- 实例化 IoC 容器:由客户端实例化容器,获取需要的 Bean。
- hello world
@Test
public void testHelloWorld() {
// 1、读取配置文件实例化一个 IoC 容器
ApplicationContext context = new ClassPathXmlApplicationContext("helloWorld.xml");
// 2、从容器获取 Bean,主次此处完全“面向接口编程”
HelloApi helloApi = context.getBean("hello", HelloApi.class);
// 3、执行业务逻辑
helloApi.sayHello();
}
2、Spring IoC 初始化
3、Spring IoC 容器工作原理
三、核心组件协同工作
- Bean : 【演员】Spring 的 bean 实例
- Context :【导演】Spring 的上下文
Bean 包装的是 Object,而 Object 必然有数据,如何给这些数据体提供生存环境就是 Context 要解决的问题,对 Context 来说就是要发现每个 Bean 之间的关系,为它们建立这种关系并维护关系,所以 Context 就是 Bean 关系的集合,这个关系机构叫做 IoC 容器。
- Core :【道具】Spring 的核心工具包
建立和维护每个 Bean 之间的关系所需要的一些核心工具包。相当于 Util。
四、BeanFactory 和 ApplicationContext 的区别
IoC 中最核心的接口是 BeanFactory 提供 IoC 的高级服务,而 ApplicationContext 是建立在 BeanFactory 基础之上提供抽象的面向应用的服务。
- BeanFactory
提供了一种先进的配置机制来管理任何种类 Bean 对象,这种配置机制考虑到任何一种可能的存储方式。
- ApplicationContext
建立在 BeanFactory,具有 BeanFactory 的所有功能和行为
- MessageSource,提供国际化的消息服务,扩展类 MessageResource 接口
- 资源访问,如 URL 和文件
- 事件传播
- 载入多个(有继承关系)上下文,使得每一个上下文都专注于一个特定的层次,比如应用的 web 层。
不同点
选用哪个?
- BeanFactory:是延迟加载,如果 Bean 的某一个属性没有注入,BeanFactory 加载后,直至第一次使用调用 getBean 方法才会抛出异常
- ApplicationContext:则在初始化自身时检验,这样有利于检查所依赖属性是否注入;所以通常情况下使用 ApplicationContext。
特性表 BeanFatory / ApplicationContext
* Bean 实例化/装配 YES YES * 启动 BeanPostProcessor注册 NO YES * 启动 BeanFactoryPostProcessor注册 NO YES * 便捷的 MessageSource反问(i18n) NO YES * ApplicationEvent 发送 NO YES
关系
* BeanFactory 提供了配置框架和基本的功能 * ApplicationContext 建立在 BeanFactory 之上,并增加了其他功能 * 一般来说,ApplicationContext 是 BeanFactory 的完全超集,任何 BeanFactory 功能和行为的描述也同样被认为适用于 ApplicationContext。
五、三种注入方式
在Spring框架中,依赖注入(DI)的设计模式是用来定义对象彼此间的依赖。使用xml配置bean的情况下,它主要有两种类型:
- Setter 方法注入
- 构造器注入
- 注解 @autowired
当然,有了注解之后,使用注解的方式更加方便快捷。即自动装配功能实现属性自动注入(@autowire)。
六、原理解析
1、Contxt 的初始化过程
当运行 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
构造方法 ClassPathXmlApplicationContext(String configLocation), 调用了 this(new String[] {configLocation}, true, null); 该构造方法具体代码:
【ClassPathXmlApplicationContext构造方法】:
/**
* 在 parent 下创建 ClassPathXmlApplicationContext,
* 从 XML 中读取素有 Bean 定义。
* @param configLocations 配置文件路径如 c:\simpleContext.xml
* @param refresh 是否需要自动刷新 context,refrest--> 重新加载
* 加载所有的 bean 定义,创建所有单例。
* refresh 为 true 的时候,根据 context 来手工刷新
* @param parent the parent context
* @throws BeansException if context creation failed
* @see #refresh()
*/
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException{
// 初始化XmlApplicationContext
super(parent);
// 转换配置文件的路径
setConfigLocations(configLocations);
if(refresh) {
// 重新刷新所有的 context,这一篇的重点
refresh();
}
}
【refresh方法】:
/**
* 加载或刷新持久的配置,可能是xml文件,properties文件,或者关系型数据库的概要。
* 作为一启动方法,如果初始化失败将会销毁已经创建好的单例,避免重复加载配置文件。
* 换句话说,在执行这个方法之后,要不全部加载单例,要不都不加载。
public void refresh() throws BeansException, IllegalStateException {
synchronized(this.startupShutdownMonitor) {
// 初始化配置准备刷新,验证环境中的一些必选参数
prepareRefresh();
// 告诉继承销毁内部的 factory创建新的 factory 实例
// 初始化 Bean 实例
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 初始化 beanFactory 的基本信息,包括 classLoader、environment、忽略的注解等。
prepareBeanFactory(beanFactory);
try {
// beanFactory 内部的 postProces,可以理解为 context 中的 PostProcess 的补充
beanFactory.postProcessBeanFactory(beanFactory)p;
// 执行 BeanFactoryPostProcessor(在beanFactory)初始化过程中,bean 初始化之前,修改BeanFactory 参数
// BeanDefinitionRegistryPostProcessor 其实也是继承自 BeanFactoryPostProcessor,
// 多个对 BeanDefinitionRegistry 的支持 invokeBeanFactoryPostProcessors(beanFactory)
// 执行 postProcess,那 BeanPoseProcessor 是什么呢,是为了在 bean 加载过程中修改 bean 的内容,
// 使用Before、After分别对应初始化前和初始化后
registerBeanPostProcessors(beanFactory);
// 初始化 MessageSource,主要用于 I18N 本地化的内容
initMessageSource();
// 初始化事件广播 ApplicationEventMulticaster,使用观察者模式,对注册的 ApplicationEvent 时间进行捕捉
initApplicationEventMulticaster();
// 初始化特殊的 bean 方法
onRefresh();
// 将所有 ApplicationEventListener 注册到 ApplicationEventMulticaster 中
registerListeners();
// 初始化所有部位 lazy-init 的bean,singleton 实例
finishBeanFactoryInitialization(beanFactory);
// 初始化 lifeCycle 的bean启动(例如 quartz 的定时器),如果开启 JMX 则将 ApplicationContext 注册到上面
finishRefresh();
}
catch(BeansException ex) {
// 销毁已经创建单例
resources.destroyBeans();
// 将 context 的状态转换为无效,标示初始化失败
flag.cancelRefresh(ex);
}
}
}
2、从时序图看上述启动初始化
【容器初始化时序图】: