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

news2025/1/10 17:00:15

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);
        //获取某个bean
        Book 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.注册事件监听后置处理器 EventListenerMethodProcessor
	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));
	}
	// 3.6.注册事件监听工厂 DefaultEventListenerFactory
	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;
}
  • 查看一共注册的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 @ManagedBean
		this.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 @Named
		this.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);
		// 遍历BeanDefinition
		for (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*:/**/*.class
		String 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);
				// 判断是不是一个bean
				if (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.coloradmin.cn/o/721206.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

计算机网络概述(三)

常见的计算机网络体系结构 OSI体系结构&#xff1a; 物理层→数据链路层→网络层→运输层→会话层→表示层→应用层 TCP/IP体系结构&#xff1a; 网络接口层→网际层→运输层→应用层 一般用户的设备都有TCP/IP协议用于连接因特网&#xff0c;TCP/IP的网络接口层并没有规定使用…

Linux基础+html和script一些基本语法

文章目录 linux 基础名字含义指令 html 语法style 样式属性样式标签属性颜色margin 边距ransform 旋转角度重复样式opacity 透明度div 方块元素box-shadow 阴影属性浮动 script获取节点onclick 点击触发setTimeout 定时器利用定时器实现 动画效果 javascript强弱语言区分parseI…

简单详细的MySQL数据库结构及yum和通用二进制安装mysql的方法

目录 mysql体系结构mysql的安装方法一&#xff0c;yum安装1&#xff0c;首先下载一个网络源仓库&#xff1a;2&#xff0c;然后安装 mysql-community-server3&#xff0c;启动mysqld 服务4&#xff0c;然后登录数据库5&#xff0c;初次登录要设置密码&#xff0c;而且不能太简单…

小型电子声光礼花器电子烟花爆竹电路设计

节日和庆典时燃放礼花&#xff0c;其绚丽缤纷的图案&#xff0c;热烈的爆炸声、欢乐的气氛&#xff0c;能给人们留下美好的印象&#xff0c;但有一定的烟尘污染和爆炸危险隐患。本电路可以模拟礼花燃放装置&#xff0c;达到声型兼备的效果&#xff0c;给人们在安全、环保的环境…

redis rehash

dict结构 dictEntry即键值对&#xff0c;每个桶就是dictEntry连接的链表 typedef struct dictEntry {void *key;union {void *val; // 自定义类型uint64_t u64;int64_t s64;double d;} v;struct dictEntry *next; } dictEntry;数据真正指向的地方 typedef struct dictht {di…

京东网站登录二维码显示不出来

环境&#xff1a; 360急速浏览器 Win10专业版 问题描述&#xff1a; 京东网站登录二维码显示不出来 解决方案&#xff1a; 1.打开安全卫士 2.功能大全找到断网急救箱 3.全面诊断一下有问题修复一下&#xff0c;重启浏览器解决

数字迷宫:探秘统计位数为偶数的奇妙世界

本篇博客会讲解力扣“1295. 统计位数为偶数的数字”的解题思路&#xff0c;这是题目链接。 统计位数是偶数的数据个数&#xff0c;关键在于如何统计一个整数的位数。方法是&#xff1a;对于一个整数n&#xff0c;每次/10&#xff0c;都会缩小一位&#xff0c;所以一直进行/10操…

【爬虫】AOI

目前几个大厂&#xff0c;高德百度腾讯&#xff0c;都支持POI爬取&#xff0c;而AOI是需要自己找接口的。 换言之&#xff0c;爬虫需谨慎 1 百度AOI 参考链接是&#xff1a; 这两个链接是选定范围爬取范围内选定类别的AOI 黑科技 | 百度地图抓取地块功能&#xff08;上&#x…

DeepSpeed-Chat 打造类ChatGPT全流程 笔记二之监督指令微调

文章目录 系列文章0x0. 前言0x1. &#x1f415; Supervised finetuning (SFT) 教程翻译&#x1f3c3; 如何训练模型&#x1f3c3; 如何对SFT checkpoint进行评测?&#x1f481; 模型和数据☀️来自OPT-1.3B及其SFT变体&#xff08;使用不同微调数据&#xff09;的提示示例☀️…

关于layui实现按钮点击添加行的功能

关于layui实现按钮点击添加行的功能 实现效果 代码实现 <!DOCTYPE html> <html lang"zh" xmlns:th"http://www.thymeleaf.org"> <head><meta charset"UTF-8"><title>Title</title><link rel"styl…

帅气的头像-InsCode Stable Diffusion 美图活动一期

1.运行地址 Stable Diffusion 模型在线使用地址&#xff1a; https://inscode.csdn.net/inscode/Stable-Diffusion 界面截图&#xff1a; 2.模型版本及相关配置 模型&#xff1a;chilloutmix-Ni.safetensor [7234b76e42] 采样迭代步数&#xff08;steps&#xff09;: 30 采样…

QtDesigner的使用

QtDesigner的使用 1、快速入门2、布局管理 1、快速入门 主窗口 菜单栏、工具栏、状态栏 快捷预览方式&#xff0c;工具箱 对象查看器 属性编辑器 英文名作用objectName控件对象名称geometry相对坐标系sizePolicy控件大小策略minnimumSize最小宽度、高度maximumSize最大宽度…

基于jsp+Servlet+mysql学生信息管理系统V2.0

基于jspServletmysql学生信息管理系统V2.0 一、系统介绍二、功能展示1.项目骨架2.数据库表3.项目内容4.登陆界面5.学生-学生信息6、学生-修改密码7、管理员-学生管理8、管理员-添加学生9.管理员-修改学生信息10.管理员-班级信息11.管理员-教师信息 四、其它1.其他系统实现五.获…

旅游卡系统旅行社小程序APP

旅游业的不断发展&#xff0c;旅游卡系统、旅行社小程序APP等数字化工具已经成为了旅行社提升业务效率、提高客户体验的重要手段。下面&#xff0c;我们将为您介绍旅游卡系统旅行社小程序APP的相关内容。 一、旅游卡系统 旅游卡系统是一种将旅游门票、优惠券等资源整合…

实时包裹信息同步:WebSocket 在 Mendix 中的应用

场景介绍 在现代物流中&#xff0c;能够实时跟踪包裹信息&#xff0c;尤其是包裹重量&#xff0c;是非常重要的。在这种场景中&#xff0c;我们可以使用称重设备获取包裹的信息&#xff0c;然后实时将这些信息同步给 Mendix 开发的 App&#xff0c;并在 App 的页面上实时显示包…

获取java对象被更新的属性和值

业务场景 更新User信息后&#xff0c;需要收集哪些字段的哪些值被更新了。 思路 比较originUser和newUser&#xff0c;收集值不同的属性以及变化前后的值。 代码实现 public static Map<String, Map<String, Object>> getChange(Object originObject, Object ne…

Java 9 - 模块化系统

定义要导出的模块 package com.lfsun.java9study.mypackage;public class MyClass {public static void sayHello() {System.out.println("Hello from com.lfsun.java9study.mypackage!");}public static void main(String[] args) {System.out.println("test&…

【Linux】LVS负载均衡群集 DR模式

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 LVS负载均衡群集 DR模式 数据包流向分析DR 模式的特点LVS-DR中的ARP问题VIP地址相同导致响应冲突返回报文时源地址使用VIP&#xff0c;导致网关设备的ARP缓存表紊乱 DR模式 L…

Pycharm配置解释器(interpreter)

关于pycharm编译器的解释器&#xff0c;网友朋友的分享 Pycharm配置&#xff08;1&#xff09;——解释器&#xff08;interpreter&#xff09; 详细了解PyCharm支持的4种Python Interpreter和配置方法 对大多数人而言就只需要 分清虚拟解释器和系统解释器&#xff0c;使用虚拟…

关闭Win10的预览窗口

关闭Win10的预览窗口 每次拖拽文件都显示那个黑边的模型一样的东西&#xff0c;通过上面可以关闭该功能。