前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Spring源码】- 01 Spring IoC容器启动之this方法

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

作者头像
Reactor2020
发布2023-03-22 18:55:10
2100
发布2023-03-22 18:55:10
举报

开始案例

1、定义两个Service Bean

代码语言:javascript
复制
package org.source.ioc.basic.demo02;

public class TestService01 {
}
代码语言:javascript
复制
import org.springframework.stereotype.Component;

@Component
public class TestService02 {
}
代码语言:javascript
复制
package org.source.ioc.basic.demo02;

public class TestService03 {
}

2、定义Configuration

代码语言:javascript
复制
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、测试代码

代码语言:javascript
复制
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)));
 }
}

输出结果:

代码语言:javascript
复制
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到底做了哪些工作。

代码语言:javascript
复制
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
 this();
 register(componentClasses);
 refresh();
}

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

代码语言:javascript
复制
public AnnotationConfigApplicationContext() {
 this.reader = new AnnotatedBeanDefinitionReader(this);
 this.scanner = new ClassPathBeanDefinitionScanner(this);
}

AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScannerSpring中两个非常重要的类。

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

代码语言:javascript
复制
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构造方法中最关键的是在最后一句代码,其源码核心见下:

代码语言:javascript
复制
 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基本使用:

代码语言:javascript
复制
@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实现框架定制化功能,比如MyBatisMapper扫描就是一个典型应用案例,MyBatisMapperScannerConfigurer的内部就使用到一个ClassPathBeanDefinitionScanner的子类,实现将Mapper接口文件注入到IoC容器中。

代码语言:javascript
复制
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这个,这里要特别注意下,避免修改却发现没有起作用的尴尬。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-04-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Reactor2020 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 开始案例
  • Reader
  • Scanner
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档