Spring IoC容器
1、IoC容器概述
控制反转、IoC容器、依赖注入
- 控制反转:不是什么技术,而是一种设计思想。Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。
- IoC容器:IoC 容器控制了对象的创建,获取及对象之间关系的管理。
- 依赖注入(DI):组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。
IoC对编程带来的最大改变不是从代码上,而是从思想上,发生了“主从换位”的变化。应用程序原本是老大,要获取什么资源都是主动出击,但是在IoC/DI思想中,应用程序就变成被动的了,被动的等待IoC容器来创建并注入它所需要的资源了。
2、IoC容器的设计与实现——Bean组件与Context组件
- Bean 组件在Spring 的 org.springframework.beans 包下。这个包下的所有类主要解决了三件事:Bean 的定义、Bean 的创建以及对 Bean 的解析。对 Spring 的使用者来说唯一需要关心的就是 Bean 的定义,其他两个由 Spring 在内部帮你完成了,对你来说是透明的。Spring Bean 的创建时典型的工厂模式,它的顶级接口是 BeanFactory容器,实现了容器的基本功能。
- Context组件在Spring 的 org.springframework.context 包下,它实际上就是给 Spring 提供一个运行时的环境,用以保存各个对象的状态。ApplicationContext 是 Context 的顶级父类,它作为容器的高级系列而存在,应用上下文在简单容器的基础上,增加了许多面向框架的特性,同时对应用环境做了许多适配。
1. Bean组件分析
Bean 组件在 Spring 的 org.springframework.beans 包下。这个包下的所有类主要解决了三件事:Bean 的定义及创建,对 Bean 的解析和注册。对 Spring 的使用者来说唯一需要关心的就是 Bean 的定义,其他两个由 Spring 在内部帮你完成了,对你来说是透明的。
1. BeanFactory继承关系
BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和 AutowireCapableBeanFactory。但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,它实现了所有的接口。
每个接口都有他使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。
- ListableBeanFactory 接口表示这些 Bean 是可列表的
- HierarchicalBeanFactory 表示的是这些 Bean 是有继承关系的,也就是每个 Bean 有可能有父 Bean。
- AutowireCapableBeanFactory 接口定义 Bean 的自动装配规则。
- 这三个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为。
2. BeanFactory概述
BeanFactory是Spring bean容器的根接口.提供获取bean,是否包含bean,是否单例与原型,获取bean类型,bean 别名的方法 。
Bean 的定义主要由 BeanDefinition 实现,如下图说明了这些类的层次关系:
Bean 的定义就是完整的描述了在 Spring 的配置文件中你定义的
Bean 的解析过程非常复杂,功能被分的很细,因为这里需要被扩展的地方很多,必须保证有足够的灵活性,以应对可能的变化。Bean 的解析主要就是对 Spring 配置文件的解析。这个解析过程主要通过下图中的类完成:
2.1 Bean Definition从加载、解析、处理、注册到BeanFactory的过程
1、BeanDefinition的加载
使用的xml配置文件的方式来配置bean,首先要读取xml文件。
- AbstractBeanDefinitionReader类中
- 加载bean definition,但是这里方法参数resources 所指的资源并不确定是什么样的,有可能是xml文件,也有可能是属性文件,或者是脚本。
@Override
public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException {
Assert.notNull(resources, "Resource array must not be null");
int count = 0;
for (Resource resource : resources) {
count += loadBeanDefinitions(resource);//这个方法有几个实现,如下图所示
}
return count;
}- 使用实现类XmlBeanDefinitionReader中的实现,所以这里的资源是xml文件。
- 加载bean definition,但是这里方法参数resources 所指的资源并不确定是什么样的,有可能是xml文件,也有可能是属性文件,或者是脚本。
- XmlBeanDefinitionReader类中
从指定的xml文件中加载bean definition,但只是将Resource转化为EncodedResource对象。然后把这个任务递交给它的重载方法来做。
@Override
public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
return loadBeanDefinitions(new EncodedResource(resource));
}
重载方法从指定的XML文件中加载bean definition。但还不是真正的加载bean定义,只是做准备。
public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
Assert.notNull(encodedResource, "EncodedResource must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Loading XML bean definitions from " + encodedResource);
}Set
currentResources = this.resourcesCurrentlyBeingLoaded.get();
if (currentResources == null) {
currentResources = new HashSet<>(4);
this.resourcesCurrentlyBeingLoaded.set(currentResources);
}
if (!currentResources.add(encodedResource)) {
throw new BeanDefinitionStoreException(
"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
}
try {
InputStream inputStream = encodedResource.getResource().getInputStream();
try {
InputSource inputSource = new InputSource(inputStream);
if (encodedResource.getEncoding() != null) {
inputSource.setEncoding(encodedResource.getEncoding());
}
return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
}
finally {
inputStream.close();
}
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"IOException parsing XML document from " + encodedResource.getResource(), ex);
}
finally {
currentResources.remove(encodedResource);
if (currentResources.isEmpty()) {
this.resourcesCurrentlyBeingLoaded.remove();
}
}
}
doLoadBeanDefinitions方法从xml 文件中加载的bean definition 。
①、加载指定的文档,得到一个Document对象。
②、将Document对象和Resource交给registerBeanDefinitions(...)方法来完成注册
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
throws BeanDefinitionStoreException {try {
Document doc = doLoadDocument(inputSource, resource);
int count = registerBeanDefinitions(doc, resource);
if (logger.isDebugEnabled()) {
logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
}catch(...){...}
registerBeanDefinitions方法注册指定的Document对象中的bean definition。这里实际上是创建了一个BeanDefinitionDocumentReader对象然后让它来完成。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
int countBefore = getRegistry().getBeanDefinitionCount();//多种实现,如 DefaultBeanDefinitionDocumentReader
documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
return getRegistry().getBeanDefinitionCount() - countBefore;
}
2、BeanDefinition的解析
- DefaultBeanDefinitionDocumentReader类中
DefaultBeanDefinitionDocumentReader是BeanDefinitionDocumentReader的一个实现类。这一步主要是**解析文档解析的是
**。比如xml文件是XSD的还是DTD的。 这里主要是获得一个Element对象。这个对象就代表xml文件中的
节点。在这个节点下包含着文件中的所有 节点。然后将这个Element对像交给的doRegistrerBeanDefinitions(Element)方法来处理。 @Override
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
doRegisterBeanDefinitions(doc.getDocumentElement());
}
doRegisterBeanDefinitions方法将节点下的每一个
相对应的bean definition注册。但是真正做这件事的是另一个方法 parseBeanDefinitions(root, this.delegate); 在调用parseBeanDefinitions(root,this.delegate)方法之前和之后都可以对这个这个方法参数中的Element对象进行处理。
protected void doRegisterBeanDefinitions(Element root) {
BeanDefinitionParserDelegate parent = this.delegate;
this.delegate = createDelegate(getReaderContext(), root, parent);if (this.delegate.isDefaultNamespace(root)) {
String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
if (StringUtils.hasText(profileSpec)) {
String[] specifiedProfiles = StringUtils.tokenizeToStringArray(
profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS);
// We cannot use Profiles.of(...) since profile expressions are not supported
// in XML config. See SPR-12458 for details.
if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
if (logger.isDebugEnabled()) {
logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec +
"] not matching: " + getReaderContext().getResource());
}
return;
}
}
}preProcessXml(root);
parseBeanDefinitions(root, this.delegate);
postProcessXml(root);this.delegate = parent;
}用一个for循环遍历
节点下的所有子节点,也就是所有的 ,然后对 节点进行解析。注意,刚才是对 进行解析。不过这个解析的任务交给parseDefaultElement(Element ele,BeanDefinitionParserDelegate delegate)方法来完成。 protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for (int i = 0; i < nl.getLength(); i++) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element) node;
if (delegate.isDefaultNamespace(ele)) {
parseDefaultElement(ele, delegate);
}
else {
delegate.parseCustomElement(ele);
}
}
}
}
else {
delegate.parseCustomElement(root);
}
}parseDefaultElement方法用到了递归,因为bean是可以嵌套的,所以
节点下的每一个 节点都可能在嵌套有很多bean。所以它会判断这个bean是不是嵌套bean,如果不是就进行处理,如果是就进行递归。 private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
importBeanDefinitionResource(ele);
}
else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
processAliasRegistration(ele);
}
else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
processBeanDefinition(ele, delegate);
}
else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
// recurse
doRegisterBeanDefinitions(ele);
}
}
3、BeanDefinition的处理
- DefaultBeanDefinitionDocumentReader类中
processBeanDefinition方法中将Element对象转化成了BeanDefinitionHolder对象。这个BeanDefinitionHolder对象中持有的BeanDefinition实例的引用,还有beanName,还有bean的别名。(BeanDefinitionHolder的创建)
然后将BeanDefinitionHolder对象和特定的bean工厂作为参数交给BeanDefinitionReaderUtils类来处理来进行注册。
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
// Register the final decorated instance.
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry());
}
catch (BeanDefinitionStoreException ex) {
getReaderContext().error("Failed to register bean definition with name '" +
bdHolder.getBeanName() + "'", ele, ex);
}
// Send registration event.
getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
}
}
4、BeanDefinition的注册
BeanDefinitionReaderUtils类中
registerBeanDefinition方法先根据BeanDefinitionHolder获取beanName和BeanDefinition,然后将其注册到Bean工厂中的Map对象中。比如bean工厂是DefaultListableBeanFactory。
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}registry是一个Bean工厂,可以有多个实现,如DefaultListableBeanFactory等
DefaultListableBeanFactory类中,在这个方法中,将BeanDefinition 注册到了ConcurrentHashMap对象中了。
@Override
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
throws BeanDefinitionStoreException {Assert.hasText(beanName, "Bean name must not be empty");
Assert.notNull(beanDefinition, "BeanDefinition must not be null");if (beanDefinition instanceof AbstractBeanDefinition) {
try {
((AbstractBeanDefinition) beanDefinition).validate();
}
catch (BeanDefinitionValidationException ex) {
throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName,
"Validation of bean definition failed", ex);
}
}
BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName);
if (existingDefinition != null) {
if (!isAllowBeanDefinitionOverriding()) {
throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition);
}
else if (existingDefinition.getRole() < beanDefinition.getRole()) {
// e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE
if (logger.isInfoEnabled()) {
logger.info("Overriding user-defined bean definition for bean '" + beanName +
"' with a framework-generated bean definition: replacing [" +
existingDefinition + "] with [" + beanDefinition + "]");
}
}
else if (!beanDefinition.equals(existingDefinition)) {
if (logger.isDebugEnabled()) {
logger.debug("Overriding bean definition for bean '" + beanName +
"' with a different definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Overriding bean definition for bean '" + beanName +
"' with an equivalent definition: replacing [" + existingDefinition +
"] with [" + beanDefinition + "]");
}
}
**this.beanDefinitionMap.put(beanName, beanDefinition);**
}
else {
if (hasBeanCreationStarted()) {
// Cannot modify startup-time collection elements anymore (for stable iteration)
synchronized (this.beanDefinitionMap) {
**this.beanDefinitionMap.put(beanName, beanDefinition);**
ListupdatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);
updatedDefinitions.addAll(this.beanDefinitionNames);
updatedDefinitions.add(beanName);
this.beanDefinitionNames = updatedDefinitions;
if (this.manualSingletonNames.contains(beanName)) {
SetupdatedSingletons = new LinkedHashSet<>(this.manualSingletonNames);
updatedSingletons.remove(beanName);
this.manualSingletonNames = updatedSingletons;
}
}
}
else {
// Still in startup registration phase
this.beanDefinitionMap.put(beanName, beanDefinition);
this.beanDefinitionNames.add(beanName);
this.manualSingletonNames.remove(beanName);
}
this.frozenBeanDefinitionNames = null;
}if (existingDefinition != null || containsSingleton(beanName)) {
resetBeanDefinition(beanName);
}
}
BeanFactroy是一个Spring容器,用于创建,配置,管理bean,bean之间的依赖关系也由BeanFactory负责维护;
BeanFactory对bean的实例化过程,无须bean的调用者关心,调用者只需要通过getBean()方法获得指定bean的引用;
创建BeanFactory的实例时,必须提供bean的详细配置信息,bean的详细信息通过XML文件描述,创建BeanFactory实例时,需要提供XML文件作为参数
BeanDefinition的加载、解析、处理、注册主要涉及到了四个类。
①、XMLBeanDefinitionReader:主要是的任务是把XML文件加载到内存中以Document对象的形式存在。
②、DefaultBeanDefinitionDocumentReader:完成解析和处理的任务。最后将处理得到的BeanDefinitionHolder交给了BeanDefinitionReaderUtils进行注册。
③、BeanDefinitionReaderUtils:BeanDefinitionHolder有了,Bean工厂也有了,它就负责把BeanDefinitionHolder中的BeanDefinition和BeanName等取出来,然后注册到Bean工厂中。④、DefaultListableBeanFactory(bean工厂):它有一个ConcurrentHashMap成员变量,以beanName为键,BeanDefinition为值保存注册的bean。
=====================================================================================
2. Context组件分析
Context 在 Spring 的 org.springframework.context 包下,给 Spring 提供一个运行时的环境,用以保存各个对象的状态。
ApplicationContext 是 Context 的顶级父类,它除了能标识一个应用环境的基本信息外,还实现了六个接口,这六个接口主要是扩展了 Context 的功能。下面是 Context 的类结构图:
从上图中可以看出 ApplicationContext 继承了 BeanFactory,这也说明了 Spring 容器中运行的主体对象是 Bean,另外 ApplicationContext 继承了 ResourceLoader 接口,使得 ApplicationContext 可以访问到任何外部资源。
ApplicationContext 的子类主要包含两个方面:
- ConfigurableApplicationContext 表示该 Context 是可修改的,也就是在构建 Context 中用户可以动态添加或修改已有的配置信息,它下面又有多个子类,其中最经常使用的是可更新的 Context,即 AbstractRefreshableApplicationContext 类。
- WebApplicationContext 顾名思义,就是为 web 准备的 Context 他可以直接访问到 ServletContext,通常情况下,这个接口使用的少。
再往下分就是构建 Context 的文件类型,接着就是访问 Context 的方式。这样一级一级构成了完整的 Context 等级层次。
总体来说 ApplicationContext 必须要完成以下几件事:
- 标识一个应用环境
- 利用 BeanFactory 创建 Bean 对象
- 保存对象关系表
- 能够捕获各种事件
Context 作为 Spring 的 Ioc 容器,基本上整合了 Spring 的大部分功能,或者说是大部分功能的基础。
3、Core 组件
Core 组件作为 Spring 的核心组件,它其中包含了很多的关键类,其中一个重要组成部分就是定义了资源的访问方式。
Resource 相关的类结构如下图所示:
- Resource 接口封装了各种可能的资源类型,也就是对使用者来说屏蔽了文件类型的不同。
- 对资源的提供者来说,如何把资源包装起来交给其他人用这也是一个问题,我们看到 Resource 接口继承了 InputStreamSource 接口,这个接口中有个 getInputStream 方法,返回的是 InputStream 类。这样所有的资源都被可以通过 InputStream 这个类来获取,所以也屏蔽了资源的提供者。
- 另外还有一个问题就是加载资源的问题,也就是资源的加载者要统一,从上图中可以看出这个任务是由 ResourceLoader 接口完成,他屏蔽了所有的资源加载者的差异,只需要实现这个接口就可以加载所有的资源,它的默认实现是 DefaultResourceLoader。
Context 和 Resource 的类关系图:
Context 是把资源的加载、解析和描述工作委托给了 ResourcePatternResolver 类来完成,它相当于一个接头人,把资源的加载、解析和资源的定义整合在一起便于其他组件使用。Core 组件中还有很多类似的方式。