IOC:Inversion of Control的缩写,控制反转,是面向对象编程中的一种设计思想
原始的对象创建和IOC对比:
-
传统的JavaSE程序设计,我们直接在对象内部通过new进行对象的创建,是程序主动去创建依赖对象(Java程序主动控制对象的创建[正转])
-
IOC则是通过一个专门的容器来创建这些对象,由IOC容器来控制对象的创建(IOC容器控制对象的创建)
-
容器首先会查找需要创建的对象
-
然后注入依赖对象,对象在整个过程中都是被动的接受依赖对象[反转]
作用:
-
-
把对象创建和对象之间的调用过程交给Spring的IOC容器进行管理
目的:
- 有容器进行注入组合对象,对象与对象之间达到了松耦合的目的
1. 依赖注入
依赖注入是IOC的实现,因此具体IOC如何实现控制反转,还得看依赖注入。
2. IOC底层原理
2.1 IOC技术核心
-
xml解析技术
-
工厂模式
-
反射
原始Bean创建→工厂模式创建Bean对象→IOC创建Bean对象
如上图所示,使用XML就是为了解耦合
例如:UserDao更换到其他包下,只需要更改XML文件即可,并不会影响代码
2.2 IOC体系结构
如上图所示,作为一个IOC容器的设计者,主体上应该包含哪几个部分:
-
加载Bean的配置(如xml配置、groovy配置)
- 考虑不同类型资源的加载,解析生成统一Bean的定义
-
根据Bean的定义加载生成Bean的实例,并放置在Bean容器中
- 考虑Bean的依赖注入、Bean的嵌套、Bean的存放等
-
除了基础Bean外,还有常规针对企业级业务的特别Bean
- 国际化Message,事件Event等生成特殊的类结构去支撑
-
对容器中的Bean提供统一的管理和调用
-
比如用工厂模式管理,提供方法根据名字/类的类型等从容器中获取Bean
-
获取资源、国际化等常规应用型的特殊的Bean
-
…等
BeanFactory和BeanRegistry:IOC容器功能规范和Bean的注册
Spring Bean的创建是典型的工厂模式,在Spring中有许多的IOC容器的实现供开发者使用,这是IOC的基础,在顶层的结构设计主要围绕着BeanFactory和xxxRegistry进行
-
BeanFactory:工厂模式定义了IOC容器的基本功能规范
-
BeanRegistry:向IOC容器手动注册BeanDefinition对象的方法
其相互关系如下:
BeanFactory作为最顶层的接口,它定义了IOC容器的基本规范,BeanFactory有三个子类:ListableBeanFactory、HierarchicalBeanFactory和AutowiredCapableBeanFactory。
public interface BeanFactory {
//用于取消引用实例并将其与FactoryBean创建的bean区分开来。例如,如果命名的bean是FactoryBean,则获取将返回Factory,而不是Factory返回的实例。
String FACTORY_BEAN_PREFIX = "&";
//根据bean的名字和Class类型等来得到bean实例
Object getBean(String name) throws BeansException;
Object getBean(String name, Class requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType, Object... args) throws BeansException;
//返回指定bean的Provider
<T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);
<T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);
//检查工厂中是否包含给定name的bean,或者外部注册的bean
boolean containsBean(String name);
//检查所给定name的bean是否为单例/原型
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//判断所给name的类型与type是否匹配
boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;
boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;
//获取给定name的bean的类型
@Nullable
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//返回给定name的bean的别名
String[] getAliases(String name);
}
BeanFactory定义了多层次的接口,主要包括:
-
ListableBeanFactory:该接口定义了访问容器中Bean基本信息的若干方法,如:查看Bean的个数、获取某一类型Bean的配置名、查看容器中是否包括某一Bean等方法
-
HierarchicalBeanFactory:父子级联IOC容器的接口,子容器可以通过接口方法访问父子容器;通过HierarchicalBeanFactory接口,Spring的IOC容器可以建立父子层级关联的容器体系,子容器可以访问父容器中的Bean,但是父容器不能访问子容器的Bean。Spring使用父子容器实现了很多功能,如:Spring MVC中展现层Bean位于一个子容器中,而业务层和持久层的Bean位于父容器中,这样展现层Bean就可以引用业务层和持久层的Bean,而业务层和持久层的Bean则看不到展现层的Bean
-
ConfigurableBeanFactory:是一个重要的接口,增强了IOC容器的可定制性,定义了设置类装载器、属性编辑器、容器初始化后置处理器等方法
-
ConfigurableListableBeanFactory:ListableBeanFactory和ConfigurableBeanFactory的组合
-
AutowireCapableBeanFactory:定义了将容器中的Bean按某种规则(byName/byType)进行自动装配的方法
如何将Bean注册到BeanFactory中
Spring配置文件中每个节点元素在Spring容器中都通过一个BeanDefinition对象表示,它描述了Bean的配置信息,而BeanDefinitionRegistory接口提供了向容器手动注册BeanDefinition对象的方法
BeanDefinition:各种Bean对象及其相互关系
Bean对象存在依赖嵌套等关系,因此设计出了BeanDefinition,用于对Bean对象及关系定义
-
BeanDefinition:定义了各种Bean对象及其相互关系
BeanDefinition的继承关系 -
BeanDefinitionReader:这是BeanDefinition的解析器
BeanDefinitionReader的继承关系 -
BeanDefinitionHolder:这是BeanDefinition的包装类,用于存储BeanDefinition,name以及aliases等
BeanDefinitionHolder的继承关系
ApplicationContext:IOC结构设计和实现
IOC容器的接口类是ApplicationContext,它继承了BeanFactory对Bean规范(最基本的IOC容器的实现)进行了定义。ApplicationContext表示应用的上下文
ApplicationContext除了包含Bean的管理,还包含如下:
-
访问资源:对不同方式的Bean配置进行加载。(实现ResourcePatternResolver接口)
-
国际化:支持信息源,可以实现国际化。(实现MessageSource接口)
-
应用事件:支持应用事件。(实现ApplicationEventPublisher接口)
ApplicationContext接口的实现需要考虑不同的Bean的配置方式(如:xml、groovy、annotation等)有着不同的资源加载方式,因此衍生了许多其他的ApplicationContext
ApplicationContext继承关系
-
从类结构设计上,围绕着是否需要Refresh容器衍生出的两个抽象类
-
GenericApplicationContext:是初始化的时候就创建容器,往后的每次refresh都不会更改
-
AbstractRefreshableApplicationContext:AbstractRefreshableApplicationContext及其子类的每次refresh都是先清除已有(没有就创建)的容器,然后再重新创建
AbstractRefreshableApplicationContext及其子类无法做到和GenericApplicationContext混合搭配,从不同源头获取bean的定义信息
-
-
从加载源上,衍生出众多的抽象类
-
FileSystemXmlApplicationContext:从文件系统下的一个或多个xml配置文件中加载上下文定义,也就是说系统盘符中加载xml配置文件。
-
ClassPathXmlApplicationContext:从类路径下的一个或多个xml配置文件中加载上下文定义,适用于Xml配置方式
-
AnnotationConfigApplicationContext:从一个或多个基于Java的配置类中加载上下文定义,适用于java注解方式
-
ConfigurableApplicationContext:扩展于ApplicationContext,它新增加了两个主要方法:refresh()和close(),让ApplicationContext具有启动、刷新和关闭应用上下文的功能,在应用上下文关闭的情况下调用refresh()即可启动应用上下文,在已经启动的状态下,调用refresh()则清除缓存并重新装载配置信息,而调用close()则可关闭应用上下文,这些接口方法为容器的控制管理带来了便利
-
为什么AnnotationConfigApplicationContext是继承GenericApplicationContext?
答:因为基于注解的配置,是不太会被运行时修改的,这意味着不需要进行动态Bean配置和刷新容器,所以只需要GenericApplicationContext
为什么ClassPathXmlApplicationContext是继承AbstractRefreshableApplicationContext
基于XML这种配置文件,是容易修改的,需要动态性刷新Bean的支持,所以XML相关配置必然继承AbstractRefreshableApplicationContext。并且存在多种xml的加载方式(位置不同的设计),所以必然会设计出AbstractXmlApplicationContext,其中包含了对XML配置解析成BeanDefinition的过程
为什么AnnotationWebConfigApplicationContext是继承AbstractRefreshableApplicationContext
因为用户可以通过ApplicationContextInitializer来设置contextInitializerClasses(context-param/init-param), 在这种情况下用户倾向于刷新Bean的,所以设计者选择让AnnotationWebConfigApplicationContext继承了AbstractRefreshableApplicationContext。
<p>As an alternative to setting the "contextConfigLocation" parameter, users may
implement an {@link org.springframework.context.ApplicationContextInitializer
ApplicationContextInitializer} and set the
{@linkplain ContextLoader#CONTEXT_INITIALIZER_CLASSES_PARAM "contextInitializerClasses"}
context-param / init-param. In such cases, users should favor the {@link #refresh()}
and {@link #scan(String...)} methods over the {@link #setConfigLocation(String)}
method, which is primarily for use by {@code ContextLoader}.
最终了解了底层设计思路后,我们可以得到如下图解:
2.3 IOC初始化流程
3. IOC接口
IOC容器的本质就是对象工厂,所谓容器,可以视为xml文件,每次塞入一个个的bean
Spring提供IOC容器实现的两种方式:
-
BeanFactory
IOC容器基本实现,是Spring内部的使用接口,不推荐开发人员使用
-
特点
加载配置文件时不会创建对象,而是在获取对象时才会创建对象,即
context.getBean("user", User.class);
-
-
ApplicationContext
BeanFactory接口的子接口,提供了更多更强大的功能,推荐开发人员使用
-
特点:
加载配置文件的时候就会在配置文件中进行对象的创建,目的是把创建对象这种耗费耗时耗资源的操作提前到项目启动的时候
解析XML文件的两个接口
FileSystemXmlApplicationContext:用于解析全路径或相对路径下的配置文件,默认是当前项目下
ClassPathXmlApplicationContext:用于解析当前类加载路径下的配置文件,推荐使用,因为类路径相对稳定 -
4 什么是Bean
5. IOC操作Bean管理(XML方式)
5.2 IOC操作Bean管理
5.3 Spring配置文件中的常用标签
5.4 自动装配
5.5 引入外部属性文件
6. IOC操作Bean管理(注解方式)
什么是注解?注解的目的是为了简化xml配置