Spring源码解析(二):bean容器的创建、默认后置处理器、扫描包路径bean

Spring源码系列文章

Spring源码解析(一):环境搭建

Spring源码解析(二):


目录

  • 一、Spring源码基础组件
    • 1、bean定义接口体系
    • 2、bean工厂接口体系
    • 3、ApplicationContext上下文体系
  • 二、AnnotationConfigApplicationContext注解容器
    • 1、创建bean工厂-beanFactory
    • 2、注册默认后置处理器
    • 3、自定义扫描包功能
    • 4、注册配置类(Config.class)
  • 三、总结

一、Spring源码基础组件

  • 阅读源码时候,接口与类过多,可以对照这里查看对应的关系

1、bean定义接口体系

在这里插入图片描述

2、bean工厂接口体系

在这里插入图片描述

3、ApplicationContext上下文体系

在这里插入图片描述

二、AnnotationConfigApplicationContext注解容器

配置类:

@ComponentScan("com.xc")
@Configuration
public class Config {@Bean()public Book book(){return new Book();}
}

启动类:

public class Client {public static void main(String[] args) {//创建注解容器,入参为配置类AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);//获取某个beanBook book = context.getBean(Book.class);System.out.println(book);//关闭容器context.close();}
}

AnnotationConfigApplicationContext的构造方法:

在这里插入图片描述

1、创建bean工厂-beanFactory

  • 调用AnnotationConfigApplicationContext 的无参构造方法

在这里插入图片描述

  • AnnotationConfigApplicationContext的父类是GenericApplicationContext
  • 调用当前类的无参构造,先会调用父类的无参构造,先看下父类无参构造做的事情
  • 这个beanFactory就是spring容器的核心实现类

在这里插入图片描述

  • DefaultListableBeanFactory(bean容器)会存放bean定义及bean名称集合等等

在这里插入图片描述

  • DefaultListableBeanFactory父类DefaultSingletonBeanRegistry会缓存所有实例化的bean

在这里插入图片描述

  • 接下来继续查看AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner的创建

2、注册默认后置处理器

  • 调用AnnotatedBeanDefinitionReader的有参构造方法

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 注册默认后置处理器
public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {// 1.获取之前创建好的DefaultListableBeanFactory对象,beanFactory实例DefaultListableBeanFactory beanFactory = unwrapDefaultListableBeanFactory(registry);// 2.为beanFactory添加两属性if (beanFactory != null) {if (!(beanFactory.getDependencyComparator() instanceof AnnotationAwareOrderComparator)) {beanFactory.setDependencyComparator(AnnotationAwareOrderComparator.INSTANCE);}if (!(beanFactory.getAutowireCandidateResolver() instanceof ContextAnnotationAutowireCandidateResolver)) {beanFactory.setAutowireCandidateResolver(new ContextAnnotationAutowireCandidateResolver());}}// 3.bean定义持有集合(持有bean定义、名字、别名)Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);// 查询beanDefinitionMap是否有 internalConfigurationAnnotationProcessor = ConfigurationClassPostProcessor// 3.1.注册@Configuration,@Import,@ComponentScan,@Bean等注解 bean定义后置处理器if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);// 注册到beanDefinitionMap中,并添加到beanDefs集合中beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}// 注册AutowiredAnnotationBeanPostProcessor// 3.2.注册@Autowired注解 bean后置处理器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));}// JSR-250,是java定义的一些注解,spring支持这注解。大致有:@PostConstruct @PreDestroy @Resource// 3.3.注册@Resource @PostConstruct @PreDestroy注解 bean后置处理器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));}// 3.4.spring注册支持jpa注解的beanFactory后置处理器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));}// 3.5.注册事件监听后置处理器 EventListenerMethodProcessorif (!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));}// 3.6.注册事件监听工厂 DefaultEventListenerFactoryif (!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;
}
  • 查看一共注册的beanDefinition

在这里插入图片描述

  1. internalConfigurationAnnotationProcessor = ConfigurationClassPostProcessor
    • @Configuration,@Import,@ComponentScan,@Bean等注解 bean定义后置处理器
  2. internalAutowiredAnnotationProcessor = AutowiredAnnotationBeanPostProcessor
    • @Autowired注解 bean实例化后置处理器
  3. internalCommonAnnotationProcessor = CommonAnnotationBeanPostProcessor
    • @Resource @PostConstruct @PreDestroy注解 bean实例化后置处理器
  4. internalEventListenerProcessor = EventListenerMethodProcessor 事件监听后置处理器
  5. internalEventListenerFactory = DefaultEventListenerFactory 事件监听工厂

3、自定义扫描包功能

  • 调用ClassPathBeanDefinitionScanner的有参构造方法
protected void registerDefaultFilters() {// 注册过滤器 添加 @Component// @Controller @Service @Repository 也会被扫描this.includeFilters.add(new AnnotationTypeFilter(Component.class));ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();try {// 扫描 JSR-250 @ManagedBeanthis.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));logger.trace("JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-250 1.1 API (as included in Java EE 6) not available - simply skip.}try {//扫描 JSR-330 @Namedthis.includeFilters.add(new AnnotationTypeFilter(((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));logger.trace("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");}catch (ClassNotFoundException ex) {// JSR-330 API not available - simply skip.}
}
  • 注册扫描过滤器 @Component,@Controller 、@Service、 @Repository 也会被添加进去
  • ClassPathBeanDefinitionScanner类:主要为了扩展AnnotationConfigApplicationContext添加一个自助扫描包的功能
  • 配置类上@ComponentScan注解扫描包路径下bean也是由此类实现
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
context.scan("com.xc.entity");

查看扫描包的源码

  • 这个方法主要就是进行扫描包路径,然后得到一个beanDefinition集合
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {Assert.notEmpty(basePackages, "At least one base package must be specified");Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();// 遍历扫描路径for (String basePackage : basePackages) {// 扫描得到beanDefinition,主要进行行excludeFilters、includeFilters判断和Conditional判断Set<BeanDefinition> candidates = findCandidateComponents(basePackage);// 遍历BeanDefinitionfor (BeanDefinition candidate : candidates) {// 解析scope注解信息ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);// 设置scope注解信息candidate.setScope(scopeMetadata.getScopeName());// 主要是解析Component有没有设置bean的名字,有则直接返回// 没有则根据短名构造一个(如果写的名字第1、2个字符是大写则直接返回,否则直接将第一个字符转成小写返回)String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);if (candidate instanceof AbstractBeanDefinition) {// 主要是给BeanDefinition设置一些默认的值postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);}if (candidate instanceof AnnotatedBeanDefinition) {// 获取类上的@Lazy、@Primary、@DependsOn、@Role、@Description相关信息并设置到定义信息中AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);}// 检查Spring容器中是否已经存在该beanName,有的话会判断是否匹配,匹配则不会加入spring容器// 不匹配则抛出异常,没有则直接加入spring容器if (checkCandidate(beanName, candidate)) {BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);definitionHolder =AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);beanDefinitions.add(definitionHolder);// 注册registerBeanDefinition(definitionHolder, this.registry);}}}return beanDefinitions;
}
  • 扫描bean的代码在findCandidateComponents中,进入
  • addCandidateComponentsFromIndex方法就是读取META-INF目录下的缓存文件
  • 解析原理和scanCandidateComponents一样
public Set<BeanDefinition> findCandidateComponents(String basePackage) {/***	  //解析是否有文件写的有component注解*    if()里面就是判断META-INF目录下面是否有写components文件*   (在该文件里面可以直接定义bean)然后在解析这个文件,其逻辑大致和下面一样*/if (this.componentsIndex != null && indexSupportsIncludeFilters()) {return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);} else {return scanCandidateComponents(basePackage);}
}
  • 该方法主要是获得扫描路径下面的所有file对象(.class)
  • 然后读取注解信息,判断与excludeFilters、includeFilters是否匹配以及Conditional条件判断
  • 最后判断是否独立的非接口非抽象类的普通类,或者@Lookup注解的抽象类,最终返回BeanDefinition集合
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {Set<BeanDefinition> candidates = new LinkedHashSet<>();try {// 获取basePackage下所有的文件资源// classpath*:/**/*.classString packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +resolveBasePackage(basePackage) + '/' + this.resourcePattern;// classpath文件下的所有file对象Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);boolean traceEnabled = logger.isTraceEnabled();boolean debugEnabled = logger.isDebugEnabled();for (Resource resource : resources) {if (traceEnabled) {logger.trace("Scanning " + resource);}try {// 元数据读取器,读取注解的信息,类的信息,接口抽象类,父类等MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);// 判断是不是一个beanif (isCandidateComponent(metadataReader)) {ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);// 把bean的属性设置进去,主要是名字sbd.setSource(resource);// 独立的非接口非抽象类的普通类,或者@Lookup注解的抽象类if (isCandidateComponent(sbd)) {if (debugEnabled) {logger.debug("Identified candidate component class: " + resource);}// 添加到返回的符合扫描的候选bean集合中candidates.add(sbd);} else {if (debugEnabled) {logger.debug("Ignored because not a concrete top-level class: " + resource);}}} else {if (traceEnabled) {logger.trace("Ignored because not matching any filter: " + resource);}}} catch (FileNotFoundException ex) {if (traceEnabled) {logger.trace("Ignored non-readable " + resource + ": " + ex.getMessage());}} catch (Throwable ex) {throw new BeanDefinitionStoreException("Failed to read candidate component class: " + resource, ex);}}} catch (IOException ex) {throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);}return candidates;
}

isCandidateComponent判断是不是bean

  • 和排除过滤器匹配,如果匹配成功则此.class不是扫描的bean
  • 先和之前注册的扫描过滤@Component匹配,再判断@Conditional的条件是否满足
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {for (TypeFilter tf : this.excludeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return false;}}for (TypeFilter tf : this.includeFilters) {if (tf.match(metadataReader, getMetadataReaderFactory())) {return isConditionMatch(metadataReader);}}return false;
}

4、注册配置类(Config.class)

  • register(componentClasses);将配置类转为BeanDefinition放入bean容器

在这里插入图片描述
在这里插入图片描述

  • 配置类bean添加到beanDefinitionMap中,与默认后置处理器存放在一起

在这里插入图片描述

三、总结

  • 本篇文章主要讲述刷新上下文前的准备工作
  • 创建bean工厂容器,也就是map对象,以后缓存单例对象
  • 添加常用注册bean和解析注解的后置处理器
  • new扫描包的类,根据包路径扫描Component的bean

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/8813.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Nginx05】Nginx学习:HTTP核心模块(二)Server

Nginx学习&#xff1a;HTTP核心模块&#xff08;二&#xff09;Server 第一个重要的子模块就是这个 Server 相关的模块。Server 代表服务的意思&#xff0c;其实就是这个 Nginx 的 HTTP 服务端所能提供的服务。或者更直白点说&#xff0c;就是虚拟主机的配置。通过 Server &…

iview切换Select时选项丢失,重置Seletc时选项丢失

分析原因 在旧版本的iview中如果和filterable一起使用时&#xff0c;当值清空选项或者使用重置按钮清空时选项会丢失。 解决方式一 把去掉filterable 解决方式二 使用ref&#xff0c;调用clearSingleSelect()方法清空 ref"perfSelect" this.$refs.perfSelect.c…

Java链式编程

一、链式编程 1.1.释义 链式编程&#xff0c;也叫级联式编程&#xff0c;调用对象的函数时返回一个this对象指向对象本身&#xff0c;达到链式效果&#xff0c;可以级联调用。 1.2.特点 可以通过一个方法调用多个方法&#xff0c;将多个方法调用链接起来&#xff0c;形成一…

UE4中创建的瞄准偏移或者混合空间无法拖入动画

UE4系列文章目录 文章目录 UE4系列文章目录前言一、解决办法 前言 UE4 AimOffset(瞄准偏移)动画融合时&#xff0c;AimOffse动画拖入不了融合框的解决办法&#xff0c;你会发现动画无法拖入到融合框&#xff0c;ue4编辑器提示“Invalid Additive animation Type”&#xff0c;…

C#核心知识回顾——10.List、Dictionary、数据结构

1.List List<int> list new List<int>(); List<String> strings new List<String>();//增list.Add(0);list.Add(1);List<int> ints new List<int>();ints.Add(0);list.AddRange(ints);//插入list.Insert(0, 1);// 位置0插入1//删//1.移…

使用GPIO来模拟UART

前言 最近在看一些秋招的笔试和面试题&#xff0c;刚好看到一个老哥的经验贴&#xff0c;他面试的时候被问到了如果芯片串口资源不够了该怎么办&#xff1f;其实可以用IO口来模拟串口&#xff0c;但我之前也没有具体用代码实现过&#xff0c;借此机会用32开发板上的两个IO口来…

从0开始,手写MySQL数据管理器DM

说在前面 从0开始&#xff0c;手写一个MySQL的学习价值在于&#xff1a; 可以深入地理解MySQL的内部机制和原理&#xff0c;MySQL可谓是面试的绝对重点和难点&#xff0c; 尼恩曾经指导过的一个7年经验小伙&#xff0c;凭借精通MySQL 搞定月薪40K。 从而更好地掌握MySQL的使…

前端实现pdf,图片,word文件预览

前端实现文件预览功能 需求&#xff1a;实现一个在线预览pdf、excel、word、图片等文件的功能。 介绍&#xff1a;支持pdf、xlsx、docx、jpg、png、jpeg。 以下使用Vue3代码实现所有功能&#xff0c;建议以下的预览文件标签可以在外层包裹一层弹窗。 图片预览 iframe标签能够将…

Vue3 JSX 插槽、v-model 的用法以及 React JSX 的区别

前言 写这篇文章的初衷是&#xff0c;Vue3 JSX 部分与 React JSX 容易混淆&#xff0c;比如如本文所说的 slot & v-model&#xff0c; 如果你是第一次接触 JSX&#xff0c;可先阅读前面写过的 React & JSX 日常用法与基本原则 来对 JSX 有一个整体的认知以及比较两者间…

架构重构|性能和扩展性大幅提升的Share Creators智能数字资产管理软件3.0

作为数字资产管理行业的领军者&#xff0c;Share Creators智能数字资产管理软件持续致力于帮助企业和团队智能化管理数字资产&#xff0c;提升工业化管线制作效率。经过本次重构&#xff0c;Share Creators 3.0版本重装上阵&#xff0c;全面更新的服务架构标志着软件整体性能的…

2023最新ChatGPT商业运营网站源码+支持ChatGPT4.0+新增GPT联网功能+支持ai绘画+实时语音识别输入+用户会员套餐+免费更新版本

2023最新ChatGPT商业运营网站源码支持ChatGPT4.0新增GPT联网功能支持ai绘画实时语音识别输入用户会员套餐免费更新版本 一、AI创作系统二、系统程序下载三、系统介绍四、安装教程五、主要功能展示六、更新日志 一、AI创作系统 提问&#xff1a;程序已经支持GPT3.5、GPT4.0接口…

磁盘镜像软件

什么是磁盘镜像 磁盘镜像是存储在计算机磁盘中的数据的副本或副本。磁盘镜像将包含数据存储设备的内容&#xff0c;并复制此类设备的结构。它还将包含操作系统分区。 磁盘镜像本质上是一种从主系统复制操作系统和存储在磁盘中的数据以将其分发到其他目标计算机的方法。自动化…