Spring IOC源码分析

2018-11-05  本文已影响22人  ChaLLengerZeng

什么是Spring IOC / DI

大家都知道,Spring是一个管理Bean的容器,IOC承担着控制反转的责任,不论是我们之前单纯使用Spring框架去管理Bean还是之后接触到的SSM框架,再到SpringBoot中的IOC承担更大的责任,管理的是整个应用需要使用到的Bean,我喜欢把Spring组成的一系列体系叫做生态圈,IOC容器就相当于是生态圈中的户籍登记处,管理着每一个Bean

依赖注入,依赖注入的场景在使用Spring开发的时候程序员都经历过,之前仅仅停留在Java语言的角度上的时候,我们使用的一切对象都是需要我们自己new出来的,这种方式称为主动创建对象,而使用Spring的DI注入之后配合着IOC的控制反转,我们之前主动创建对象的操作,交由给Spring容器去管理,并且之后通过DI注入所需的依赖,简单地来说,使用了Spring的IOC和DI之后,将不用程序员自己去管理一个实例的生命周期,并且交由Spring管理的Bean可以通过Scope字段去设置Bean的模式,例如单例模式,以及设置一个Bean创建的作用域

Idea生成的IOC核心体系结构

BeanFactory

其中BeanFactory作为最顶层的一个接口类,它定义了IOC容器的基本功能规范,BeanFactory 有三个子类:ListableBeanFactory、HierarchicalBeanFactory 和AutowireCapableBeanFactory。

那为何要定义这么多层次的接口呢?查阅这些接口的源码和说明发现,每个接口都有他使用的场合,它主要是为了区分在 Spring 内部在操作过程中对象的传递和转化过程中,对对象的数据访问所做的限制。

这四个接口共同定义了 Bean 的集合、Bean 之间的关系、以及 Bean 行为,但是从上图中我们可以发现最终的默认实现类是 DefaultListableBeanFactory,他实现了所有的接口。

在这篇总结中将不会贴出源码,因为一个是量多,一个是源码中藏着的注释比较多,篇幅可能会很大,在接下来的说明中将只会阐述主要的观点

public interface BeanFactory {    
      
    //用于取消引用实例并将其与FactoryBean创建的bean区分开来。例如,如果命名的bean是FactoryBean,则获取将返回Factory,而不是Factory返回的实例。
    String FACTORY_BEAN_PREFIX = "&"; 
        
    //根据bean的名字,获取在IOC容器中得到bean实例    
    Object getBean(String name) throws BeansException;    
   
    //根据bean的名字和Class类型来得到bean实例,如果想要获取的bean实例的name与传入的类型不符将会抛出异常   
    Object getBean(String name, Class requiredType) throws BeansException;    
     
    //根据bean的name获取这个bean,允许指定显式的构造参数,覆盖bean定义中缺省的构造参数
    Object getBean(String name, Object... args) throws BeansException;

    //返回唯一匹配给定对象类型的bean实例(如果有)
    <T> T getBean(Class<T> requiredType) throws BeansException;

    //返回唯一匹配给定对象类型的bean实例(如果有),并且指定bean的构造参数
    <T> T getBean(Class<T> requiredType, Object... args) throws BeansException;

    //返回指定bean的Provider,允许对实例进行惰性按需检索,包括可用性和唯一性选项
    <T> ObjectProvider<T> getBeanProvider(Class<T> requiredType);

    //返回指定bean的Provider,参数为可解决的类型,这种类型可以更好的支持传入类型的各种方法,包括泛型
    <T> ObjectProvider<T> getBeanProvider(ResolvableType requiredType);

    //检查工厂中是否包含给定name的bean,或者外部注册的bean
    boolean containsBean(String name);

    //检查所给定name的bean是否为单例
    boolean isSingleton(String name) throws NoSuchBeanDefinitionException;

    //检查所给定name的bean是否为原型
    boolean isPrototype(String name) throws NoSuchBeanDefinitionException;

    //判断所给name的类型与type是否匹配
    boolean isTypeMatch(String name, ResolvableType typeToMatch) throws NoSuchBeanDefinitionException;

    //判断所给name的类型与type是否匹配
    boolean isTypeMatch(String name, Class<?> typeToMatch) throws NoSuchBeanDefinitionException;

    //获取给定name的bean的类型
    @Nullable
    Class<?> getType(String name) throws NoSuchBeanDefinitionException;

    //返回给定name的bean的别名
    String[] getAliases(String name);
     
}

补充部分

Improved programmatic resolution of dependencies

Spring Framework 4.3 also introduces ObjectProvider, an extension of the existing ObjectFactory interface with handy signatures such as getIfAvailable and getIfUniqueto retrieve a bean only if it actually exists (optional support) or if a single candidate can be determined (in particular: a primary candidate in case of multiple matching beans).

@Service
public class FooService {

    private final FooRepository repository;

    public FooService(ObjectProvider<FooRepository> repositoryProvider) {
        this.repository = repositoryProvider.getIfUnique();
    }
}

You may use such an ObjectProvider handle for custom resolution purposes during initialization as shown above, or store the handle in a field for late on-demand resolution (as you typically do with an ObjectFactory).

上述文档是Spring官方文档中的一部分,可以看到当需要使用注入IOC中bean的时候,我们可以使用ObjectProvider针对注入点进行一些处理,比如是否存在该bean或者是否可用以及是否唯一


上面代码是BeanFactory接口的抽象方法的声明,BeanFactory的子类有很多,其中DefaultListableBeanFactory是整个bean加载的核心部分

而要知道工厂是如何产生对象的,我们需要看具体的IOC容器实现,spring提供了许多IOC容器的实现。比如XmlBeanFactory,ClasspathXmlApplicationContext等

其中XmlBeanFactory就是针对最基本的IOC容器的实现,这个IOC容器可以读取XML文件定义的BeanDefinition(XML文件中对bean的描述),XmlBeanFactory在5.1.2版本中已被声明为不希望继续被使用

ApplicationContext是Spring提供的一个高级的IoC容器,它除了能够提供IoC容器的基本功能外,还为用户提供了以下的附加服务。

BeanDefinition

Spring IOC容器管理我们声明的各种bean之间的关系,bean的实例在Spring的实现中是以BeanDefinition来描述的

BeanDefinition体系结构

xml文件的读取是Spring中的重要功能,bean的解析过程很复杂,功能也同样被划分得很细,其中的解析过程主要由下列的类去完成

XmlBeanDefinitionReader体系结构

XmlBeanFactory流程 已被声明为不建议使用

@SuppressWarnings({"serial", "all"})
public class XmlBeanFactory extends DefaultListableBeanFactory {

    private final XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);


    /**
     * Create a new XmlBeanFactory with the given resource,
     * which must be parsable using DOM.
     * @param resource the XML resource to load bean definitions from
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource) throws BeansException {
        //调用第二个构造器传入这个Resource实例,即传入转换后的Xml文件资源
        this(resource, null);
    }

    /**
     * Create a new XmlBeanFactory with the given input stream,
     * which must be parsable using DOM.
     * @param resource the XML resource to load bean definitions from
     * @param parentBeanFactory parent bean factory
     * @throws BeansException in case of loading or parsing errors
     */
    public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
        //调用父类构造器传入一个父bean工厂
        super(parentBeanFactory);
        //加载bean的定义(这里的定义其实就是解析Xml文件装配一个Bean)
        this.reader.loadBeanDefinitions(resource);
    }

}

接下来看一下具体的加载逻辑

/**
     * Load bean definitions from the specified XML file.规定的XML文件
     * @param resource the resource descriptor for the XML file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    @Override
    public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException {
        return loadBeanDefinitions(new EncodedResource(resource));
    }

上述方法返回值为一共解析出了多少个bean的信息

/**
     * Load bean definitions from the specified XML file.
     * @param encodedResource the resource descriptor for the XML file,
     * allowing to specify an encoding to use for parsing the file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     */
    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
        Assert.notNull(encodedResource, "EncodedResource must not be null");
        //是否启用跟踪log
        if (logger.isTraceEnabled()) {
            logger.trace("Loading XML bean definitions from " + encodedResource);
        }

        //获取当前正在被加载的Xml中bean定义的Set
        Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
        if (currentResources == null) {
            //若正在被加载的bean的资源Set为空 则初始化
            currentResources = new HashSet<>(4);
            this.resourcesCurrentlyBeingLoaded.set(currentResources);
        }
        //Set在add时出现重复会返回false 则抛出循环加载
        if (!currentResources.add(encodedResource)) {
            throw new BeanDefinitionStoreException(
                    "Detected cyclic loading of " + encodedResource + " - check your import definitions!");
        }
        try {
            //获取Xml资源的输入流
            InputStream inputStream = encodedResource.getResource().getInputStream();
            try {
                InputSource inputSource = new InputSource(inputStream);
                //判断原始资源的编码为InputSource设置编码
                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 {
            //从正在加载的Set中移除这个Xml资源
            currentResources.remove(encodedResource);
            if (currentResources.isEmpty()) {
                this.resourcesCurrentlyBeingLoaded.remove();
            }
        }
    }

接下来进一步看解析Xml的过程

/**
     * Actually load bean definitions from the specified XML file.
     * @param inputSource the SAX InputSource to read from
     * @param resource the resource descriptor for the XML file
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of loading or parsing errors
     * @see #doLoadDocument
     * @see #registerBeanDefinitions
     */
    protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource)
            throws BeanDefinitionStoreException {

        //将Xml文件资源转换为Document对象
        Document doc = doLoadDocument(inputSource, resource);
        //注册Document对象的所有声明的bean
        int count = registerBeanDefinitions(doc, resource);
        if (logger.isDebugEnabled()) {
            logger.debug("Loaded " + count + " bean definitions from " + resource);
        }
        //返回新注册bean的数量
        return count;
    }
/**
     * 注册Xml中bean的定义
     * Register the bean definitions contained in the given DOM document.
     * Called by {@code loadBeanDefinitions}.
     * <p>Creates a new instance of the parser class and invokes
     * {@code registerBeanDefinitions} on it.
     * @param doc the DOM document
     * @param resource the resource descriptor (for context information)
     * @return the number of bean definitions found
     * @throws BeanDefinitionStoreException in case of parsing errors
     * @see #loadBeanDefinitions
     * @see #setDocumentReaderClass
     * @see BeanDefinitionDocumentReader#registerBeanDefinitions
     */
    public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
        BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader();
        //获取之前IOC容器中已经注册过的bean
        int countBefore = getRegistry().getBeanDefinitionCount();
        //解析Xml资源,注册其中配置的bean
        documentReader.registerBeanDefinitions(doc, createReaderContext(resource));
        //返回新注册bean的数量
        return getRegistry().getBeanDefinitionCount() - countBefore;
    }

/**
     * Register each bean definition within the given root {@code <beans/>} element.
     */
    @SuppressWarnings("deprecation")  // for Environment.acceptsProfiles(String...)
    protected void doRegisterBeanDefinitions(Element root) {
        // Any nested <beans> elements will cause recursion in this method. In
        // order to propagate and preserve <beans> default-* attributes correctly,
        // keep track of the current (parent) delegate, which may be null. Create
        // the new (child) delegate with a reference to the parent for fallback purposes,
        // then ultimately reset this.delegate back to its original (parent) reference.
        // this behavior emulates a stack of delegates without actually necessitating one.
        BeanDefinitionParserDelegate parent = this.delegate;
        this.delegate = createDelegate(getReaderContext(), root, parent);

        if (this.delegate.isDefaultNamespace(root)) {
            //获取profile节点
            String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE);
            //判断profileSpec中是否包含空串或空白字符或者为null包含的话返回false
            if (StringUtils.hasText(profileSpec)) {
                //将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.
                //判断是否有profile被激活
                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);
        //重头戏来了,从这里开始接下来的逻辑全都是在解析Xml
        parseBeanDefinitions(root, this.delegate);
        postProcessXml(root);

        this.delegate = parent;
    }

上面逻辑会看到有一些环境的字眼

!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)

熟悉配置bean的人应该能理解profile的作用,既然需要满足profile的作用就需要读取环境变量进行对环境变量的判空,并且判断解析出来的profile是不是环境变量中所定义的

主要解析的类为DefaultBeanDefinitionDocumentReader

其中声明了大量的常量,这些常量都是Xml在配置bean的时候允许的语法标记,可以想象得到正是通过这个类实现的解析逻辑

上一篇下一篇

猜你喜欢

热点阅读