美文网首页Java架构技术栈
长文预警!Spring源码之IoC容器的基本实现

长文预警!Spring源码之IoC容器的基本实现

作者: 若丨寒 | 来源:发表于2020-11-03 11:25 被阅读0次

    Spring源码之容器的基本实现

    概述

    Spring是一个开放源代码的设计层面框架,他解决的是业务逻辑层和其他各层的松耦合问题,因此它将面向接口的编程思想贯穿整个系统应用。Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson创建。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。

    本文以Spring 5.1.X版本为前提

    Spring的结构组成

    Spring框架是一个分层架构,它包含一系列的功能要素,并被分为大约20个模块,如下图所示:

    长文预警!Spring源码之IoC容器的基本实现

    Spring容器类型

    Spring提供了两种不同的类型的容器

    Spring BeanFactory容器:它是最简单的容器,给 DI 提供了基本的支持ApplicationContext容器 :ApplicationContext 容器继承自BeanFactory,它包括 BeanFactory 容器的所有功能,所以通常建议使用。

    Spring容器的类别及其特点

    长文预警!Spring源码之IoC容器的基本实现

    IoC容器的初始化过程

    所谓的IOC,其实就是把我们的类打包成一个BeanDefinition对象,该对象里面会包含我们的类Class名称,以及我们在xml中定义的scope,还要这个类的属性等信息。然后把这个BeanDefinition对象put到Map中,这个Map就是我们所谓的容器。

    具体来说,这个启动包括BeanDefinition的Resouce定位、载入和注册三个基本过程。如果我们了解如何编程式地使用IoC容器,就可以清楚地看到Resource定位和载入过程的接口调用。.具体分别如下:

    • 第一个过程是Resource定位过程。这个Resource定位指的是BeanDefinition的资源定位,它由ResourceLoader通过统一的Resource接口来完成,这个Resource对各种形式的BeanDefinition的使用都提供了统一接口。对于这些BeanDefinition的存在形式,相信大家都不会感到陌生。比如,在文件系统中的Bean定义信息可以使用FileSystemResource来进行抽象;在类路径中的Bean定义信息可以使用前面提到的ClassPathResource来使用,等等。这个定位过程类似于容器寻找数据的过程,就像用水桶装水先要把水找到一样。
    • 第二个过程是BeanDefinition的载入。这个载入过程是把用户定义好的Bean表示成IoC容器内部的数据结构,而这个容器内部的数据结构就是BeanDefinition。下面介绍这个数据结构的详细定义。具体来说,这个BeanDefinition实际上就是POJO对象在IoC容器中的抽象,通过这个BeanDefinition定义的数据结构,使IoC容器能够方便地对POJO对象也就是Bean进行管理。在下面的章节中,我们会对这个载入的过程进行详细的分析,使大家对整个过程有比较清楚的了解。
    • 第三个过程是向IoC容器注册这些BeanDefinition的过程。这个过程是通过调用BeanDefinitionRegistry接口的实现来完成的。这个注册过程把载入过程中解析得到的BeanDefinition向IoC容器进行注册。通过分析,我们可以看到,在IoC容器内部将BeanDefinition注入到一个HashMap中去,IoC容器就是通过这个HashMap来持有这些BeanDefinition数据的.

    值得注意的是,这里谈的是IoC容器初始化过程,在这个过程中,一般不包含Bean依赖注入的实现。在Spring IoC的设计中,Bean定义的载入和依赖注入是两个独立的过程,下面我们看看这个启动过程.

    在使用IoC容器时,需要如下几个步骤:

    1)创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息.

    2)创建一个BeanFactory,比如常用的DefaultListableBeanFactory。

    3)创建一个载入BeanDefinition的读取器,比如XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,通过一个回调配置给BeanFactory。

    4)从定义好的资源位置读入配置信息,具体的解析过程由XmlBeanDefinitionReader来完成。完成整个载入和注册Bean定义之后,需要的IoC容器就建立起来了。这个时候就可以直接使用IoC容器了。

    IOC容器具体实现源码解析

    以读取xml配置文件为例

    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    public ClassPathXmlApplicationContext(String[] paths, Class<?> clazz, ApplicationContext parent)
                throws BeansException {
        // 动态确定使用哪个加载器加载配置文件
            super(parent);
            Assert.notNull(paths, "Path array must not be null");
            Assert.notNull(clazz, "Class argument must not be null");
        // 获取配置文件的路径
            this.configResources = new Resource[paths.length];
            for (int i = 0; i < paths.length; i++) {
                this.configResources[i] = new ClassPathResource(paths[i], clazz);
            }
        // 加载配置文件
            refresh();
        }
    

    ClassPathResource

    ClassPathResource 中的实现方式便是通 class 或者 classLoader 提供的底层方法进行调用

    public class ClassPathResource extends AbstractFileResolvingResource {
        private final String path;
        @Nullable
        private ClassLoader classLoader;
        @Nullable
        private Class<?> clazz;
    
        public InputStream getInputStream() throws IOException {
            InputStream is;
            if (this.clazz != null) {
                is = this.clazz.getResourceAsStream(this.path);
            } else if (this.classLoader != null) {
                is = this.classLoader.getResourceAsStream(this.path);
            } else {
                is = ClassLoader.getSystemResourceAsStream(this.path);
            }
    
            if (is == null) {
                throw new FileNotFoundException(this.getDescription() + " cannot be opened because it does not exist");
            } else {
                return is;
            }
        }
    }
    

    ClassPathXmlApplicationContext

    refresh()是来自AbstractApplicationContext里面, ClassPathXmlApplicationContext的refresh()也是出自

    AbstractApplicationContext.refresh()方法代码如下(从里面也基本可以看出初始化的大致过程):

    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            //准备启动spring容器,设置容器的启动日期和活动标志 
            prepareRefresh();
    
            // Tell the subclass to refresh the internal bean factory.
            //获得容器ApplicationContext的子类BeanFactory。步骤如下:
            //1.如果已经有了BeanFactory就销毁它里面的单例Bean并关闭这个BeanFactory。
            //2.创建一个新的BeanFactory。
            //3.对这个BeanFactory进行定制(customize),如allowBeanDefinitionOverriding等参数
            //4.转载BeanDefinitions(读取配置文件,将xml转换成对应得BeanDefinition)
            //5.检查是否同时启动了两个BeanFactory。  
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    
            // Prepare the bean factory for use in this context.
            //配置BeanFactory(就是将ApplicationContext的一些属性配置到BeanFactory上吧)
         //如重要的如设置classLoader;将BeanPostProcess注册到BeanFactory里  
            prepareBeanFactory(beanFactory);
    
            try {
            // Allows post-processing of the bean factory in context subclasses.
            //允许上下文的子类去执行postProcessor  
            postProcessBeanFactory(beanFactory);
    
            // Invoke factory processors registered as beans in the context.
            // 执行注册到该上下文的BeanFactoryPostProcessors
            invokeBeanFactoryPostProcessors(beanFactory);
    
            // Register bean processors that intercept bean creation.
            // 开始注册BeanPostProcessor来拦截其他的bean的初始化过程
            registerBeanPostProcessors(beanFactory);
    
            // Initialize message source for this context.
            // 初始化消息源
            initMessageSource();
    
            // Initialize event multicaster for this context.
            //注册上下文事件的广播集  
            initApplicationEventMulticaster();
    
            // Initialize other special beans in specific context subclasses.
            //初始化一些特殊的bean
            onRefresh();
    
            // Check for listener beans and register them.
            //查询并校验监听器并注册
            registerListeners();
    
            // Instantiate all remaining (non-lazy-init) singletons.
            /// 实例化所有非懒加载的所有bean
            finishBeanFactoryInitialization(beanFactory);
    
            // Last step: publish corresponding event.
            //最后一步发布所有的运用
            finishRefresh();
            }
    
            catch (BeansException ex) {
                if (logger.isWarnEnabled()) {
                    logger.warn("Exception encountered during context initialization - " +
                    "cancelling refresh attempt: " + ex);
                }
    
                // Destroy already created singletons to avoid dangling resources.
                destroyBeans();
    
                // Reset 'active' flag.
                cancelRefresh(ex);
    
                // Propagate exception to caller.
                throw ex;
            }
    
            finally {
                // Reset common introspection caches in Spring's core, since we
                // might not ever need metadata for singleton beans anymore...
                resetCommonCaches();
            }
        }
    }
    

    BeanFactory

    obtainFreshBeanFactory方法如下:

    /**
         * Tell the subclass to refresh the internal bean factory.
         * @return the fresh BeanFactory instance
         * @see #refreshBeanFactory()
         * @see #getBeanFactory()
         */
        protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
        //第一步对Resource定位
            refreshBeanFactory();
            ConfigurableListableBeanFactory beanFactory = getBeanFactory();
            if (logger.isDebugEnabled()) {
                logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
            }
            return beanFactory;
        }
    

    AbstractRefreshableApplicationContext.refreshBeanFactory方法源码如下:

    /**
         * This implementation performs an actual refresh of this context's underlying
         * bean factory, shutting down the previous bean factory (if any) and
         * initializing a fresh bean factory for the next phase of the context's lifecycle.
         */
        @Override
        protected final void refreshBeanFactory() throws BeansException {
            if (hasBeanFactory()) {
                destroyBeans();
                closeBeanFactory();
            }
            try {
          // 创建容器
                DefaultListableBeanFactory beanFactory = createBeanFactory();
                beanFactory.setSerializationId(getId());
          // 对容器进行定制化,如设置启动的参数,开启注解的自动装配等
                customizeBeanFactory(beanFactory);
          // 载入BeanDefinition,委派模式
                loadBeanDefinitions(beanFactory);
                synchronized (this.beanFactoryMonitor) {
                    this.beanFactory = beanFactory;
                }
            }
            catch (IOException ex) {
                throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
            }
        }
    

    DefaultListableBeanFactory

    public DefaultListableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
          super(parentBeanFactory);
    }
    

    AbstractAutowireCapableBeanFactory.java

    public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
       this();
       this.setParentBeanFactory(parentBeanFactory);
    }
    
    public AbstractAutowireCapableBeanFactory() {
            this.instantiationStrategy = new CglibSubclassingInstantiationStrategy();
            this.parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
            this.allowCircularReferences = true;
            this.allowRawInjectionDespiteWrapping = false;
            this.ignoredDependencyTypes = new HashSet();
            this.ignoredDependencyInterfaces = new HashSet();
            this.currentlyCreatedBean = new NamedThreadLocal("Currently created bean");
            this.factoryBeanInstanceCache = new ConcurrentHashMap(16);
            this.filteredPropertyDescriptorsCache = new ConcurrentHashMap(256);
            // 自动装配时忽略给定的依赖接口
            // 忽略该接口的实现类中和接口setter方法入参类型相同的依赖
            this.ignoreDependencyInterface(BeanNameAware.class);
            this.ignoreDependencyInterface(BeanFactoryAware.class);
            this.ignoreDependencyInterface(BeanClassLoaderAware.class);
        }
    

    ignoreDependencylnterface 的主要功能是 忽略给定接口的向动装配功能。

    举例来说,当 A 中有属性 B ,那么当 Spring 在获取 A的 Bean 的时候如果其属性 B 还没有 初始化,那么 Spring 会自动初始化 B,这也是 Spring 提供的一个重要特性 。但是,某些情况 下, B不会被初始化,其中的一种情况就是B 实现了 BeanNameAware 接口 。Spring 中是这样介绍的:自动装配时忽略给定的依赖接口,典型应用是边过其他方式解析 Application 上下文注册依赖,类似于 BeanFactory 通过 BeanFactoryAware 进行注入或者 ApplicationContext 通过 ApplicationContextAware 进行注入。

    调用ignoreDependencyInterface方法后,被忽略的接口会存储在BeanFactory的名为ignoredDependencyInterfaces的Set集合中:

    public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
    
      private final Set<Class<?>> ignoredDependencyInterfaces;
    
      public void ignoreDependencyInterface(Class<?> ifc) {
          this.ignoredDependencyInterfaces.add(ifc);
      }
    }
    

    ignoredDependencyInterface的真正作用还得看AutowireUtils类的isSetterDefinedInInterface方法:

    public static boolean isSetterDefinedInInterface(PropertyDescriptor pd, Set<Class<?>> interfaces) {
            // //获取bean中某个属性对象在bean类中的setter方法
            Method setter = pd.getWriteMethod();
            if (setter != null) {
                // // 获取bean的类型
                Class<?> targetClass = setter.getDeclaringClass();
                Iterator var4 = interfaces.iterator();
    
                while(var4.hasNext()) {
                    Class<?> ifc = (Class)var4.next();
                    // bean类型是否接口的实现类
                    // 接口是否有入参和bean类型完全相同的setter方法
                    if (ifc.isAssignableFrom(targetClass) && ClassUtils.hasMethod(ifc, setter.getName(), setter.getParameterTypes())) {
                        return true;
                    }
                }
            }
    
            return false;
    }
    

    我们最初理解是在自动装配时忽略该接口的实现,实际上是在自动装配时忽略该接口实现类中和setter方法入参相同的类型,也就是忽略该接口实现类中存在依赖外部的bean属性注入。

    典型应用就是BeanFactoryAware和ApplicationContextAware接口。首先看该两个接口的源码:

    public interface BeanFactoryAware extends Aware {
        void setBeanFactory(BeanFactory var1) throws BeansException;
    }
    
    public interface ApplicationContextAware extends Aware {
        void setApplicationContext(ApplicationContext var1) throws BeansException;
    }
    

    在Spring源码中在不同的地方忽略了该两个接口:

    // AbstractAutowireCapableBeanFactory.java
    this.ignoreDependencyInterface(BeanFactoryAware.class);
    // AbstractApplicationContext.java
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    

    这样的做法使得ApplicationContextAware和BeanFactoryAware中的ApplicationContext或BeanFactory依赖在自动装配时被忽略,而统一由框架设置依赖,如ApplicationContextAware接口的设置会在ApplicationContextAwareProcessor类中完成:

    private void invokeAwareInterfaces(Object bean) {
            if (bean instanceof Aware) {
                if (bean instanceof EnvironmentAware) {
                    ((EnvironmentAware)bean).setEnvironment(this.applicationContext.getEnvironment());
                }
    
                if (bean instanceof EmbeddedValueResolverAware) {
                    ((EmbeddedValueResolverAware)bean).setEmbeddedValueResolver(this.embeddedValueResolver);
                }
    
                if (bean instanceof ResourceLoaderAware) {
                    ((ResourceLoaderAware)bean).setResourceLoader(this.applicationContext);
                }
    
                if (bean instanceof ApplicationEventPublisherAware) {
                    ((ApplicationEventPublisherAware)bean).setApplicationEventPublisher(this.applicationContext);
                }
    
                if (bean instanceof MessageSourceAware) {
                    ((MessageSourceAware)bean).setMessageSource(this.applicationContext);
                }
    
                if (bean instanceof ApplicationContextAware) {
                    ((ApplicationContextAware)bean).setApplicationContext(this.applicationContext);
                }
            }
    
        }
    

    通过这种方式保证了ApplicationContextAware和BeanFactoryAware中的容器保证是生成该bean的容器。

    BeanDefinitions

    AbstractXmlApplicationContext.loadBeanDefinitions

    protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
            // Create a new XmlBeanDefinitionReader for the given BeanFactory.
        // 创建Bean读取器,并通过回调设置到容器中,容器使用该读取器
            XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    
            // Configure the bean definition reader with this context's
            // resource loading environment.
        // 为Bean读取器设置环境变量
            beanDefinitionReader.setEnvironment(this.getEnvironment());
        // 设置Spring资源加载器
            beanDefinitionReader.setResourceLoader(this);
        // 设置SAX xml解析器
            beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    
            // Allow a subclass to provide custom initialization of the reader,
            // then proceed with actually loading the bean definitions.
            initBeanDefinitionReader(beanDefinitionReader);
            loadBeanDefinitions(beanDefinitionReader);
        }
    

    最终调用了XmlBeanDefinitionReader.loadBeanDefinitions

    public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
      ...
        var5 = this.doLoadBeanDefinitions(inputSource, encodedResource.getResource());
      ...
    }
    
        protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
            try {
                // 调用doLoadDocument方法将资源文件转换为Document实例
                Document doc = this.doLoadDocument(inputSource, resource);
                // 调用registerBeanDefinitions方法提取并注册bean
                return this.registerBeanDefinitions(doc, resource);
            } catch (BeanDefinitionStoreException var4) {
                throw var4;
            } catch (SAXParseException var5) {
                throw new XmlBeanDefinitionStoreException(resource.getDescription(), "Line " + var5.getLineNumber() + " in XML document from " + resource + " is invalid", var5);
            } catch (SAXException var6) {
                throw new XmlBeanDefinitionStoreException(resource.getDescription(), "XML document from " + resource + " is invalid", var6);
            } catch (ParserConfigurationException var7) {
                throw new BeanDefinitionStoreException(resource.getDescription(), "Parser configuration exception parsing XML from " + resource, var7);
            } catch (IOException var8) {
                throw new BeanDefinitionStoreException(resource.getDescription(), "IOException parsing XML document from " + resource, var8);
            } catch (Throwable var9) {
                throw new BeanDefinitionStoreException(resource.getDescription(), "Unexpected exception parsing XML document from " + resource, var9);
            }
        }
    

    继续向下走会发现调用了DefaultBeanDefinitionDocumentReader的doRegisterBeanDefinitions方法完成了bean的解析及注册。

    protected void doRegisterBeanDefinitions(Element root) {
            BeanDefinitionParserDelegate parent = this.delegate;
            this.delegate = this.createDelegate(this.getReaderContext(), root, parent);
            if (this.delegate.isDefaultNamespace(root)) {
                String profileSpec = root.getAttribute("profile");
                if (StringUtils.hasText(profileSpec)) {
                    String[] specifiedProfiles = StringUtils.tokenizeToStringArray(profileSpec, ",; ");
                    if (!this.getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) {
                        if (this.logger.isInfoEnabled()) {
                            this.logger.info("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + this.getReaderContext().getResource());
                        }
    
                        return;
                    }
                }
            }
    
            // 解析前处理,子类实现
            this.preProcessXml(root);
            this.parseBeanDefinitions(root, this.delegate);
            // 解析后处理,子类实现
            this.postProcessXml(root);
            this.delegate = parent;
        }
    

    类的结构层次图

    XmlBeanFactory继承自DefaultListableBeanFactory,而DefaultListableBeanFactory是整个bean加载的核心部分,是Spring注册及加载bean的默认实现

    长文预警!Spring源码之IoC容器的基本实现

    XML配置文件的读取是Spring中重要的功能,因为Spring的大部分功能都是以配置作为切入点的,可以从XmlBeanDefinitionReader中梳理一下资源文件读取、解析及注册的大致脉络

    长文预警!Spring源码之IoC容器的基本实现

    来源:https://www.tuicool.com/articles/VfeqAbv

    相关文章

      网友评论

        本文标题:长文预警!Spring源码之IoC容器的基本实现

        本文链接:https://www.haomeiwen.com/subject/qasbvktx.html