Spring之Ioc容器

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 该文章主要介绍了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();
            }
        }
    }
 // ...    
}

流程说明:

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

相关文章
|
1天前
|
Java 测试技术 Windows
咦!Spring容器里为什么没有我需要的Bean?
【10月更文挑战第11天】项目经理给小菜分配了一个紧急需求,小菜迅速搭建了一个SpringBoot项目并完成了开发。然而,启动测试时发现接口404,原因是控制器包不在默认扫描路径下。通过配置`@ComponentScan`的`basePackages`字段,解决了问题。总结:`@SpringBootApplication`默认只扫描当前包下的组件,需要扫描其他包时需配置`@ComponentScan`。
|
1月前
|
XML Java 数据格式
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
Spring 第二节内容补充 关于Bean配置的更多内容和细节 万字详解!
169 18
Spring IOC—基于XML配置Bean的更多内容和细节(通俗易懂)
|
28天前
|
XML Java 测试技术
spring复习01,IOC的思想和第一个spring程序helloWorld
Spring框架中IOC(控制反转)的思想和实现,通过一个简单的例子展示了如何通过IOC容器管理对象依赖,从而提高代码的灵活性和可维护性。
spring复习01,IOC的思想和第一个spring程序helloWorld
|
19天前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
66 9
|
16天前
|
存储 开发框架 Java
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
文章详细介绍了Spring、IOC、DI的概念和关系,解释了控制反转(IOC)和依赖注入(DI)的原理,并提供了IOC的代码示例,阐述了Spring框架作为IOC容器的应用。
17 0
什么是Spring?什么是IOC?什么是DI?IOC和DI的关系? —— 零基础可无压力学习,带源码
|
25天前
|
缓存 Java Spring
手写Spring Ioc 循环依赖底层源码剖析
在Spring框架中,IoC(控制反转)是一个核心特性,它通过依赖注入(DI)实现了对象间的解耦。然而,在实际开发中,循环依赖是一个常见的问题。
33 4
|
3天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
14 0
|
29天前
|
XML Java 开发者
经典面试---spring IOC容器的核心实现原理
作为一名拥有十年研发经验的工程师,对Spring框架尤其是其IOC(Inversion of Control,控制反转)容器的核心实现原理有着深入的理解。
75 3
|
11天前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
28 0
|
2月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)