【Spring源码】 Spring IoC容器启动之this方法

4-Spring源码解析之this()(1)——AnnotatedBeanDefinitionReader 1. 程序入口 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class); 2. AnnotationConfigApplicationContext类有参构造器 其中MainConfig类是自己写的配置类,我们从这一行代码来开始... 阅读详情

开始案例

1、定义两个Service Bean

package org.source.ioc.basic.demo02;

public class TestService01 {
}
import org.springframework.stereotype.Component;

@Component
public class TestService02 {
}
package org.source.ioc.basic.demo02;

public class TestService03 {
}

2、定义Configuration

package org.source.ioc.basic.demo02;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@ComponentScan(basePackageClasses = {TestConfig.class})
@Import(TestService03.class)
public class TestConfig {

 @Bean
 public TestService01 testService01(){
  return new TestService01();
 }

}

3、测试代码

public class MainDemo {
 @Test
 public void test01(){
  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TestConfig.class);
  Arrays.stream(context.getBeanDefinitionNames()).forEach(System.out::println);
  //Arrays.stream(context.getBeanDefinitionNames()).forEach(x -> System.out.println(x+"->"+context.getBean(x)));
 }
}

输出结果:

org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
testConfig
testService02
org.source.ioc.basic.demo02.TestService03
testService01

通过上面的输出结果可以看出,三个TestService都被纳入了Spring IoC容器中,但却是通过三种不同方式实现的,@ComponentScan@Import@Bean,从输入结果来看,IoC容器中除了自定义的类外,还有几个非我们自定义的Bean,它们又是从哪里引入的、引入进来又有什么用呢?下面我们就通过源码方式分析下IoC的启动流程,看看IoC容器启动的背后到底隐藏了哪些玄机。

Reader

new AnnotationConfigApplicationContext(TestConfig.class)这一句测试代码就可以驱动IoC启动,非常的简单,下面我们就来研究下隐藏在这一句代码的背后Spring到底做了哪些工作。

public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
 this();
 register(componentClasses);
 refresh();
}

这个构造方法代码非常简单,主要逻辑封装在三个方法中,首先我们来看下this()这个方法。

public AnnotationConfigApplicationContext() {
 this.reader = new AnnotatedBeanDefinitionReader(this);
 this.scanner = new ClassPathBeanDefinitionScanner(this);
}

AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScannerSpring中两个非常重要的类。

首先,我们来看下AnnotatedBeanDefinitionReader这个类,@Configuration@Import@Autowired@Bean等等这些Spring中常用注解可以很神奇的为我们实现各种功能,注解本身是没有任何意义的,核心在于隐藏在这些注解背后的处理逻辑,AnnotatedBeanDefinitionReader就是这个隐藏在注解背后的处理逻辑,可以实现对Spring中常用注解的解析处理。

public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry, Environment environment) {
 Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
 Assert.notNull(environment, "Environment must not be null");
 this.registry = registry;
 //条件比较器,用于处理@Condition注解
 this.conditionEvaluator = new ConditionEvaluator(registry, environment, null);
 AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}

AnnotatedBeanDefinitionReader构造方法中最关键的是在最后一句代码,其源码核心见下:

 public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
   BeanDefinitionRegistry registry, @Nullable Object source) {

        ...//省略

  Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);

  if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {
   RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));
  }

  if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {
   RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));
  }

  if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
   RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
  }

  if (jpaPresent && !registry.containsBeanDefinition(PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) {
   RootBeanDefinition def = new RootBeanDefinition();
   try {
    def.setBeanClass(ClassUtils.forName(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME,
      AnnotationConfigUtils.class.getClassLoader()));
   }
   catch (ClassNotFoundException ex) {
    throw new IllegalStateException(
      "Cannot load optional framework class: " + PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, ex);
   }
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME));
  }

  if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
   RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
  }

  if (!registry.containsBeanDefinition(EVENT_LISTENER_FACTORY_BEAN_NAME)) {
   RootBeanDefinition def = new RootBeanDefinition(DefaultEventListenerFactory.class);
   def.setSource(source);
   beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_FACTORY_BEAN_NAME));
  }

  return beanDefs;
 }

其逻辑很简单,就是向IoC中注册一个BeanFactoryPostProcessor和5个BeanPostProcessorSpring就是通过这些PostProcessor扩展点实现对各种注解的解析、处理,让开发只需要简单的几个注解就可以实现很多复杂功能,屏蔽了注解背后处理的复杂逻辑,这也是目前Spring开发趋势:注解驱动开发。

这几个PostProcessor大致作用:

  • ConfigurationClassPostProcessor:完成@Configuration@Import@ComponentScan@Component@Bean等注解支持,该类主要完成完成BeanDefinition的采集工作,就是解析各种注解,把需要纳入Spring管理的Bean都采集到一起生成BeanDefinition存储起来,供后续生成对象提供所需的材料;

  • AutowiredAnnotationBeanPostProcessor:完成@Autowired@Value注解支持,实现Spring中依赖注入的核心逻辑;

  • CommonAnnotationBeanPostProcessor:支持JSR-250的一些注解,如:@Resource@PostConstruct@PreDestroy等;

  • PersistenceAnnotationBeanPostProcessor:支持JPA中相关注解的支持;

  • EventListenerMethodProcessorDefaultEventListenerFactory:这两个类主要完成对Spring4.2之后引入的@EventListener等事件注解支持;

上面六个PostProcessor中,最重要的是前两个,一个负责完成从各个地方把需要纳入IoC管理的Bean都收集到一起;另一个则完成对这些收集的Bean进行依赖注入。Spring IoC基本工作就是管理Bean以及依赖注入,所以IoC启动流程分析中,这两个类占有很大的比重。

Scanner

下面再来看下另一个非常重要的类:ClassPathBeanDefinitionScanner

Spring项目中配置<context:component-scan base-package="com.example.demo"/>@ComponentScan(basePackages="a.b.c"),这背后的工作就是靠ClassPathBeanDefinitionScanner完成,其主要就是完成对指定包路径下的Bean进行扫描,把含有特定注解的Bean生成BeanDefinition注册到IoC容器中。

下面通过一个Demo了解下ClassPathBeanDefinitionScanner基本使用:

@Test
public void classPathBeanDefinitionScannerTest(){
 String BASE_PACKAGE = "org.source.ioc.basic.demo02.scanner";
 //1.创建一个IoC容器,用于装载ClassPathBeanDefinitionScanner扫描出的BeanDefinition
 SimpleBeanDefinitionRegistry registry= new SimpleBeanDefinitionRegistry();
 /**
 * 2.创建一个Scanner扫描器,useDefaultFilters:是否使用默认过滤器,默认该值为true,
 * 即会把@Component注解的Bean都扫描出来,这里我们不需要这个功能,只需要扫描我们自定义注解的Bean
 */
 ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(registry, false);
 //3.这里注册一个注解类型过滤器,完成对自定义注解Bean过滤
 scanner.addIncludeFilter(new AnnotationTypeFilter(MyComponent.class));

 /**
 * 4.是否向IoC中注册用于用于处理核心注解的6个PostProcessor,默认true
 * AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry)
 */
 scanner.setIncludeAnnotationConfig(false);
 //5.上面工作都准备完成,调用scan(String... basePackages)即可对指定的包路径下的Bean扫描过滤,返回值是扫描出的Bean数量
 int beanCount = scanner.scan(BASE_PACKAGE);
 //6.scan()方法会把符合要求的Bean生成BeanDefinition并注册到IoC容器中,我们就可以从IoC容器中获取到这些BeanDefinition
 String[] beanDefinitionNames = registry.getBeanDefinitionNames();
 System.out.println("bean count:"+beanCount);
 Arrays.stream(beanDefinitionNames).forEach(System.out::println);
}

上面案例就可以完成扫描org.source.ioc.basic.demo02.scanner包下的所有Bean,将含有@MyComponent注解的Bean生成对应的BeanDefinition,并注册到IoC容器中。

ClassPathBeanDefinitionScannerSpring中非常重要的一个类,决定了哪些类需要被纳入IoC容器。我们可以继承ClassPathBeanDefinitionScanner实现框架定制化功能,比如MyBatis 的Mapper扫描就是一个典型应用案例,MyBatis 的MapperScannerConfigurer的内部就使用到一个ClassPathBeanDefinitionScanner的子类,实现将Mapper接口文件注入到IoC容器中。

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
 if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    scanner.setAnnotationClass(this.annotationClass);
    scanner.setMarkerInterface(this.markerInterface);
    scanner.setSqlSessionFactory(this.sqlSessionFactory);
    scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
    scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
    scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
    scanner.setResourceLoader(this.applicationContext);
    scanner.setBeanNameGenerator(this.nameGenerator);
    scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
    if (StringUtils.hasText(lazyInitialization)) {
      scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
    }
    scanner.registerFilters();
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}

ClassPathMapperScanner继承ClassPathBeanDefinitionScanner,完成@MapperScan注解支持,将特定的Mapper类生成BeanDefinition注册到IoC容器中,这样我们才能通过@Autowired依赖注入Service类中。

MyBatisMapper类是一个接口,而依赖注入获取到的是一个对象,这是如何做到的?

这里主要运用了动态代理功能,具体可以参见后续MyBatis Mapper实现原理分析。

注意:AnnotationConfigApplicationContext中定义的:this.scanner = new ClassPathBeanDefinitionScanner(this);,这个其实一般情况下是没有使用的,只有手工调用AnnotationConfigApplicationContext#scan()才会使用到scanner。大部分Bean的采集工作是AnnotatedBeanDefinitionReader中向IoC注册的ConfigurationClassPostProcessor这个BeanFactory后置处理器完成的,它在处理@ComponentScan注解时会重新创建一个ClassPathBeanDefinitionScanner实例,而不是使用AnnotationConfigApplicationContext.scanner这个,这里要特别注意下,避免修改却发现没有起作用的尴尬。

Spring IoC容器启动源码解析 本文深入剖析Spring IoC容器启动流程,先介绍核心类关系,重点解析容器初始化关键方法`AbstractApplicationContext#refresh()`的12个执行步骤。随后拆解XML、注解及Spring Boot三种主流启动方式,虽入口各异,最终均统一调用`refresh()`完成容器初始化。文章结合源码揭示底层运行机制,帮助开发者理解从配置加载到Bean实例化的完整流程。 阅读详情

相关推荐

深入理解 Spring IOC 底层实现机制(refresh 方法源码分析)

/IOC 核心方法//防止 启动和销毁并发执行//启动步骤记录//刷新准备工作//创建 beanFactory 将配置文件解析为 beandefiniton 对象 注册到 beanFactory 中//ConfigurableListableBeanFactory是一个接口 真正干活的是 DefaultListableBeanFactory//beanFactory 预处理 主要是设置 beanFactory 类加载器及忽略一些自动装配 设置一些默认benatry {

weixin_42118323的博客 1751

spring源码spring IOC容器底层源码分析

spring源码】AOP底层源码分析1.入口2.spring IOC核心refresh()方法源码分析2.1 prepareRefresh()刷新前的预处理2.2 obtainFreshBeanFactory();获取BeanFactory2.3 prepareBeanFactory(beanFactory);BeanFactory的预准备工作2.4 postProcessBeanFactory...

aaa_bbb_ccc_123_456的博客 2万+

ArcGIS Pro SDK (二十五)工作流管理器

【代码】ArcGIS Pro SDK (二十五)工作流管理器。

Winemonk 482

Spring事务传播行为及失效this详解

使用this失效的场景: 无事务方法A,使用this调用开启默认事务方法B,无论在A方法中抛出异常,还是在B方法中抛出异常,方法A和方法B的操作都不会回滚。 无事务方法A,使用代理对象调用开启默认事务方法B,如果方法A抛出异常,方法A和方法B的操作都不会回滚;如果方法B抛出异常,则方法B的操作会回滚,方法A的操作不会回滚。 开启默认事务方法A,无事务方法B,方法A使用this或者代理对象调用方法B,方法A抛出异常,方法A和方法B的操作都会回滚。 开启默认事务方法A,无事务方法B,方法A使用this或者代理

Linging_24的博客 3457

Spring源码】- 01 Spring IoC容器启动this方法

开始案例1、定义两个Service Beanpackageorg.source.ioc.basic.demo02; publicclassTestService01{ } im...

god_86的专栏 289

spring容器启动流程(一)之this方法

使用 ClassPathBeanDefinitionScanner。

weixin_56690415的博客 163

Spring事务this自调用的理解误区?真的会让事务失效吗?

我们经常谈到Spring事务失效会有多种场景导致:可参考我另外一篇文章一文清晰讲解@Transactional 注解失效场景@Transactional 应用在非 public 修饰的方法上@Transactional 注解属性 propagation 设置错误@Transactional 注解属性 rollbackFor 设置错误同一个类中方法调用,导致@Transactional失效异常被你的 catch“吃了”导致@Transactional失效数据库引擎不支持事务。

Apple_Boy的博客 6601

Spring深度学习:IOC容器源码解析

大家经常戏称Java工程师为Spring工程师,毫无疑问,这句话体现出Spring框架在Java开发中的重要性和普及度。本文小豪将带大家深度学习Spring IOC底层源码,学习过程中不仅仅是了解Spring IOC的工作原理,更重要的是学会理解框架的底层逻辑,从而提升我们的思维能力。

Code豪客 1659

Spring IOC 源码分析

【1】Spring容器启动的时候,先会保存所有注册来的Bean的定义信息;■Xml注册■ 注解注册Bean@Service@Component@Bean等等【2】Spring容器会在合适的时机创建bean;■ 用到这个bean的时候,利用getBean创建bean,创建好后保存在容器中。■ 统一创建剩下所有bean的时候,调用:每一个bean的创建完成,都会使用各种后置处理器进行处理,来增强bean的功能:■:处理自动注入;■:来做AOP功能;等等。。。事件监听。

程序猿进阶 2383

【小家SpringSpring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(二),Spring容器启动/刷新的完整总结

前言 在上一篇文章:【小家SpringSpring IOC容器启动流程 AbstractApplicationContext#refresh()方法源码分析(一)中已经介绍了前五步,现在Spring IOC容器的工厂环境已经都准备好了。 Bean工厂可以简单理解为一个钩子容器,里面注册有众多的BeanFactoryPostProcessor以及BeanFactoryPostProcessor,接...

架构师:通透,才能写出好代码! 5916

Spring IOC容器启动整体流程

目录 一、简介 二、IOC整体流程 一、简介 Spring核心有两个: IOC:控制反转 AOP:面向切面编程 相信大家在项目中都使用过Spring进行开发,都知道Spring非常强大,那么在熟练掌握Spring进行开发后,我们有必要去了解一下其中的原理,Spring工作原理也是面试中必问的一个环节,先来看看几道常见的面试题,想想看,如果被面试官问到,你会怎么答? Spring IOC的工作原理? Spring Bean的生命周期是怎样的? Spring 中使用到了哪些设计模式? Spr

weixiaohuai的博客 3486

Spring源码IOC容器源码

ApplicationContext 是 Spring 的核心接口,表示 SpringIOC 容器,以下三个类是ApplicationContext 接口的常用实现类:ClassPathXmlApplicationContext,AnnotationConfigApplicationContext,FileSystemXmlApplicationContext。

weixin_42828342的博客 1395

Spring源码系列-IOC容器二】

spring获取bean的流程

qq_45592483的博客 1467

Spring ioc源码分析

Spring源码分析 查看方式 先猜测,后验证,不需要一开始就调试代码 入口 从web.xml入口 DispatcherServlet extends HttpServlet init() :初始化方法(入口) service():浏览器输入url的时候,从web容器调用,doget,doPost执行逻辑 destory():生命周期最后关头 核心容器类图 BeanFactory ​ 是典型的...

黄金时代的架构之路 4561

Spring Ioc 容器启动流程源码解析

java开发四年了,很多时候都在想spring究竟是怎么玩转的,当对java有一定深入的理解,我们就需要对spring源码进行解读,源码的解读过程个人觉得至少要看5遍以上才会不晕车,品读其中的奥妙。 IOC(Inversion of Control)控制反转:所谓控制反转,就是把原先我们代码里面需要实现的对象创 建、依赖的代码,反转给容器来帮忙实现。 一、Spring 核心容器类图 1、BeanFactory: spring Bean 的创建是典型的工厂模式,这一系列的 Bea...

dayuyu_123的博客 939

Spring源码_05_IOC容器启动细节

前面几章,大致讲了SpringIOC容器的大致过程和原理,以及重要的容器和beanFactory的继承关系,为后续这些细节挖掘提供一点理解基础。掌握总体脉络是必要的,接下来的每一章都是从总体脉络中, 去研究之前没看的一些重要细节。 本章就是主要从Spring容器启动开始,查看一下Spring容器是怎么启动的,调用了父类的构造方法有没有干了什么。

abc2675811870的博客 1580

Spring源码之手写IOC容器

手写Spring IOC容器 源码

小兔子的博客 1384

Spring IoC容器源码解析(一):BeanDefinition与容器启动流程

Spring框架的核心是IoC容器(Inversion of Control Container),而是容器内部描述Bean的元数据模型。本文将深入源码层面,解析BeanDefinition的层次体系、属性配置,并逐步追踪容器启动流程(Container Startup Process),从资源定位、加载、注册到Bean实例化的完整生命周期。通过详细的类图、序列图和源码分析,揭示Spring如何实现依赖注入和控制反转的核心机制。控制反转(Inversion of Control, IoC

七夜zippoe的博客 649

Spring源码学习系列二:IOC容器启动流程

Spring IOC说起 众所周知,Spring有2大核心,IOC和AOP,而IOC又是AOP的基础,因此Spring源码学习系列的第一篇博客以IOC开始。本篇博客以IOC容器启动流程为切入点,介绍从web容器启动Spring ContextLoaderListener监听到ServletContextEvent并初始化WebApplicationContext到最终WebApplicati...

Spring源码学习系列 1733

TriCore架构-基于TC397在SOTA过程中AB-SWAP切换底层原理

TC397的SOTA升级机制通过硬件地址重映射实现,利用MMU将逻辑地址动态映射到不同物理地址。其PFlash支持标准/备用地址映射,通常使用PF0+PF1+PF4空间以便于OTA处理。升级时,新固件自动写入非活动分区(A/B分区切换),由地址转换函数完成逻辑地址到物理地址的转换。系统重启后根据寄存器选择启动区域,实现安全无缝的固件更新。

xiandang8023的博客 456

基于高斯过程回归的锂电池SOC估计方法及其应用案例

内容概要:本文详细介绍了使用高斯过程回归(GPR)方法来建立锂电池SOC估计模型的学习案例。首先,文章解释了高斯过程回归的基本概念及其在电池SOC估计中的应用。接着,通过具体的数据集(bjdst, dst, fuds工况作为训练集,us06工况作为验证集),展示了如何使用Python的GPy库构建GPR模型,并进行了详细的代码实现与分析。最后,文章讨论了实验结果,指出尽管该方法的估计精度一般,但对于初学者来说是一个很好的入门学习机会,能够帮助他们理解并应用高斯过程回归进行电池SOC估计。适合人群:对电池管理、机器学习感兴趣的初学者,尤其是希望了解高斯过程回归在电池SOC估计中应用的研究人员和工程师。使用场景及目标:适用于需要掌握高斯过程回归基本原理及其在电池SOC估计中应用的人群。目标是通过实际案例学习,掌握如何使用GPR模型进行电池SOC估计,并理解其优缺点。其他说明:文中提供的代码示例和数据分析有助于读者更好地理解模型的工作机制和应用场景。同时,文章还指出了未来改进的方向,鼓励进一步研究和发展更精确的电池SOC估计方法

上一篇: 【Netty源码分析】 客户端接入流程
下一篇: 【Spring源码】Spring IoC容器启动之refresh方法
小虾米啊
博客等级 码龄4年 118粉丝 · 112原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值