【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(一)(中)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【小家Spring】Spring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(一)(中)

request上下文是如何跟线程上下文绑定的,参考原理:【小家Spring】Spring MVC执行流程 FrameworkServlet、DispatcherServlet源码分析(processRequest、doDispatch)


小伙伴们以后要使用Servlet源生对象,不要只知道从方法入参里注入,看起来很不优雅,很不Spring MVC化,懂了这些原理来龙去脉后,相信可以放心的、安全的使用了~

从这方面可以看出,我们不得不佩服Spring的考虑周到。一个优秀的框架,绝对不仅仅是实现了某个功能而已,而是能让你用得舒服,用得爽 ,可扩展性非常强等一些附加属性~~~~比如下面的web容器AbstractRefreshableWebApplicationContext就会设置一些初始值:

  @Override
  protected void initPropertySources() {
    ConfigurableEnvironment env = getEnvironment();
    // 初始化web环境中的初始值(比如init等,这个后续会说)
    if (env instanceof ConfigurableWebEnvironment) {
      ((ConfigurableWebEnvironment) env).initPropertySources(this.servletContext, null);
    }
  }
    @Override
  public ConfigurableEnvironment getEnvironment() {
    if (this.environment == null) {
      this.environment = createEnvironment();
    }
    return this.environment;
  }
  // 这个方法需要注意一下,默认是StandardEnvironment,但是它是protected方法设计,所以之类可以重写(如下图)
  protected ConfigurableEnvironment createEnvironment() {
    return new StandardEnvironment();
  }
//StandardServletEnvironment.initPropertySources(this.servletContext, null)
  @Override
  public void initPropertySources(@Nullable ServletContext servletContext, @Nullable ServletConfig servletConfig) {
  // 将 servletContext、servletConfig 添加到 propertySources里(ServletConfigPropertySource)
  //ServletConfigPropertySource作用:reads init parameters from ServletConfig
    WebApplicationContextUtils.initServletPropertySources(getPropertySources(), servletContext, servletConfig);
  }


image.png


它们都是web环境下的上下文,因此重写的方式也是一模一样:


  //StandardServletEnvironment是web环境上下文里的Enviroment
  @Override
  protected ConfigurableEnvironment createEnvironment() {
    return new StandardServletEnvironment();
  }


refresh() 第二步:ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory()


实际上就是重新创建一个bean工厂,并销毁原工厂。主要工作是创建DefaultListableBeanFactory实例,解析配置文件,注册Bean的定义信息

  protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
    refreshBeanFactory();
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    if (logger.isDebugEnabled()) {
      logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
    }
    return beanFactory;
  }


在spring中,基本上各司其职,每个类都有每个类的作用。其中refreshBeanFactory()是具体的刷新BeanFactory,负责这个工作做在类AbstractRefreshableApplicationContext#refreshBeanFactory中,顾名思义这是专门用来刷新的:

  @Override
  protected final void refreshBeanFactory() throws BeansException {
    // 判断是否已经存在BeanFactory,存在则销毁所有Beans,并且关闭BeanFactory
    // 避免重复加载BeanFactory
    if (hasBeanFactory()) {
      destroyBeans();
      closeBeanFactory();
    }
    try {
      // 创建具体的beanFactory,这里创建的是DefaultListableBeanFactory,最重要的beanFactory spring注册及加载bean就靠它
      // createBeanFactory()这个方法,看下面,还有得说的
      DefaultListableBeanFactory beanFactory = createBeanFactory();
      beanFactory.setSerializationId(getId());
      // 这句比较简单,就是把当前旧容器的一些配置值复制给新容器 
      // allowBeanDefinitionOverriding属性是指是否允对一个名字相同但definition不同进行重新注册,默认是true。
      // allowCircularReferences属性是指是否允许Bean之间循环引用,默认是true.
      // 这两个属性值初始值为空:复写此方法即可customizeBeanFactory
      customizeBeanFactory(beanFactory);
      // 这个就是最重要的了,加载所有的Bean配置信息,具体如下详细解释
      // 它属于模版方法,由子类去实现加载的方式
      loadBeanDefinitions(beanFactory);
      synchronized (this.beanFactoryMonitor) {
        this.beanFactory = beanFactory;
      }
    }
    catch (IOException ex) {
      throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    }
  }


createBeanFactory()


  // 创建的时候就是new了一个工厂:DefaultListableBeanFactory   这个时候工厂里面所有东西都是默认值,很多还没有完成初始化属性的设置呢
  protected DefaultListableBeanFactory createBeanFactory() {
    return new DefaultListableBeanFactory(getInternalParentBeanFactory());
  }
  // 给设置父的BeanFactory,若存在的话
  public AbstractAutowireCapableBeanFactory(@Nullable BeanFactory parentBeanFactory) {
    this();
    setParentBeanFactory(parentBeanFactory);
  }
  // 父类空构造器有这么些语句
  public AbstractAutowireCapableBeanFactory() {
    super();
    // 这里是重点。忽略自动装配。这里指定的都是接口。什么意思呢?
    // ignoreDependencyInterface的真正意思是在自动装配时忽略指定接口的实现类中,对外的依赖。(这里面注意:@Autowired和它的关系,其实是有坑的,后续会专门讲解这个坑)
    ignoreDependencyInterface(BeanNameAware.class);
    ignoreDependencyInterface(BeanFactoryAware.class);
    ignoreDependencyInterface(BeanClassLoaderAware.class);
  }
  //找到父的,若存在就返回 若存在父容器就存在父的BeanFactory
  @Nullable
  protected BeanFactory getInternalParentBeanFactory() {
    return (getParent() instanceof ConfigurableApplicationContext) ?
        ((ConfigurableApplicationContext) getParent()).getBeanFactory() : getParent();
  }


image.png


AnnotationConfigWebApplicationContext#loadBeanDefinitions()方法,加载Bean的定义 (XmlWebApplicationContext的实现不一样,因为它是加载xml配置文件)


  @Override
  protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
    // 初始化这个脚手架 其实就是直接new出实例。具体做的工作,下面有相关博文链接
    AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
    ClassPathBeanDefinitionScanner scanner = getClassPathBeanDefinitionScanner(beanFactory);
    // 生成Bean的名称的生成器,如果自己没有setBeanNameGenerator(可以自定义),这里目前为null
    BeanNameGenerator beanNameGenerator = getBeanNameGenerator();
    if (beanNameGenerator != null) {
      reader.setBeanNameGenerator(beanNameGenerator);
      scanner.setBeanNameGenerator(beanNameGenerator);
      //若我们注册了beanName生成器,那么就会注册进容器里面
      beanFactory.registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, beanNameGenerator);
    }
    //这是给reader和scanner注册scope的解析器  此处为null
    ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();
    if (scopeMetadataResolver != null) {
      reader.setScopeMetadataResolver(scopeMetadataResolver);
      scanner.setScopeMetadataResolver(scopeMetadataResolver);
    }
    // 此处注意了:annotatedClasses和basePackages一般是选其一(当然看到此处,他们是可以并存的)
    //我们可以自己指定annotatedClasses 配置文件,同时也可以交给下面扫描
    if (!this.annotatedClasses.isEmpty()) {
      // 这里会把所有的配置文件输出=======info日志  请注意观察控制台
      if (logger.isInfoEnabled()) {
        logger.info("Registering annotated classes: [" +
            StringUtils.collectionToCommaDelimitedString(this.annotatedClasses) + "]");
      }
      // 若是指明的Bean,就交给reader去处理,至于怎么处理,见上篇博文的doRegisterBean去怎么解析每一个Config Bean的
      reader.register(ClassUtils.toClassArray(this.annotatedClasses));
    }
    // 也可以是包扫描的方式,扫描配置文件的Bean
    if (!this.basePackages.isEmpty()) {
      // 输出对应的info日志
      if (logger.isInfoEnabled()) {
        logger.info("Scanning base packages: [" +
            StringUtils.collectionToCommaDelimitedString(this.basePackages) + "]");
      }
      // 这里重要了,scan方法具体做了什么事,上篇博文也有详细的介绍,请参阅
      scanner.scan(StringUtils.toStringArray(this.basePackages));
    }
    // 此处的意思是,也可以以全类名的形式注册。比如可以调用setConfigLocations设置(这在xml配置中使用较多)  可以是全类名,也可以是包路径
    String[] configLocations = getConfigLocations();
    if (configLocations != null) {
      for (String configLocation : configLocations) {
        try {
          Class<?> clazz = ClassUtils.forName(configLocation, getClassLoader());
          reader.register(clazz);
        } catch (ClassNotFoundException ex) {
          // 发现不是全类名,那就当作包扫描吧
          int count = scanner.scan(configLocation);
        }
      }
    }
  }


最开始涉及到两个类的初始化,请参考博文:

【小家Spring】Spring容器加载Bean定义信息的两员大将:AnnotatedBeanDefinitionReader和ClassPathBeanDefinitionScanner


有了这篇博文解释这两个类先行,理解上面loadBeanDefinitions就简单太多了。至此,整个ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();这个步骤全部结束。

现在BeanFactory已经创建了,并且Config配置文件的Bean定义已经注册完成了**(备注:其它单例Bean是还没有解析的~~~~)**

显然,下面的步骤大都把BeanFactory传进去了,都是基于此Bean工厂的了~~~


refresh() 第三步:prepareBeanFactory(beanFactory)


这个方法是配置工厂的标准上下文特征


  protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 设置beanFactory的classLoader为当前context的classLoader
    beanFactory.setBeanClassLoader(getClassLoader());
    // 设置EL表达式解析器(Bean初始化完成后填充属性时会用到)
    // spring3增加了表达式语言的支持,默认可以使用#{bean.xxx}的形式来调用相关属性值
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    // 设置属性注册解析器PropertyEditor 这个主要是对bean的属性等设置管理的一个工具
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    // 将当前的ApplicationContext对象交给ApplicationContextAwareProcessor类来处理,从而在Aware接口实现类中的注入applicationContext等等
    // 添加了一个处理aware相关接口的beanPostProcessor扩展,主要是使用beanPostProcessor的postProcessBeforeInitialization()前置处理方法实现aware相关接口的功能
    // 类似的还有ResourceLoaderAware、ServletContextAware等等等等
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    // 下面是忽略的自动装配(也就是实现了这些接口的Bean,不要Autowired自动装配了)
    // 默认只有BeanFactoryAware被忽略,所以其它的需要自行设置
    // 因为ApplicationContextAwareProcessor把这5个接口的实现工作做了(具体你可参见源码) 所以这里就直接忽略掉
    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的类,就注册beanFactory
    //  如果是ResourceLoader、ApplicationEventPublisher、ApplicationContext等等就注入当前对象this(applicationContext对象)
    // 此处registerResolvableDependency()方法注意:它会把他们加入到DefaultListableBeanFactory的resolvableDependencies字段里面缓存这,供后面处理依赖注入的时候使用 DefaultListableBeanFactory#resolveDependency处理依赖关系
    // 这也是为什么我们可以通过依赖注入的方式,直接注入这几个对象比如ApplicationContext可以直接依赖注入
    // 但是需要注意的是:这些Bean,Spring的IOC容器里其实是没有的。beanFactory.getBeanDefinitionNames()和beanFactory.getSingletonNames()都是找不到他们的,所以特别需要理解这一点
    // 至于容器中没有,但是我们还是可以@Autowired直接注入的有哪些,请看下图:
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    // 注册这个Bean的后置处理器:在Bean初始化后检查是否实现了ApplicationListener接口
    // 是则加入当前的applicationContext的applicationListeners列表 这样后面广播事件也就方便了
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    // 检查容器中是否包含名称为loadTimeWeaver的bean,实际上是增加Aspectj的支持
    // AspectJ采用编译期织入、类加载期织入两种方式进行切面的织入
    // 类加载期织入简称为LTW(Load Time Weaving),通过特殊的类加载器来代理JVM默认的类加载器实现
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
      // 添加BEAN后置处理器:LoadTimeWeaverAwareProcessor
          // 在BEAN初始化之前检查BEAN是否实现了LoadTimeWeaverAware接口,
          // 如果是,则进行加载时织入,即静态代理。
      beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
      // Set a temporary ClassLoader for type matching.
      beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
    // 注入一些其它信息的bean,比如environment、systemProperties、SystemEnvironment等
    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());
    }
  }


说个小细节:@Autowiredh和@Qualifier一起是用时,@Qualifier的值需保证容器里一定有,否则启动报错


image.png


这部分比较简单,就是设置一些标准的值,解释都放代码里了。

附:IOC容器中没有Bean,但是我们还是可以依赖注入的Bean如下(resolvableDependencies):


image.png



相关文章
|
9天前
|
XML Java 数据格式
【SpringFramework】Spring IoC-基于XML的实现
本文主要讲解SpringFramework中IoC和DI相关概念,及基于XML的实现方式。
97 69
|
7天前
|
Java Spring 容器
【SpringFramework】Spring IoC-基于注解的实现
本文主要记录基于Spring注解实现IoC容器和DI相关知识。
40 21
|
13天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
12天前
|
存储 Java 应用服务中间件
【Spring】IoC和DI,控制反转,Bean对象的获取方式
IoC,DI,控制反转容器,Bean的基本常识,类注解@Controller,获取Bean对象的常用三种方式
|
1月前
|
存储 缓存 Java
Spring面试必问:手写Spring IoC 循环依赖底层源码剖析
在Spring框架中,IoC(Inversion of Control,控制反转)是一个核心概念,它允许容器管理对象的生命周期和依赖关系。然而,在实际应用中,我们可能会遇到对象间的循环依赖问题。本文将深入探讨Spring如何解决IoC中的循环依赖问题,并通过手写源码的方式,让你对其底层原理有一个全新的认识。
56 2
|
28天前
|
监控 NoSQL 时序数据库
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
《docker高级篇(大厂进阶):7.Docker容器监控之CAdvisor+InfluxDB+Granfana》包括:原生命令、是什么、compose容器编排,一套带走
196 77
|
1月前
|
监控 Docker 容器
在Docker容器中运行打包好的应用程序
在Docker容器中运行打包好的应用程序
|
9天前
|
Ubuntu Linux 开发工具
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
Docker 是一个开源的容器化平台,允许开发者将应用程序及其依赖项打包成标准化单元(容器),确保在任何支持 Docker 的操作系统上一致运行。容器共享主机内核,提供轻量级、高效的执行环境。本文介绍如何在 Ubuntu 上安装 Docker,并通过简单步骤验证安装成功。后续文章将探讨使用 Docker 部署开源项目。优雅草央千澈 源、安装 Docker 包、验证安装 - 适用场景:开发、测试、生产环境 通过以上步骤,您可以在 Ubuntu 系统上成功安装并运行 Docker,为后续的应用部署打下基础。
docker 是什么?docker初认识之如何部署docker-优雅草后续将会把产品发布部署至docker容器中-因此会出相关系列文章-优雅草央千澈
|
15天前
|
存储 Kubernetes 开发者
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
Docker 是一种开源的应用容器引擎,允许开发者将应用程序及其依赖打包成可移植的镜像,并在任何支持 Docker 的平台上运行。其核心概念包括镜像、容器和仓库。镜像是只读的文件系统,容器是镜像的运行实例,仓库用于存储和分发镜像。Kubernetes(k8s)则是容器集群管理系统,提供自动化部署、扩展和维护等功能,支持服务发现、负载均衡、自动伸缩等特性。两者结合使用,可以实现高效的容器化应用管理和运维。Docker 主要用于单主机上的容器管理,而 Kubernetes 则专注于跨多主机的容器编排与调度。尽管 k8s 逐渐减少了对 Docker 作为容器运行时的支持,但 Doc
82 5
容器化时代的领航者:Docker 和 Kubernetes 云原生时代的黄金搭档
|
20天前
|
关系型数据库 应用服务中间件 PHP
实战~如何组织一个多容器项目docker-compose
本文介绍了如何使用Docker搭建Nginx、PHP和MySQL的环境。首先启动Nginx容器并查看IP地址,接着启动Alpine容器并安装curl测试连通性。通过`--link`方式或`docker-compose`配置文件实现服务间的通信。最后展示了Nginx配置文件和PHP代码示例,验证了各服务的正常运行。
45 3
实战~如何组织一个多容器项目docker-compose