Spring source code analysis-annotation-based IOC startup process (4)

Preface

In the previous chapter, we have analyzed the startup process of the IOC configured by XML. This chapter analyzes the startup process of the IOC based on the annotation configuration. With the previous foreshadowing, the analysis in this chapter will be relatively simple.

Spring annotation programming

I believe that older programmers have experienced the use of Spring's xml configuration to develop projects. A large number of complicated configurations increase the cumbersomeness of development. Spring has introduced annotation programming since 2.0 to replace complex XML configuration-namely JavaConfig. The Spring framework also uses a lot of annotations instead of XML configuration. For new programmers, they may directly use SpringBoot to develop projects. SpringBoot itself is based on Spring packaging. It is recommended to use a rapid development framework for fully-annotated programming. For example: in SpringBoot, the previous XML configuration has become an annotated @Configuration configuration class, let's get started with a simple annotation-based IOC

Step 1: create a class

public class MyBean {
}

Step 2: Define an annotation-based configuration

//定义一个基于注解的Spring配置类,相当于是Spring的xml配置
@Configuration
public class AppConfig {

    //向容器中注册一个Bean
    @Bean
    public MyBean myBean(){
        return new MyBean();
    }
}

The third step: use AnnotationConfigApplicationContext container factory loading location

public class MyBeanTest {

    @Test
    public void testMyBean(){
        //通过容器工厂:AnnotationConfigApplicationContext 加载配置
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
        //通过容器工厂获取MyBean
        MyBean myBean = applicationContext.getBean(MyBean.class);
        //打印myBean
        System.out.println(myBean);
    }
}

Container factory: AnnotationConfigApplicationContext

AnnotationConfigApplicationContext is an annotation-based IOC container factory that takes the configuration class annotated with @Configuration as input. It inherits the GenericApplicationContext container factory and AnnotationConfigRegistry annotation configuration register. The source code is as follows:

public class AnnotationConfigApplicationContext extends GenericApplicationContext implements AnnotationConfigRegistry {

	//基于注解方式的Bean的注册器,提供了注册Bean的方法
	private final AnnotatedBeanDefinitionReader reader;
	
	//bean 定义扫描器,它检测类路径上的 bean ,使用给定的注册器注册相应的 bean 定义。
	//通过可配置的类型过滤器检测候选类。 默认过滤器包括使用 Spring 的@Component 、 @Repository 、 @Service或@Controller型注释的@Component 。
	private final ClassPathBeanDefinitionScanner scanner;


	/**
	 * Create a new AnnotationConfigApplicationContext that needs to be populated
	 * through {@link #register} calls and then manually {@linkplain #refresh refreshed}.
	 */
	//创建Bean的注册器  和 Bean的扫描器
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
	//根据class注册一个类
	@Override
	public void register(Class<?>... componentClasses) {
		Assert.notEmpty(componentClasses, "At least one component class must be specified");
		this.reader.register(componentClasses);
	}

	//扫描一个包,注册多个类
	@Override
	public void scan(String... basePackages) {
		Assert.notEmpty(basePackages, "At least one base package must be specified");
		this.scanner.scan(basePackages);
	}

The following is the inheritance system:

Insert picture description here

AnnotatedBeanDefinitionReader

AnnotatedBeanDefinitionReaderIt is a Bean registrar based on the annotation method. It is responsible for encapsulating the class of the incoming class into a BeanDefinition, and parses the annotations of the class: @Scope, @Lazy, @Primary, etc., and then uses the BeanDefinitionRegistryregistered Bean.

public class AnnotatedBeanDefinitionReader {
	//Bean的注册器
	private final BeanDefinitionRegistry registry;
	//Bean的名字生成器
	private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
	//Scope元注解解析器
	private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();
	//根据多个class注册Bean
	public void register(Class<?>... componentClasses) {
		for (Class<?> componentClass : componentClasses) {
			registerBean(componentClass);
		}
	}
	//根据class注册一个 Bean
	public void registerBean(Class<?> beanClass) {
		doRegisterBean(beanClass, null, null, null);
	}
	...省略...

ClassPathBeanDefinitionScanner

The Bean scanner scans the annotated: @Component,@Service,@Controller,@Repositoryclass according to the given package path , and uses it to BeanDefinitionRegistryregister the Bean in the Spring container.

public class ClassPathBeanDefinitionScanner extends ClassPathScanningCandidateComponentProvider {

	private final BeanDefinitionRegistry registry;
	...省略...
	//扫描一个包
	public int scan(String... basePackages){
		...省略...
	}
	//注册一个Bean
	protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
	}

Start process: AnnotationConfigApplicationContext

In the previous chapters, the inheritance system of the IOC container factory has been introduced. I won't go into details here. Let's analyze the IOC process directly from the AnnotationConfigApplicationContext constructor. Below is the source code

	/**
		根据给定的配置类,创建新的AnnotationConfigApplicationContext容器工厂
	 * Create a new AnnotationConfigApplicationContext, deriving bean definitions
	 * from the given annotated classes and automatically refreshing the context.
	 * @param annotatedClasses one or more annotated classes,
	 * e.g. {@link Configuration @Configuration} classes
	 */
	 //annotatedClasses 是贴了@Configuration的Spring的配置类,
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		//创建 AnnotatedBeanDefinitionReader注册器 和 ClassPathBeanDefinitionScanner扫描器
		this();
		//执行注册,传入配置类
		register(annotatedClasses);
		//刷新容器
		refresh();
	}

From the parameters of the construction method, it can be seen that multiple configuration classes can be passed in here, and three things are done in the method

  • Call the constructor to create AnnotatedBeanDefinitionReader Bean registrar and ClassPathBeanDefinitionScanner Bean scanner
  • Call the register method to perform registration
  • Call the refresh method to refresh the container

[Note] The configuration class itself is also a Bean, and the purpose of the register method is to register the configuration class currently passed in.

With what AnnotationConfigApplicationContext#registermethod, source code is as follows:

@Override
public void register(Class<?>... componentClasses) {
	Assert.notEmpty(componentClasses, "At least one component class must be specified");
	//调用AnnotatedBeandefinitionReader 注册器注册Bean
	this.reader.register(componentClasses);
}

Bean registration AnnotatedBeanDefinitionReader

To the code AnnotatedBeanDefinitionReader#registermethod, the following source

public class AnnotatedBeanDefinitionReader {
	//BeanDefinition 的注册器
	private final BeanDefinitionRegistry registry;
	//名字生成器
	private BeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator();
	//scope 解析器
	private ScopeMetadataResolver scopeMetadataResolver = new AnnotationScopeMetadataResolver();

	...省略...
	//注册多个类
	public void register(Class<?>... componentClasses) {
		for (Class<?> componentClass : componentClasses) {
			registerBean(componentClass);
		}
	}

In the register method, multiple configuration classes are handed over to the registerBean method to register through the for method, and the code comes .AnnotatedBeanDefinitionReader#registerBean(java.lang.Class<?>)

public void registerBean(Class<?> beanClass) {
		doRegisterBean(beanClass, null, null, null);
	}

	//从给定的 bean 类注册一个 bean,从类声明的注释中获取其元数据
	<T> void doRegisterBean(Class<T> beanClass, @Nullable Supplier<T> instanceSupplier, @Nullable String name,
			@Nullable Class<? extends Annotation>[] qualifiers, BeanDefinitionCustomizer... definitionCustomizers) {
		//创建带注释的通用 Bean 定义
		AnnotatedGenericBeanDefinition abd = new AnnotatedGenericBeanDefinition(beanClass);
		//判断配置类上是否有 Conditional 条件注解,判断是否要跳过该Bean的注册
		if (this.conditionEvaluator.shouldSkip(abd.getMetadata())) {
			return;
		}

		abd.setInstanceSupplier(instanceSupplier);
		//通过 ScopeMetadataResolver 解析 当前Bean的@Scope注解,封装成 ScopeMetadata
		ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(abd);
		// scope属性,默认是 singleton 单利
		abd.setScope(scopeMetadata.getScopeName());
		//生成Bean的名字,如果从注解中没有获取到name,就生成唯一的默认 bean 名称。
		String beanName = (name != null ? name : this.beanNameGenerator.generateBeanName(abd, this.registry));
		//处理注解 Bean 定义中的通用注解 ,比如读取:lazy, @Primary,@DependsOn , Description等注解属性,然后设置到BeanDefinition对象
		AnnotationConfigUtils.processCommonDefinitionAnnotations(abd);
		//检查是否有而外的限定符注解
		if (qualifiers != null) {
			for (Class<? extends Annotation> qualifier : qualifiers) {
				//如果有@Primary注解,这标记该Bean是自动注入时的首选Bean
				if (Primary.class == qualifier) {
					abd.setPrimary(true);
				}
				//如果配置了@Lazy这设置为懒加载
				else if (Lazy.class == qualifier) {
					abd.setLazyInit(true);
				}
				else {
					//如果没有@Primary和Lazy配置,则指定自动注入时使用名称自动注入
					abd.addQualifier(new AutowireCandidateQualifier(qualifier));
				}
			}
		}
		for (BeanDefinitionCustomizer customizer : definitionCustomizers) {
			customizer.customize(abd);
		}
		//封装一个BeanDefinitionHolder,以beanName作为名字
		BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(abd, beanName);
		//Scope作用域,创建相应的代理对象
		definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
		//注册BeanDefinition
		BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, this.registry);
	}

The following things are done in this method

  • Create an AnnotatedGenericBeanDefinition for the current Bean
  • Analyze the @Scope annotation of the current Bean through the ScopeMetadataResolver meta-annotation parser, encapsulate it into ScopeMetadata, and then set the scope to BeanDefinition
  • Set the name for the Bean, if the name is not specified, a globally unique name is generated
  • Bean general annotations such as: lazy, @Primary, @DependsOn, Description and other annotation attributes
  • Create Scope proxy object
  • Call BeanDefinitionReaderUtils#registerBeanDefinition to register Beandefinition

As BeanDefinitionReaderUtils#registerBeanDefinitionin the previous chapter we have seen, its implementation is through BeanDefinitionRegistrythe BeanDefinition registered to DefaultListableBeanFactorymanage Bean container factory in the Map ( beanDefinitionMap = new ConcurrentHashMap(256))

At this point, the registration of the Bean of the configuration class is complete. Then some students will ask, what is the function of the MyBean registered in the configuration class AppConfig? It is called refresh () method finishBeanFactoryInitialization(beanFactory)to perform. In this method, the simple interest bean will be instantiated in advance, so I won't go into it here.

to sum up

This article is relatively simple, talked about the annotation-based AnnotationConfigApplicationContext(annotatedClasses)container plant start-up process, to sum up here

  1. Create AnnotatedBeanDefinitionReader Bean registrar and ClassPathBeanDefinitionScanner Bean scanner through the constructor
  2. Then encapsulate the incoming configuration class into AnnotatedGenericBeanDefinition, and parse the annotations of the class, such as: @Lazy, @Scope, @Primary and other attributes are set to BeanDefinition
  3. Call BeanDefinitionReaderUtils#registerBeanDefinition to register the BeanDefinition in a Bean management Map in DefaultListableBeanFactory

At the end of the article, in the next chapter we will continue to discuss another startup method of AnnotationConfigApplicationContext: the scanning method based on the package path. Please give a good comment if you like it, yours is definitely my biggest motivation! ! !