关于java:Spring源码之IoC容器的基本实现

50次阅读

共计 13682 个字符,预计需要花费 35 分钟才能阅读完成。

Spring 源码之容器的根本实现

概述

Spring 是一个凋谢源代码的设计层面框架,他解决的是业务逻辑层和其余各层的松耦合问题,因而它将面向接口的编程思维贯通整个零碎利用。Spring 是于 2003 年衰亡的一个轻量级的 Java 开发框架,由 Rod Johnson 创立。简略来说,Spring 是一个分层的 JavaSE/EE full-stack(一站式) 轻量级开源框架。

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

Spring 的构造组成

Spring 框架是一个分层架构,它蕴含一系列的性能因素,并被分为大概 20 个模块,如下图所示:

Spring 容器类型

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

Spring BeanFactory 容器: 它是最简略的容器,给 DI 提供了根本的反对 ApplicationContext 容器 :ApplicationContext 容器继承自 BeanFactory, 它包含 BeanFactory 容器的所有性能,所以通常倡议应用。

Spring 容器的类别及其特点

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 的默认实现

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

正文完
 0