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 — 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);
}