Spring之Ioc容器

279 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第11天,点击查看活动详情

前言

Spring’s Inversion of Control (IoC) container 简称IoC容器,也称为dependency injection (DI) 依赖注入,是Spring最核心的关键技术之一。

一、对Spring Ioc的理解

Spring IoC是对Bean的全生命周期管理的容器,包括bean创建,依赖注入,销毁。他是Spring技术生态的底座,所有的Spring技术在Ioc容器之上进行扩展实现。

beans和context包是ioc容器的基础,BeanFactory支持配置任意类型的对象。ApplicationContext是BeanFactory的子接口,它更易于集成aop,支持国际化,事件发布,以及应用层特定的上下文,如用于web应用程序的WebApplicationContext。 

二、Spring IoC需要提供的功能

1、 获取不同来源bean的资源

2、 加载读取bean资源

3、 解析资源bean的定义信息

4、 根据bean的定义信息进行实例化,并存储 

三、Spring IoC启动流程

在AbstractApplicationContext类中,spring使用了模版模式来将Spring ioc容器初始化进行了定义。 

public abstract class AbstractApplicationContext extends DefaultResourceLoader
        implements ConfigurableApplicationContext {
        // ...
@Override
    public void refresh() throws BeansException, IllegalStateException {
        synchronized (this.startupShutdownMonitor) {
            // Prepare this context for refreshing.
            prepareRefresh();
​
            // Tell the subclass to refresh the internal bean factory.
            ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
​
            // Prepare the bean factory for use in this context.
            prepareBeanFactory(beanFactory);
​
            try {
                // Allows post-processing of the bean factory in context subclasses.
                postProcessBeanFactory(beanFactory);
​
                // Invoke factory processors registered as beans in the context.
                invokeBeanFactoryPostProcessors(beanFactory);
​
                // Register bean processors that intercept bean creation.
                registerBeanPostProcessors(beanFactory);
​
                // Initialize message source for this context.
                initMessageSource();
​
                // Initialize event multicaster for this context.
                initApplicationEventMulticaster();
​
                // Initialize other special beans in specific context subclasses.
                onRefresh();
​
                // Check for listener beans and register them.
                registerListeners();
​
                // Instantiate all remaining (non-lazy-init) singletons.
                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();
            }
        }
    }
 // ...    
}

流程说明:

首先是资源定位:ioc容器需要统一创建对象,创建对象的类信息,属性信息需要从不同配置文件中解析出来,所以需要查找类的配置文件。这就是资源定位。

spring设计了如下接口和类用来定位资源路径,通过一个字符串路径获取资源加载器。字符串路径可以是类路径下的文件,网络Url,文件系统等等。其中PathMatchingResourcePatternResolver通过持有ResourceLoader的引用,可以根据不同的资源加载器加载不同位置的资源。 

加载资源

//资源加载处理器 **此处使用了策略模式** 持有了ResourceResolver的引用,根据不同类型的ResourceResolver来实现加载不同位置的资源
public class PathMatchingResourcePatternResolver implements ResourcePatternResolver {
​
//通过构造函数持有ResourceLoader引用
public PathMatchingResourcePatternResolver(ResourceLoader resourceLoader) {
        Assert.notNull(resourceLoader, "ResourceLoader must not be null");
        this.resourceLoader = resourceLoader;
    }
    
    
    //通过不同的ResourceLoader来加载资源
    @Override
    public Resource[] getResources(String locationPattern) throws IOException {
        Assert.notNull(locationPattern, "Location pattern must not be null");
        if (locationPattern.startsWith(CLASSPATH_ALL_URL_PREFIX)) {
            // a class path resource (multiple resources for same name possible)
            if (getPathMatcher().isPattern(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()))) {
                // a class path resource pattern
                return findPathMatchingResources(locationPattern);
            }
            else {
                // all class path resources with the given name
                return findAllClassPathResources(locationPattern.substring(CLASSPATH_ALL_URL_PREFIX.length()));
            }
        }
        else {
            // Generally only look for a pattern after a prefix here,
            // and on Tomcat only after the "*/" separator for its "war:" protocol.
            int prefixEnd = (locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 :
                    locationPattern.indexOf(':') + 1);
            if (getPathMatcher().isPattern(locationPattern.substring(prefixEnd))) {
                // a file pattern
                return findPathMatchingResources(locationPattern);
            }
            else {
                // a single resource with the given name
                return new Resource[] {getResourceLoader().getResource(locationPattern)};
            }
        }
    }
​
}

不同资源类继承图 

包含本地文件,类路径资源,字节数组等

第2点是资源加载,读取到了资源文件,需要将资源文件转换成BeanDefinition

1、先获取类信息资源字节流对象

2、加载资源字节流对象成org.w3c.dom.Document对象,在Spring中通过jdk的SAX的xml解析技术,类图如下

3、在DefaultBeanDefinitionDocumentReader类中,读取Document对象里所有xml节点,委派给BeanDefinitionParserDelegate类进行解析,使用了委派设计模式 

public class DefaultBeanDefinitionDocumentReader implements BeanDefinitionDocumentReader {    protected void doRegisterBeanDefinitions(Element root) {        BeanDefinitionParserDelegate parent = this.delegate;        this.delegate = createDelegate(getReaderContext(), root, parent);​        // ....        //解析前处理        preProcessXml(root);       //解析bean定义        parseBeanDefinitions(root, this.delegate);        //解析后处理        postProcessXml(root);        //解析        this.delegate = parent;    }}

4、parseBeanDefinitions()方法进行具体解析

先解析bean标签属性,如name,id,class,参数等等,然后构造GenericBeanDefinition对象,并填充属性,这就是bean实例化的原始配方。解析主要有两种情况,

 第一种是spring默认支持的标签。 

//构造GenericBeanDefinition定义public static AbstractBeanDefinition createBeanDefinition(      @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException {​    GenericBeanDefinition bd = new GenericBeanDefinition();    bd.setParentName(parentName);    if (className != null) {      if (classLoader != null) {        bd.setBeanClass(ClassUtils.forName(className, classLoader));      }      else {        bd.setBeanClassName(className);      }    }    return bd;  }

对于应用程序自定义的标签,Spring支持灵活定制,方便集成其他组件到spring中。用户可以将自己的解析器注册器放到META-INF/spring.handlers文件中,同时用户还需要自定义xsd文件和META-INF/spring.schemas文件,在tx模块和dubbo中都是用到了该扩展点。 在加载自定义标签的时候,spring将会加载到具体解析器。具体解析过程中,

通过org.springframework.beans.factory.xml.BeanDefinitionParserDelegate#parseCustomElement()方法进行管理解析流程。委派给NamespaceHandlerSupport进行解析,NamespaceHandlerSupport类持有了所有的解析器,由它委派给具体的解析器进行解析。

下面是自定义标签解析器相关类图。 

解析标签流程图 

5、第5步,将生成的GenericBeanDefinition对象,注册到容器中, 

public abstract class BeanDefinitionReaderUtils {    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);      }    }  }}

注册beandefinition具体过程包括,校验,判断是否可以覆盖,存入到 

public class DefaultListableBeanFactory extends AbstractAutowireCapableBeanFactory    implements ConfigurableListableBeanFactory, BeanDefinitionRegistry, Serializable {    @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);          List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1);          updatedDefinitions.addAll(this.beanDefinitionNames);          updatedDefinitions.add(beanName);          this.beanDefinitionNames = updatedDefinitions;          removeManualSingletonName(beanName);        }      }      else {        // Still in startup registration phase        this.beanDefinitionMap.put(beanName, beanDefinition);        this.beanDefinitionNames.add(beanName);        removeManualSingletonName(beanName);      }      this.frozenBeanDefinitionNames = null;    }​    if (existingDefinition != null || containsSingleton(beanName)) {      resetBeanDefinition(beanName);    }  }}

6、看的上面,beanDefinition就解析好了。

接下里spring初始化了一系列bean,这些bean比较特殊,他是spring的bean工厂级别的,比如Environment,ClassLoader, post-processors 

public abstract class AbstractApplicationContext extends DefaultResourceLoader    implements ConfigurableApplicationContext {/**   * Configure the factory's standard context characteristics,   * such as the context's ClassLoader and post-processors.   * @param beanFactory the BeanFactory to configure   */  protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {    // Tell the internal bean factory to use the context's class loader etc.    beanFactory.setBeanClassLoader(getClassLoader());    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));​    // Configure the bean factory with context callbacks.        //这个地方是注册容器本身的ApplicationContextAwareProcessor,他的作用是为实现了**Aware接口的bean进行回调赋值。    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);​    // BeanFactory interface not registered as resolvable type in a plain factory.    // MessageSource registered (and found for autowiring) as a bean.    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);    beanFactory.registerResolvableDependency(ResourceLoader.class, this);    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);    beanFactory.registerResolvableDependency(ApplicationContext.class, this);​    // Register early post-processor for detecting inner beans as ApplicationListeners. 检测实现了ApplicationListener接口的bean,并存储下来    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));​    // Detect a LoadTimeWeaver and prepare for weaving, if found.    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));      // Set a temporary ClassLoader for type matching.      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));    }​    // Register default environment beans.环境对象,里面包含系统环境配置    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {      beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());    }    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {        //注册系统属性对象      beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());    }    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {    //注册系统环境对象   beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());    }  }        }

7、上一步初始化了必须的bean,接下来执行postProcessBeanFactory(beanFactory);方法方便不同容器对BeanFactory进行定制。

8、调用invokeBeanFactoryPostProcessors(beanFactory);执行所有的BeanFactoryPostProcessor。对beandefinition进行修改,这个是spring提供的扩展点,用来自定义修改beandefinition的。比如mybatis(将mapper的class统一修改成MapperFactoryBean,将mapper的class作为它的构造参数),dubbo,都使用这个技术来和spring进行集成。

9、Register bean processors that intercep registerBeanPostProcessors(beanFactory); 实例化并注册所有bean的后置处理器,用于拦截bean创建。

10、initMessageSource(); 国际化支持

11、initApplicationEventMulticaster(); 初始化事件多播器

12、onRefresh(); 子类使用,初始化其他资源

13、registerListeners(); 注册事件监听器

在spring容器中,提供了一种事件机制,帮助springbean之间进行通信。

14、finishBeanFactoryInitialization(beanFactory); 初始化bean,并完成依赖注入

15、finishRefresh();完成容器初始化函数

16、最后回收资源,清除缓存,结束初始化