spring源码学习之路---IOC实现原理(三)_weixin_30425949的博客-程序员资料

技术标签: java  

          作者:zuoxiaolong8810(左潇龙),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可。

          上一章我们已经初步认识了BeanFactory和BeanDefinition,一个是IOC的核心工厂接口,一个是IOC的bean定义接口,上章提到说我们无法让BeanFactory持有一个Map<String,Object>来完成bean工厂的功能,是因为spring的初始化是可以控制的,可以到用的时候才将bean实例化供开发者使用,除非我们将bean的lazy-init属性设置为true,初始化bean工厂时采用延迟加载。

          那么知道了上述两个接口,我相信不少人甚至不看源码都已经猜到spring是如何做的了。没错,就是让bean工厂持有一个Map<String,BeanDefinition>,这样就可以在任何时候我们想用哪个bean,取到它的bean定义,我们就可以创造出一个新鲜的实例。

          接口当然不可能持有这样一个对象,那么这个对象一定是在BeanFactory的某个实现类或者抽象实现类当中所持有的,我经过跋山涉水,终于把它给找出来了,来看DefaultListableBeanFactory。

package org.springframework.beans.factory.support;

import java.io.NotSerializableException;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.inject.Provider;

import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanCurrentlyInCreationException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.CannotLoadBeanClassException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.SmartFactoryBean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

/**
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Sam Brannen
 * @author Costin Leau
 * @since 16 April 2001
 */
public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory
        implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {

    private static Class javaxInjectProviderClass = null;

    static {
        ClassLoader cl = DefaultListableBeanFactory.class.getClassLoader();
        try {
            javaxInjectProviderClass = cl.loadClass("javax.inject.Provider");
        }
        catch (ClassNotFoundException ex) {
            // JSR-330 API not available - Provider interface simply not supported then.
        }
    }


    /** Map from serialized id to factory instance */
    private static final Map<String, Reference<DefaultListableBeanFactory>> serializableFactories =
            new ConcurrentHashMap<String, Reference<DefaultListableBeanFactory>>();

    /** Optional id for this factory, for serialization purposes */
    private String serializationId;

    /** Whether to allow re-registration of a different definition with the same name */
    private boolean allowBeanDefinitionOverriding = true;

    /** Whether to allow eager class loading even for lazy-init beans */
    private boolean allowEagerClassLoading = true;

    /** Resolver to use for checking if a bean definition is an autowire candidate */
    private AutowireCandidateResolver autowireCandidateResolver = new SimpleAutowireCandidateResolver();

    /** Map from dependency type to corresponding autowired value */
    private final Map<Class, Object> resolvableDependencies = new HashMap<Class, Object>();

    /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

    /** List of bean definition names, in registration order */
    private final List<String> beanDefinitionNames = new ArrayList<String>();

    /** Whether bean definition metadata may be cached for all beans */
    private boolean configurationFrozen = false;

    /** Cached array of bean definition names in case of frozen configuration */
    private String[] frozenBeanDefinitionNames;


}

              注明下,这里我省略了下面N多行源码,源码太长,而且太多的话容易混乱,切勿认为此类就这么多了。

              看它名字就知道,这是一个默认的bean工厂实现类,也就是说,如果你需要的功能非常单一,这个实现类已经足够可以满足你了,而以后如果你想要对spring的容器扩展,那么只需要扩展或者持有这个对象即可。

        /** Map of bean definition objects, keyed by bean name */
    private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<String, BeanDefinition>();

              看到这一行,其实已经证明了我们的猜测,即使英文不太好,也能看懂它所注释的意思是bean定义的MAP对象,采用beanName作为key值。

              走到这里,思路已经很明确了,bean工厂的初始化其实就是往这个Map里填充东西。只要把我们XML文件中定义的bean都填充到这里,其实这个工厂就已经可以工作了。

              那么从现在来看,我们需要什么才能把Map填充呢?也就是初始化bean工厂呢,或者说建立IOC容器。我首先列出来以下几步。

             1.需要一个File指向我们的XML文件(本文的配置文件都已XML为例,因为这是我们最熟悉的),专业点可以叫资源定位,简单点可以说我们需要一些工具来完成找到XML文件的所在位置。

             2.需要一个Reader来读取我们的XML文件,专业点叫DOM解析,简单点说,就是把XML文件的各种定义都给拿出来。

             3.需要将读出来的数据都设置到Map当中。

             这三部总结起来就是定位、解析、注册。我们首先按照这个思路来试一下。

             直接上代码,我们还使用原来的Person类作为一个Bean。

package com.springframework.beans.test;

public class Person {

    public void work(){
        System.out.println("I am working");
    }
}

            我们还需要写一个简单的XML文件,beans.xml。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
 <bean id="person" class="com.springframework.beans.test.Person"></bean>
</beans>

           下面是我们根据上述的思路写一段程序,来看看会发生什么情况。

package com.springframework.beans.test;

import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.ClassPathResource;


public class TestDefaultListableBeanFactory {

    public static void main(String[] args) {
        ClassPathResource classPathResource = new ClassPathResource("beans.xml");
        DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory);
        xmlBeanDefinitionReader.loadBeanDefinitions(classPathResource);
        System.out.println("numbers: " + defaultListableBeanFactory.getBeanDefinitionCount());
        ((Person)defaultListableBeanFactory.getBean("person")).work();
    }
}

            以下是输出结果。
 

           可以看到,结果与我们期望的是一样的,成功的解析了XML文件,并注册了一个bean定义,而且我们使用getBean方法也成功得到了Person的实例。

           上述这段程序当中可以看出,bean工厂的初始化一共使用了四行程序。

           第一行完成了我们的第一步,即资源定位,采用classpath定位,因为我的beans.xml文件是放在src下面的。

           第二行创建了一个默认的bean工厂。

           第三行创建了一个reader,从名字就不难看出,这个reader是用来读取XML文件的。这一步要多说一句,其中将我们创建的defaultListableBeanFactory作为参数传给了reader的构造函数,这里是为了第四步读取XML文件做准备。

           第四行使用reader解析XML文件,并将读取的bean定义回调设置到defaultListableBeanFactory当中。其实回调这一步就相当于我们上述的注册这一步。

           这个时候defaultListableBeanFactory已经被正确初始化了,我们已经可以使用它的一些方法了,比如上面所使用的获取bean个数,以及获得一个bean实例的方法。

           但是我相信真正的开发当中,没有人会采用这样的方式去创造一个bean工厂,我们可以有更简单的方式。上面的四步,我们肯定希望一步就可以完成它。是的,这不是在做梦,就像下面这样。

package com.springframework.beans.test;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class TestApplicationContext {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new FileSystemXmlApplicationContext("classpath:beans.xml");
        System.out.println("numbers: " + applicationContext.getBeanDefinitionCount());
        ((Person)applicationContext.getBean("person")).work();
    }
}

          接下来就是见证奇迹的时刻了,输出结果如下图。        

          我们果然一步就完成了上面四步所做的事情。而且仔细看会发现日志信息当中,第二次采用FileSystemXmlApplicationContext时,日志信息多了许多,分别在上面的前面多了两行,后面多了两行,这说明别看我们是一步,但其实这里比上面做了更多的事情。

          具体我们在new一个FileSystemXmlApplicationContext对象的时候,spring到底做了哪些事情呢,这个自然要去跟随源码去看个究竟。

 

 

 

转载于:https://www.cnblogs.com/zuoxiaolong/p/spring3.html

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_30425949/article/details/97682910

智能推荐

校招面试要点_校招面试关于makefile的问题_shzxrt的博客-程序员资料

一:操作系统1. 进程的有哪几种状态,状态转换图,及导致转换的事件。2. 进程与线程的区别。3. 进程通信的几种方式。4. 线程同步几种方式。(一定要会写生产者、消费者问题,完全消化理解)5. 线程的实现方式. (也就是用户线程与内核线程的区别)6. 用户态和核心态的区别。7. 用户栈和内核栈的区别。8. 内存池、进程池、线程池。(c

前端性能优化(三)performance_浅海鱼~的博客-程序员资料

前端性能优化从首屏到交互,三大利器已传授

前端HTML5面试官和应试者一问一答_一问一答h5_掘金-我是哪吒的博客-程序员资料

哪吒人生信条:如果你所学的东西 处于喜欢 才会有强大的动力支撑。把你的前端拿捏得死死的,每天学习得爽爽的,如果你所学的东西 处于喜欢 才会有强大的动力支撑。感谢不负每一份热爱前端的程序员,不论前端技能有多奇葩,欢迎关注加我入群vx:xiaoda0423前言希望可以通过这篇文章,能够给你得到帮助。(感谢一键三连)1.HTML5表单增加的输入类型url类型:专门为输入url地址定义的文本库,在验证输入文本的格式时,如果文本框中的的内容不符合url地址的格式,会提示验证错误。email类型:

Chrome Devtool — Performance_devtools [email protected]的博客-程序员资料

前言 Performance 一个在前端开发领域中,无法被忽视的存在。使用Chrome DevTools的performance面板可以记录和分析页面在运行时的所有活动。本文将详细介绍如何使用performance面板解决性能瓶颈。一、Performance 工具优点可视化图形界面 每毫秒做的事情 文件的执行加载的顺序 每毫秒界面展示的效果 每个方法执行的顺序和时...

CentOS7.0+ 安装PHP+Apache+MySQL_十四月凉的博客-程序员资料

PHP和Apache的安装跟以前系统差不多,有差别的主要是MySQL的安装。

随便推点

把VIM打造成一个真正的IDE_tianxiaogang12的博客-程序员资料

转载http://www.vimer.cn/ 这里所说的IDE主要是指C/C++开发,但是由于笔者之前也搞java和c#开发,所以对这两种语言也会有所兼顾。这个话题可能要分好几篇文章来写了,今天主要讲一下最简单的,包括vimrc里面的基本配置,让vim能够完美支持各种源码文件;vimrc配置如下(为简单起见,采取注释的方式直接说明):if(has(”win32″) || has

android平台下OpenGL ES 3.0着色语言基础知识(上)_handy周的博客-程序员资料

本篇整理自《OpenGL ES 3.0 编程指南第2版》着色器版本规范OpenGL ES 3.0 顶点着色器和片段着色器的第1行总是声明着色器版本。声明着色器版本通知着色器编译器预期在着色器中出现的语法和结构。编译器按照声明的着色语言版本检查着色器语法。采用如下语法声明着色器使用OpenGL ES着色语言3.00版本:#version 300 es没有声明版本号的着色器被认定为使用Ope...

指针:什么是指针?_independenting的博客-程序员资料

概念指针是C语言中的一种数据类型,T*,用该种类型定义的变量称为指针变量,该变量中存储的是一个地址注:指针变量中存储的是一个地址,指针就是地址,地址就是指针分类指针分为一级指针和多级指针指针的操作解引用:返回内存地址中对应的对象。int a=10; int *p=&amp;a;cout&lt;&lt;*p&lt;&lt;endl;// 输出a的值,就是解引用操作。指向结构体成员运算符(-&gt;):使用一个指向结构体或对象的指针访问其内成员。sizeof(指针):32位系统下占用

前端性能优化-Performance详解_前端performance_紫微前端的博客-程序员资料

Performance接口可以获取到当前页面中与性能相关的信息。它是 High Resolution Time API 的一部分,同时也融合了Performance Timeline API、Navigation Timing API、User Timing API和Resource Timing API。该类型的对象可以通过调用只读属性Window.performance来获得。注意:除了以下指出的情况外,该接口及其成员在Web Worker中可用。此外,还需注意,perfo...

前端展示ftp服务器的文件,向ftp服务端上传文件跟文件夹-20151201_柳帝城的博客-程序员资料

当前位置:我的异常网» Web前端»向ftp服务端上传文件跟文件夹-20151201向ftp服务端上传文件跟文件夹-20151201www.myexceptions.net网友分享于:2015-12-08浏览:0次向ftp服务端上传文件和文件夹-20151201import java.io.File;import java.io.FileInputStream;import java....

适合新手的3d建模软件,你知道几个?_3d建模软件基础入门级_普通网友的博客-程序员资料

许多新手小白刚开始接触3D建模行业时,肯定是非常迷茫的。首先,不知道学习3D建模用什么软件好?就目前市场上,PC端的3D建模软件太多了:室内设计的,三维动画的、角色模型的等等,每对应的不同行业就有不同的软件。今天,来讲一讲适合新手的3D建模软件。在选择软件的时候,很多人的顾虑是电脑能不能带起来?会不会有延迟、卡顿等问题?所以在他还没使用软件之前会先配一台好的电脑,或者把电脑的配置升级起来。今天推荐的这款3d建模软件非常适合小白和达人使用,而且也不用顾虑电脑配置问题,它是一款手机APP3d建模软件-易模。

推荐文章

热门文章

相关标签