spring学习源码第一课

spring源码学习:

启动地方法。这里使用AnnotationConfigApplicationContext,注解配置应用程序对象。这里是可以全局初始化spring。所有操作基本上都在这个对象中。

package com.luoyan.test;

import com.luoyan.app.AppConfig;
import com.luoyan.dao.IndexDao;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @Author luoyan
 * @Description:
 * @Date 2024年03月14日 21:28
 * @Version: V1.0
 */
public class Test20240316 {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
		IndexDao bean = applicationContext.getBean(IndexDao.class);
		bean.query();
	}
}

此类org.springframework.context.annotation.AnnotationConfigApplicationContext,用于存放了spring的上下文对象。非常重要。
这个类继承了很多类
org.springframework.context.support.GenericApplicationContext:spring中bean的初始化对象,也就是beanDefinition,描述一个bean使用的。
org.springframework.context.support.AbstractApplicationContext:
org.springframework.core.io.DefaultResourceLoader 接口:
org.springframework.core.io.ResourceLoader
org.springframework.context.ConfigurableApplicationContext
org.springframework.context.annotation.AnnotationConfigRegistry
这里是spring容器类的构造参数对象:

/**
 1. 这个构造方法需要传入一个被javaconfig注解了的配置类
 2. 然后会把这个被注解了javaconfig的类通过注解读取器读取后继而解析
 3. Create a new AnnotationConfigApplicationContext, deriving com.luoyan.bean definitions
 4. from the given component classes and automatically refreshing the context.
 5. @param componentClasses one or more component classes — for example,
 6. {@link Configuration @Configuration} classes
 */
public AnnotationConfigApplicationContext(Class<?>... componentClasses) {
	//TODO: 面试点:当我们调用一个类的构造方法时,一定要看他是否有父类,如果有父类一定会去调用父类的构造方法.
	/**
	 * annotatedClasses appconfig.class
	 * 这里由于他有父类,故而会先调用父类的构造方法,然后才会调用自己的构造方法
	 * 在自己的构造方法中初始化一个读取器(读取所有的class文件)和扫描器(把所有的bd都扫描出来注入到容器中)
	 */
	this();
	//注册我们的配置类
	register(componentClasses);
	//IOC容器刷新接口,初始化Spring的环境,包含了bean的实例化
	refresh();
}

这里调用this点击以后进入的方法。这个方法会初始换spring的读取器和扫描器

	/**
	 * 初始化一个bean的读取和扫描器
	 * 合为读取器和扫描器参考上面的属性注释
	 * 默认构造函数,如果直接调用这个默认构造方法,需要在稍后通过调用其register()
	 * 去注册配置类(javaconfig),并调用refresh()方法刷新容器,
	 * 触发容器对胡姐Bean的载入,解析和注册过程
	 * 这种使用过程我在ioc应用的第二节课讲@profile的时候讲过
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
	 */
	public AnnotationConfigApplicationContext() {
		/**
		 * 父类的构造方法
		 * 创建一个读取注解的Bean定义读取器
		 * 什么是bean定义?BeanDefinition
		 *
		 * this代表当前的类也就是AnnotationConfigApplicationContext类
		 * 被注解的BeanDefinition:被注解的Bean定义
		 *
		 * bean的解析方法,
		 * 1、注解
		 * 2、xml
		 * 3、@Bean
		 * 4、spring内部的bean
		 */
		this.reader = new AnnotatedBeanDefinitionReader(this);
		/**
		 * 可以用来扫描包或者类,继而转换成bd
		 * 但是实际上我们扫描包工作并不是scanner这个对象来完成的
		 * 是spring自己new的一个ClassPathBeanDefinitionScanner
		 * 这里的scanner仅仅是为了程序员能够在外部调用AnnotationConfigApplicationContext对象的scan方法
		 * 描述自己的那个包下面的需要扫描,不用注解的方式.
		 */
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

这里可以看到是一个读取器

/**
 * 这里的BeanDefinitionRegistry registry是通过在AnnotatioinConfigApplicationContext
 * 的构造方法中传进来的this
 * 由此说明AnnotationConfigApplicationContext是一个BeanDefinitionRegistry类型的类
 * 何以证明我们可以看到AnnotationConfigApplicationContext的类关系
 * GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry
 * BeanDefinitionRegistry顾名思义就是BeanDefinition的注册器
 * 那么何为BeanDefinition呢?参考BeanDefinition的源码的注释
 * Create a new {@code AnnotatedBeanDefinitionReader} for the given registry.
 * <p>If the registry is {@link EnvironmentCapable}, e.g. is an {@code ApplicationContext},
 * the {@link Environment} will be inherited, otherwise a new
 * {@link StandardEnvironment} will be created and used.
 * @param registry the {@code BeanFactory} to load com.luoyan.bean definitions into,
 * in the form of a {@code BeanDefinitionRegistry}
 * @see #AnnotatedBeanDefinitionReader(BeanDefinitionRegistry, Environment)
 * @see #setEnvironment(Environment)
 */
public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
	this(registry, getOrCreateEnvironment(registry));
}

这里是一个扫描器:

/**
 * Create a new {@code ClassPathBeanDefinitionScanner} for the given com.luoyan.bean factory.
 * @param registry the {@code BeanFactory} to load com.luoyan.bean definitions into, in the form
 * of a {@code BeanDefinitionRegistry}
 */
public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
	this(registry, true);
}

register(componentClasses);方法。使用读取器。读取传入的配置的类。

/**
 * 注册单个bean给容器
 * 比如有新加的类可以用这个方法
 * 但是注册注册之后需要手动调用refresh方法去出发容器解析注解
 *
 * 有两个意思:
 * 1、它可以注册一个配置类
 * 2、它还可以单独注册一个bean
 * Register one or more component classes to be processed.
 * <p>Note that {@link #refresh()} must be called in order for the context
 * to fully process the new classes.
 * @param componentClasses one or more component classes &mdash; for example,
 *                         这是自己传入进来的AppConfig配置类
 * {@link Configuration @Configuration} classes
 * @see #scan(String...)
 * @see #refresh()
 */
@Override
public void register(Class<?>... componentClasses) {
	Assert.notEmpty(componentClasses, "At least one component class must be specified");
	//TODO: 读取器注册,读取夹了Annotation注解的类
	this.reader.register(componentClasses);
}

这里取循环。因为可能传入多个。

/**
 * Register one or more component classes to be processed.
 * <p>Calls to {@code register} are idempotent; adding the same
 * component class more than once has no additional effect.
 * @param componentClasses one or more component classes,
 * e.g. {@link Configuration @Configuration} classes
 */
public void register(Class<?>... componentClasses) {
	for (Class<?> componentClass : componentClasses) {
		registerBean(componentClass);
	}
}

此方法非常重要,这个方法,需要新开一篇讲解。

/**
 * Register a com.luoyan.bean from the given com.luoyan.bean class, deriving its metadata from
 * class-declared annotations.
 * @param beanClass the class of the com.luoyan.bean
 * @param name an explicit name for the com.luoyan.bean
 * @param qualifiers specific qualifier annotations to consider, if any,
 * in addition to qualifiers at the com.luoyan.bean class level
 * @param supplier a callback for creating an instance of the com.luoyan.bean
 * (may be {@code null})
 * @param customizers one or more callbacks for customizing the factory's
 * {@link BeanDefinition}, e.g. setting a lazy-init or primary flag
 * @since 5.0
 *
 * 用来解析配置类中的东西。
 */
private <T> void doRegisterBean(Class<T> beanClass, @Nullable String name,
		@Nullable Class<? extends Annotation>[] qualifiers, @Nullable Supplier<T> supplier,
		@Nullable BeanDefinitionCustomizer[] customizers) {

	/**
	 * 根据指定的bean创建一个AnnotatedGenericBeanDefinition
	 * 这个AnnotatedGenericBeanDefinition可以理解为一个数据结构
	 * AnnotatedGennericBeanDefinition包含了类的其他信息,必须一些源信息
	 * scope,lazy等等
	 *
	 * AnnotatedGenericBeanDefinition:是一个包含了bean的信息的类
	 * AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition
	 *
	 * GenericBeanDefinition extends AbstractBeanDefinition
	 * AbstractBeanDefinition implements BeanDefinition
	 *
	 * AnnotatedBeanDefinition extends BeanDefinition
	 *
	 * AnnotatedGenericBeanDefinition:这个类表示被注解的BeanDefinition
	 *
	 */
	AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);

	//判断是否有注解解析
	/**
	 * 判断这个类是否需要跳过解析
	 * 通过代码可以知道spring判断是否跳过解析,主要判断类有没有加注解
	 *
	 * Conditional:是springBoot中大量存在的.
	 */
	if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
		return;
	}
	abd.setInstanceSupplier(supplier);
	/**
	 * 得到bean的作用域,这里获取到bean的作用域然后放到add中(add是bean的描述)
	 * scope:singote
	 */
	ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
	/**
	 * 把类的作用域添加到数据结构中
	 */
	abd.setScope(scopeMetadata.getScopeName());
	/**
	 * 生成类的名字通过beanNameGenerator记得布置过一个作业
	 */
	String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
	/**
	 * 处理类当中的通过注解
	 * 分析源码可以知道他主要处理
	 * Lazy DapendsOn primary Role等等注解
	 * 处理完成知乎processCommonDefinitionAnnotations中依然是把他添加到数据结构当中
	 */
	AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);

	/**
	 * 如果在向容器注册注解Bean定义时,使用了额外的限定符注解则解析
	 * 关于Qualifier和Primary前面的课中讲过,主要涉及到spring的自动装配
	 * 这里需要注意的
	 * byName和qualifiers这个变量时Annotation类型的数组,里面存不仅仅是Qualifier注解
	 * 理论上里面存的是一切注解,所以可以看到下面的代码spring去循环了这个数组
	 * 然后依次判断了注解当中是否包含了Primary,是否包含了lazyd
	 */
	if (qualifiers != null) {
		for (Class<? extends Annotation> qualifier : qualifiers) {
			//如果配置了@Primary注解,如果加了则作为首选
			if (Primary.class == qualifier) {
				abd.setPrimary(true);
			}
			//懒加载,前面讲过
			else if (Lazy.class == qualifier) {
				abd.setLazyInit(true);
			}
			else {
				//如果使用了除了@Primary和@Lazy以外的其他注解,则为改Bean添加一个根据名称自动装配的限定符
				//这里难以理解,后面会详细介绍
				abd.addQualifier(new AutowireCandidateQualifier(qualifier));
			}
		}
	}
	if (customizers != null) {
		for (BeanDefinitionCustomizer customizer : customizers) {
			customizer.customize(abd);
		}
	}

	/**
	 *这个BeanDefinitionHolder也是一个数据结构
	 */
	BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);

	/**
	 * 这个是spring比较重要的知识,必须要去学习一下
	 * ScopedProxyMode 这个知识点比较复杂,需要结合web去理解
	 * 可以暂时放一下,等说到springmvc的时候再说
	 * 或者看情况现在说也是一样的
	 */
	definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);

	/**
	 * 把上述的这个数据结构注册给registry
	 * registry就是AnnotationConfigApplicationContext
	 * AnnotationConfigApplicationContext在初始化的时候通过调用父类的构造方法
	 * 实例化一个DefaultListableBeanFactory
	 * registerBeanDefinition里面就是把definitionHolder这个数据结构包含的鑫鑫注册到DefaultListableBeanFactory这个工程
	 */
	BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
}

相关推荐

  1. spring学习第一

    2024-03-18 22:16:03       32 阅读
  2. Spring学习-Resource

    2024-03-18 22:16:03       29 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-03-18 22:16:03       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-03-18 22:16:03       101 阅读
  3. 在Django里面运行非项目文件

    2024-03-18 22:16:03       82 阅读
  4. Python语言-面向对象

    2024-03-18 22:16:03       91 阅读

热门阅读

  1. kali wpscan 命令

    2024-03-18 22:16:03       43 阅读
  2. 自动部署SSL证书到阿里云腾讯云CDN

    2024-03-18 22:16:03       42 阅读
  3. 【CSP考点回顾】C++标准库加速输入输出

    2024-03-18 22:16:03       48 阅读
  4. 通过Https请求可以返回哪些数据?

    2024-03-18 22:16:03       41 阅读
  5. playwright test打开新的浏览器窗口和关闭当前窗口

    2024-03-18 22:16:03       38 阅读
  6. C语言基础知识笔记

    2024-03-18 22:16:03       47 阅读
  7. 字节一面二面三面四面

    2024-03-18 22:16:03       50 阅读
  8. 大模型在自动驾驶决策系统中的前沿应用

    2024-03-18 22:16:03       43 阅读
  9. kafka什么情况下会认为发送失败进而去重试

    2024-03-18 22:16:03       46 阅读
  10. 【力扣】75.颜色分类

    2024-03-18 22:16:03       43 阅读