Spring之Ioc容器

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 该文章主要介绍了Spring框架中的IoC(Inversion of Control,控制反转)容器,包括IoC容器的概念、IoC容器在Spring中的实现以及IoC容器的基础包等内容。

前言

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();
            }
        }
    }
 // ...    
}
AI 代码解读

流程说明:

首先是资源定位: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)};
            }
        }
    }
​
}
AI 代码解读

不同资源类继承图

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

第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;    }}
AI 代码解读

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;  }
AI 代码解读

对于应用程序自定义的标签,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);      }    }  }}
AI 代码解读

注册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);    }  }}
AI 代码解读

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());    }  }        }
AI 代码解读

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、最后回收资源,清除缓存,结束初始化

目录
打赏
0
4
4
0
41
分享
相关文章
Spring IOC—基于注解配置和管理Bean 万字详解(通俗易懂)
Spring 第三节 IOC——基于注解配置和管理Bean 万字详解!
130 26
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
123 69
Spring容器的本质
本文主要讨论Spring容器最核心的机制,用最少的代码讲清楚Spring容器的本质。
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
69 21
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
Spring容器Bean之XML配置方式
通过对以上内容的掌握,开发人员可以灵活地使用Spring的XML配置方式来管理应用程序的Bean,提高代码的模块化和可维护性。
84 6
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
85 2
Spring容器中的bean是线程安全的吗?
Spring容器中的bean默认为单例模式,多线程环境下若操作共享成员变量,易引发线程安全问题。Spring未对单例bean做线程安全处理,需开发者自行解决。通常,Spring bean(如Controller、Service、Dao)无状态变化,故多为线程安全。若涉及线程安全问题,可通过编码或设置bean作用域为prototype解决。
56 1
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。

热门文章

最新文章