Spring原理学习系列之五:IOC原理之Bean加载

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: 其实很多同学都想通过阅读框架的源码以汲取框架设计思想以及编程营养,Spring框架其实就是个很好的框架源码学习对象。我们都知道Bean是Spring框架的最小操作单元,Spring框架通过对于Bean的统一管理实现其IOC以及AOP等核心的框架功能,那么Spring框架是如何把Bean加载到环境中来进行管理的呢?本文将围绕这个话题进行详细的阐述,并配合Spring框架的源码解析。

引言

其实很多同学都想通过阅读框架的源码以汲取框架设计思想以及编程营养,Spring框架其实就是个很好的框架源码学习对象。我们都知道BeanSpring框架的最小操作单元,Spring框架通过对于Bean的统一管理实现其IOC以及AOP等核心的框架功能,那么Spring框架是如何把Bean加载到环境中来进行管理的呢?本文将围绕这个话题进行详细的阐述,并配合Spring框架的源码解析。

  • Bean创建
  • Bean加载流程
  • 总结


一、Bean创建

在搞清楚Spring框架如何加载Bean之前,我们需要明确Spring框架中的Bean是如何被创建的。那我们就不得不谈到BeanFactory。本文采用的AnnotationConfigApplicationContext来进行代码示例说明,它的类继承与实现结构如下所示。由图可知,BeanFactory是作为顶级接口的形式存在。

image.png

示例代码如下:

public class SpringTest {
    public static void main(String[] args) {
        ApplicationContext ac = new AnnotationConfigApplicationContext(UserServiceImpl.class);
        IUserService userService = (UserServiceImpl)ac.getBean("userService");
        //3.执行bean中的方法
        userService.query();
    }
}

如示例代码所示,在new AnnotationConfigApplicationContext(UserService.class)创建context过程中,实际调用了AnnotationConfigApplicationContext的构造方法,其重载的构造方法如下所示:

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {
...
  public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
    this();
    register(annotatedClasses);
    refresh();
  }
...
}

该构造方法主要完成三件事情:

1、this():初始化bean读取器以及扫描器,对应AnnotationConfigApplicationContext的无参构造函数,如下代码:

public AnnotationConfigApplicationContext() {
    //在IOC容器中初始化一个 注解bean读取器AnnotatedBeanDefinitionReader
    this.reader = new AnnotatedBeanDefinitionReader(this);
    //在IOC容器中初始化一个 按类路径扫描注解bean的扫描器
    this.scanner = new ClassPathBeanDefinitionScanner(this);
  }

2、register(annotatedClasses):进行Bean配置类注册,如下代码:

public void register(Class... annotatedClasses) {
        Assert.notEmpty(annotatedClasses, "At least one annotated class must be specified");
        this.reader.register(annotatedClasses);
    }

AnnotationConfigApplicationContext上下文中,通过AnnotatedBeanDefinitionReader调用register进行注解Bean的读取与注册操作。

public void register(Class<?>... annotatedClasses) {
    for (Class<?> annotatedClass : annotatedClasses) {
      registerBean(annotatedClass);
    }
  }
public void registerBean(Class<?> annotatedClass) {
    doRegisterBean(annotatedClass, null, null, null);
  }
<T> void doRegisterBean(Class<T> annotatedClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
      @Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
    //将注解类的数据格式转换为Bean容器中的AnnotatedGenericBeanDefinition
    AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);
    //@Conditional装配条件判断是否需要跳过注册
    if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
      return;
    }
    //设置回调
    abd.setInstanceSupplier(instanceSupplier);
     //解析bean作用域(单例或者原型),如果有@Scope注解,则解析@Scope,没有则默认为singleton 
    ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
    //作用域写回BeanDefinition数据结构, abd中缺损的情况下为空,将默认值singleton重新赋值到abd
    abd.setScope(scopeMetadata.getScopeName());
    //生成bean配置类beanName
    String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
    //通用注解解析到abd结构中,主要是处理Lazy, primary DependsOn, Role ,Description这五个注解
    AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
    // @Qualifier特殊限定符处理
    if (qualifiers != null) {
      for (Class<? extends Annotation> qualifier : qualifiers) {
        if (Primary.class == qualifier) {
        // 如果配置@Primary注解,则设置当前Bean为自动装配autowire时首选bean 
          abd.setPrimary(true);
        }
        else if (Lazy.class == qualifier) {
        //设置当前bean为延迟加载
          abd.setLazyInit(true);
        }
        else {
          abd.addQualifier(new AutowireCandidateQualifier(qualifier));
        }
      }
    }
    for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
      customizer.customize(abd);
    }
    BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
    definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
    //按照名称将对应的bean注册到IOC容器中(在下一篇文章中进行介绍)
    BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
  }

3、refresh():作用为刷新Spring的应用上下文;

刷新上下文的操作实际上是在AbstractApplicationContext中完成的。

public abstract class AbstractApplicationContext extends DefaultResourceLoader
    implements ConfigurableApplicationContext {
    ...
  @Override
  public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
      // 准备上下文环境
      prepareRefresh();
      // 创建并初始化BeanFactory(这部分可以继续追踪,创建BeanFactory的过程)
      ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      // 填充BeanFactory功能
      prepareBeanFactory(beanFactory);
      try {
        // 提供子类覆盖的额外处理,即子类处理自定义的BeanFactoryPostProcess
        postProcessBeanFactory(beanFactory);
        // 激活各种BeanFactory处理器
        invokeBeanFactoryPostProcessors(beanFactory);
         // 注册拦截Bean创建的Bean处理器,即注册 BeanPostProcessor
        registerBeanPostProcessors(beanFactory);
         // 初始化上下文中的资源文件,如国际化文件的处理等
        initMessageSource();
        // 初始化上下文事件广播器
        initApplicationEventMulticaster();
         // 给子类扩展初始化其他Bean
        onRefresh();
        // 在所有bean中查找listener bean,然后注册到广播器中
        registerListeners();
        // 初始化剩下的单例Bean(非延迟加载的)
        finishBeanFactoryInitialization(beanFactory);
        // 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
        finishRefresh();
      }
      catch (BeansException ex) {
        if (logger.isWarnEnabled()) {
          logger.warn("Exception encountered during context initialization - " +
              "cancelling refresh attempt: " + ex);
        }
        //销毁已经创建的Bean
        destroyBeans();
        //重置容器激活标签
        cancelRefresh(ex);
        // 异常抛出
        throw ex;
      }
      finally {
        // Reset common introspection caches in Spring's core, since we
        // might not ever need metadata for singleton beans anymore...
        resetCommonCaches();
      }
    }
  }
  ...

image.png

二、Bean加载

image.png

(图片来自于网络)

AnnotationConfigApplicationContext继承了AbstractApplicationContext

ublic abstract class AbstractApplicationContext extends DefaultResourceLoader
    implements ConfigurableApplicationContext {
    ...
    @Override
    public Object getBean(String name) throws BeansException {
      assertBeanFactoryActive();
      return getBeanFactory().getBean(name);
  }
    ...
}

BeanFactory接口主要的实现是在AbstractBeanFactory中完成,doGetBean接收以下四个参数:

(1)name:准备获取 bean 的名字

(2)requiredType:准备获取 bean 的类型

(3)args:创建 bean 时传递的参数。这个参数仅限于创建 bean 时使用

(4)typeCheckOnly:是否为类型检查

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
...
  @Override
  public Object getBean(String name) throws BeansException {
    return doGetBean(name, null, null, false);
  }
protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
    //转换Bean名称
    final String beanName = transformedBeanName(name);
    Object bean;
    // 从缓存中或者实例工厂中获取 bean
    Object sharedInstance = getSingleton(beanName);
    if (sharedInstance != null && args == null) {
      if (logger.isTraceEnabled()) {
        if (isSingletonCurrentlyInCreation(beanName)) {
          logger.trace("Returning eagerly cached instance of singleton bean '" + beanName +
              "' that is not fully initialized yet - a consequence of a circular reference");
        }
        else {
          logger.trace("Returning cached instance of singleton bean '" + beanName + "'");
        }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
    }
    else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      if (isPrototypeCurrentlyInCreation(beanName)) {
        throw new BeanCurrentlyInCreationException(beanName);
      }
      // 如果容器中没有找到,则从父类容器中加载
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
        // Not found -> check parent.
        String nameToLookup = originalBeanName(name);
        if (parentBeanFactory instanceof AbstractBeanFactory) {
          return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
              nameToLookup, requiredType, args, typeCheckOnly);
        }
        else if (args != null) {
          // Delegation to parent with explicit args.
          return (T) parentBeanFactory.getBean(nameToLookup, args);
        }
        else if (requiredType != null) {
          // No args -> delegate to standard getBean method.
          return parentBeanFactory.getBean(nameToLookup, requiredType);
        }
        else {
          return (T) parentBeanFactory.getBean(nameToLookup);
        }
      }
      if (!typeCheckOnly) {
        markBeanAsCreated(beanName);
      }
      try {
        final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
        checkMergedBeanDefinition(mbd, beanName, args);
        // 获取依赖的 Bean
        String[] dependsOn = mbd.getDependsOn();
        if (dependsOn != null) {
          for (String dep : dependsOn) {
            if (isDependent(beanName, dep)) {
              throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
            }
            registerDependentBean(dep, beanName);
            try {
              getBean(dep);
            }
            catch (NoSuchBeanDefinitionException ex) {
              throw new BeanCreationException(mbd.getResourceDescription(), beanName,
                  "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
            }
          }
        }
        // 开始创建 Bean 实例,如果是单例的,那么会创建一个单例的匿名工厂,如果是原型模式的,则不需要创建单例的工厂的,其他的如 request、session 作用域的,则根据自身的需要
        if (mbd.isSingleton()) {
          sharedInstance = getSingleton(beanName, () -> {
            try {
              return createBean(beanName, mbd, args);
            }
            catch (BeansException ex) {
              // Explicitly remove instance from singleton cache: It might have been put there
              // eagerly by the creation process, to allow for circular reference resolution.
              // Also remove any beans that received a temporary reference to the bean.
              destroySingleton(beanName);
              throw ex;
            }
          });
          bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
        }
        else if (mbd.isPrototype()) {
          // It's a prototype -> create a new instance.
          Object prototypeInstance = null;
          try {
            beforePrototypeCreation(beanName);
            prototypeInstance = createBean(beanName, mbd, args);
          }
          finally {
            afterPrototypeCreation(beanName);
          }
          bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
        }
        else {
          String scopeName = mbd.getScope();
          final Scope scope = this.scopes.get(scopeName);
          if (scope == null) {
            throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
          }
          try {
            Object scopedInstance = scope.get(beanName, () -> {
              beforePrototypeCreation(beanName);
              try {
                return createBean(beanName, mbd, args);
              }
              finally {
                afterPrototypeCreation(beanName);
              }
            });
            bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
          }
          catch (IllegalStateException ex) {
            throw new BeanCreationException(beanName,
                "Scope '" + scopeName + "' is not active for the current thread; consider " +
                "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
                ex);
          }
        }
      }
      catch (BeansException ex) {
        cleanupAfterBeanCreationFailure(beanName);
        throw ex;
      }
    }
    // 类型检查,如果不能进行类型转换,则抛出异常
    if (requiredType != null && !requiredType.isInstance(bean)) {
      try {
        T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
        if (convertedBean == null) {
          throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
        }
        return convertedBean;
      }
      catch (TypeMismatchException ex) {
        if (logger.isTraceEnabled()) {
          logger.trace("Failed to convert bean '" + name + "' to required type '" +
              ClassUtils.getQualifiedName(requiredType) + "'", ex);
        }
        throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
    }
    return (T) bean;
  }
...
}

三、总结

Spring IOC还有很多内容,本文只是管中窥豹,其他的部分将继续在后续的文章中进行详细介绍。

相关文章
|
3天前
|
XML Java 开发者
Spring Boot开箱即用可插拔实现过程演练与原理剖析
【11月更文挑战第20天】Spring Boot是一个基于Spring框架的项目,其设计目的是简化Spring应用的初始搭建以及开发过程。Spring Boot通过提供约定优于配置的理念,减少了大量的XML配置和手动设置,使得开发者能够更专注于业务逻辑的实现。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,为开发者提供一个全面的理解。
10 0
|
9天前
|
前端开发 Java 开发者
Spring生态学习路径与源码深度探讨
【11月更文挑战第13天】Spring框架作为Java企业级开发中的核心框架,其丰富的生态系统和强大的功能吸引了无数开发者的关注。学习Spring生态不仅仅是掌握Spring Framework本身,更需要深入理解其周边组件和工具,以及源码的底层实现逻辑。本文将从Spring生态的学习路径入手,详细探讨如何系统地学习Spring,并深入解析各个重点的底层实现逻辑。
35 9
|
18天前
|
XML 缓存 Java
搞透 IOC、Spring IOC ,看这篇就够了!
本文详细解析了Spring框架的核心内容——IOC(控制反转)及其依赖注入(DI)的实现原理,帮助读者理解如何通过IOC实现组件解耦,提高程序的灵活性和可维护性。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
|
29天前
|
前端开发 Java 数据库
SpringBoot学习
【10月更文挑战第7天】Spring学习
34 9
|
30天前
|
XML Java 数据格式
Spring学习
【10月更文挑战第6天】Spring学习
19 1
|
9天前
|
安全 Java 测试技术
Java开发必读,谈谈对Spring IOC与AOP的理解
Spring的IOC和AOP机制通过依赖注入和横切关注点的分离,大大提高了代码的模块化和可维护性。IOC使得对象的创建和管理变得灵活可控,降低了对象之间的耦合度;AOP则通过动态代理机制实现了横切关注点的集中管理,减少了重复代码。理解和掌握这两个核心概念,是高效使用Spring框架的关键。希望本文对你深入理解Spring的IOC和AOP有所帮助。
22 0
|
1月前
|
Java 测试技术 开发者
springboot学习四:Spring Boot profile多环境配置、devtools热部署
这篇文章主要介绍了如何在Spring Boot中进行多环境配置以及如何整合DevTools实现热部署,以提高开发效率。
56 2
|
1月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
84 1
|
30天前
|
XML Java 数据格式
Spring IOC容器的深度解析及实战应用
【10月更文挑战第14天】在软件工程中,随着系统规模的扩大,对象间的依赖关系变得越来越复杂,这导致了系统的高耦合度,增加了开发和维护的难度。为解决这一问题,Michael Mattson在1996年提出了IOC(Inversion of Control,控制反转)理论,旨在降低对象间的耦合度,提高系统的灵活性和可维护性。Spring框架正是基于这一理论,通过IOC容器实现了对象间的依赖注入和生命周期管理。
65 0
|
1月前
|
Java Spring
springboot 学习十一:Spring Boot 优雅的集成 Lombok
这篇文章是关于如何在Spring Boot项目中集成Lombok,以简化JavaBean的编写,避免冗余代码,并提供了相关的配置步骤和常用注解的介绍。
90 0