【spring源码系列-06】refresh中obtainFreshBeanFactory方法的执行流程

简介: 【spring源码系列-06】refresh中obtainFreshBeanFactory方法的执行流程

一,bean工厂的创建

前一篇了解了refresh的第一个方法prepareRefresh ,主要是初始化环境,实例化一些监听器和环境等。接下来讲解refresh中的第二个方法,obtainFreshBeanFactory()

@Override
public void refresh() throws BeansException, IllegalStateException {
  synchronized (this.startupShutdownMonitor) {
    //1:准备刷新上下文环境
    prepareRefresh();
    //2:获取告诉子类初始化Bean工厂,不同工厂有不同的实现
        //  并且将配置文件的属性值加载到当前工厂中
    ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    //3:对bean工厂进行填充属性
    prepareBeanFactory(beanFactory);
    try {
      // 第四:留个子类去实现该接口,bean工厂的后置处理器
      postProcessBeanFactory(beanFactory);
      // 调用我们的bean工厂的后置处理器. 
          //1. 会在此将class扫描成beanDefinition  2.bean工厂的后置处理器调用
      invokeBeanFactoryPostProcessors(beanFactory);
      // 注册我们bean的后置处理器
      registerBeanPostProcessors(beanFactory);
      // 初始化国际化资源处理器.
      initMessageSource();
      // 创建事件多播器
      initApplicationEventMulticaster();
      // 这个方法同样也是留个子类实现的springboot也是从这个方法进行启动tomcat的.
      onRefresh();
      //把我们的事件监听器注册到多播器上
      registerListeners();
      // 实例化我们剩余的单实例bean.
      finishBeanFactoryInitialization(beanFactory);
      // 最后容器刷新 发布刷新事件(Spring cloud也是从这里启动的)
      finishRefresh();
    }
    }
}

在refresh的前置工作中,已经提前初始化了一个默认工厂DefaultListableBeanFactory,而这一步,就是初始化具体的工厂实现。接下来继续往下面的源码看 ,此时会调用一个 obtainFreshBeanFactory() 方法

ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

进去这个方法中可以发现,第一步就是会进行一个刷新bean工厂的方法 refreshBeanFactory

protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  /**
   *  xml加载spring会在这里加载beanDefinition
   *  javaconfig只是刷新了beanFactory
   */
  refreshBeanFactory();
  //返回我们的bean工厂
  ConfigurableListableBeanFactory beanFactory = getBeanFactory();
  if (logger.isDebugEnabled()) {
    logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
  }
  return beanFactory;
}

1,refreshBeanFactory()

1,接下来查看这个刷新bean工厂的方法,首先会判断bean工厂是否存在,存在则销毁原先的bean工厂,然后再创建一个默认的工厂 DefaultListableBeanFactory ,创建工厂的过程主要是加载配置文件,设置属性等。

@Override
protected final void refreshBeanFactory() throws BeansException {
  //若已经存在了 就信息销毁等操作
  if (hasBeanFactory()) {
    destroyBeans();
    closeBeanFactory();
  }
  try {
    //为我们的Spring应用上下文对象创建我们的beanFactory
    DefaultListableBeanFactory beanFactory = createBeanFactory();
    //为容器设置一个序列化ID,可以通过反序列化获取bean工厂
    beanFactory.setSerializationId(getId());
    customizeBeanFactory(beanFactory);
    //加载我们的bean定义
    loadBeanDefinitions(beanFactory);
    synchronized(this.beanFactoryMonitor) {
      this.beanFactory = beanFactory;
    }
  } catch (IOException ex) {
    ...
  }
}

2,这个创建的bean工厂就是我们熟悉的默认bean工厂 DefaultListableBeanFactory

//为我们的spring 上下文创建我们的内置的beanFactory
protected DefaultListableBeanFactory createBeanFactory() {
  return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

3,随后就是设置一个序列化的id,可以通过反序列的形式获取到对应的beanFactory

beanFactory.setSerializationId(getId());


4,随后就是定制化bean工厂的操作,会设置两个重要的属性,就是允许覆盖bean定义和允许循环依赖,可以重写这两个方法,修改这两个值的默认属性。

protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
  if (this.allowBeanDefinitionOverriding != null) {
    beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
  }
  if (this.allowCircularReferences != null) {
    beanFactory.setAllowCircularReferences(this.allowCircularReferences);
  }
}

5,再接着就是以加载bean定义的形式将bean工厂加载,其具体的方法实现是 loadBeanDefinitions

loadBeanDefinitions(beanFactory);

在这个加载bean定义的抽象类中,可以看到存在四种不同的实现,分别就是对应着不同方式获取上下文的方式

以注解的方式,继续往下看,可以发现在内部存在着大量的方法的重载,而此时bean定义的信息时存储在工厂类里面的

在前面两步里面,主要是创建一个读取器和扫描器,获取一些系统环境和变量,资源解析器,处理一些条件注解,加载一些bean的后置处理器等

//读取器
AnnotatedBeanDefinitionReader reader = getAnnotatedBeanDefinitionReader(beanFactory);
//扫描器
ClassPathBeanDefinitionScanner scan = getClassPathBeanDefinitionScanner(beanFactory);

随后再获取bean容器的构造器,如果不存在则重新注册一个单例的构造器

BeanNameGenerator beanNameGenerator = getBeanNameGenerator();

随后再获取一个数据解析器 ScopeMetadataResolver

ScopeMetadataResolver scopeMetadataResolver = getScopeMetadataResolver();

1.1,reader.register

在加载完这些配置之后,接下来就是真正的去对这些bean定义进行注册

reader.register(ClassUtils.toClassArray(this.annotatedClasses));

真正的注册bean的方法是这个 doRegisterBean 方法

该方法中会存储一些@Configuration注解的类

AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(annotatedClass);

也会对一些条件进行筛选判断,如@ConditionalOnClass,@ConditionalOnType等,以及解析一些bean的作用域,填充一些bean的属性,如懒加载等等,最后将这个bean定义进行注册,具体的注册逻辑在上一篇详细的写过

BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry)

1.2,scanner.scan

在reader注册完之后,接下来对一些xml文件或者一些包进行扫描的工作

scanner.scan(StringUtils.toStringArray(this.basePackages));

其具体的扫描扫描方法如下,真正扫描配置类的工作是这个 doScan 方法

public int scan(String... basePackages) {
  //还没有扫描mapper包之前 容器中所有的bean定义个数
  int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
  //真正的扫描我们的mapper包的mapper类
    //此处调用的是子类的doScan因为被重写了
  doScan(basePackages);
  // 注册系统中的配置类处理器
  if (this.includeAnnotationConfig) {
    AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
  }
  //返回扫描出mapper的bean定义的个数
  return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}

接下来查看这个doscan方法 , 如下图所示

首先会创建一个 BeanDefinitionHolder 对象用于保存bean定义,这个对象里面存储着bean定义,属性等

//创建bean定义的holder对象用于保存扫描后生成的bean定义对象
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();

随后一步就是循环这个作为参数传进来的包路径,第一步就是找到候选的 Components

Set<BeanDefinition> candidates = findCandidateComponents(basePackage);

在上面reader.register注册中,在doRegisterBean 方法中将 @Configuration 注解先注册成beanDifinition,所以@Configuration比@Component注解先加载成bean定义。接下来继续查看这个 findCandidateComponents 方法,会对包路径进行解析

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
    //验证是否为空
  if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
    return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
  }
  else {
    return scanCandidateComponents(basePackage);
  }
}

在使用注解获取上下文启动spring时,在refresh的前置工作中,就会注册一个 includeFilters 和 excludeFilters ,默认会将所有的带有 @Component,@Controller,@Service 等等注解的类加载到这个 includeFilters 的过滤器中,也可以手动将他加入到 excludeFilters 中,这样spring容器就不会加载

protected void registerDefaultFilters() {
  //加入扫描我们的@Component的
  this.includeFilters.add(new AnnotationTypeFilter(Component.class));
  ....
}

接着上文,如果是包含在这个包含过滤器中,则直接将这个容器加入到包中,否者会继续扫描这些候选者们,会判断这些文件是否可读等。


看完doScan方法找到全部的候选beanDefinition之后,这些bean定义就是符合标准可以注册到bean工厂的一些bean定义,接下来就会对这些bean定义设置一些属性,随后就是关键的一步,就是将我们解析出来的组件bean定义注册到我们的IOC容器中,随后将这个bean定义返回

if (checkCandidate(beanName, candidate)) {
  BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
  definitionHolder =
    AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
  beanDefinitions.add(definitionHolder);
  registerBeanDefinition(definitionHolder, this.registry);
}

最后又会调用具体的 doRegisterBean 方法用于真正的注册bean定义

<T> void doRegisterBean(...){}

总结来说,不管是使用读取器注册还是使用扫描器进行扫描操作,都是为了生成bean定义,并将这个bean定义加入到BeanDifinitionMap中,随后交给bean工厂去生产。

2,getBeanFactory

在刷新完bean工厂之后,随后就是get获取一个bean工厂

ConfigurableListableBeanFactory beanFactory = getBeanFactory();

获取bean工厂的方法如下,加了一个同步锁,随后将这个bean工厂对象返回

@Override
public final ConfigurableListableBeanFactory getBeanFactory() {
  synchronized(this.beanFactoryMonitor) {
    if (this.beanFactory == null) {
      throw new IllegalStateException("...");
    }
    return this.beanFactory;
  }
}


相关文章
|
16天前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
5月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
366 70
|
5月前
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
459 5
|
5月前
|
Java Spring 容器
两种Spring Boot 项目启动自动执行方法的实现方式
在Spring Boot项目启动后执行特定代码的实际应用场景中,可通过实现`ApplicationRunner`或`CommandLineRunner`接口完成初始化操作,如系统常量或配置加载。两者均支持通过`@Order`注解控制执行顺序,值越小优先级越高。区别在于参数接收方式:`CommandLineRunner`使用字符串数组,而`ApplicationRunner`采用`ApplicationArguments`对象。注意,`@Order`仅影响Bean执行顺序,不影响加载顺序。
386 2
|
6月前
|
存储 监控 数据可视化
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
105 0
|
2月前
|
Java Spring 容器
SpringBoot自动配置的原理是什么?
Spring Boot自动配置核心在于@EnableAutoConfiguration注解,它通过@Import导入配置选择器,加载META-INF/spring.factories中定义的自动配置类。这些类根据@Conditional系列注解判断是否生效。但Spring Boot 3.0后已弃用spring.factories,改用新格式的.imports文件进行配置。
695 0
|
6月前
|
前端开发 Java 数据库
微服务——SpringBoot使用归纳——Spring Boot集成Thymeleaf模板引擎——Thymeleaf 介绍
本课介绍Spring Boot集成Thymeleaf模板引擎。Thymeleaf是一款现代服务器端Java模板引擎,支持Web和独立环境,可实现自然模板开发,便于团队协作。与传统JSP不同,Thymeleaf模板可以直接在浏览器中打开,方便前端人员查看静态原型。通过在HTML标签中添加扩展属性(如`th:text`),Thymeleaf能够在服务运行时动态替换内容,展示数据库中的数据,同时兼容静态页面展示,为开发带来灵活性和便利性。
279 0
|
2月前
|
缓存 JSON 前端开发
第07课:Spring Boot集成Thymeleaf模板引擎
第07课:Spring Boot集成Thymeleaf模板引擎
330 0
第07课:Spring Boot集成Thymeleaf模板引擎
|
6月前
|
XML Java 数据库连接
微服务——SpringBoot使用归纳——Spring Boot集成MyBatis——基于 xml 的整合
本教程介绍了基于XML的MyBatis整合方式。首先在`application.yml`中配置XML路径,如`classpath:mapper/*.xml`,然后创建`UserMapper.xml`文件定义SQL映射,包括`resultMap`和查询语句。通过设置`namespace`关联Mapper接口,实现如`getUserByName`的方法。Controller层调用Service完成测试,访问`/getUserByName/{name}`即可返回用户信息。为简化Mapper扫描,推荐在Spring Boot启动类用`@MapperScan`注解指定包路径避免逐个添加`@Mapper`
225 0

热门文章

最新文章