Spring's IoC source code analysis (based on annotations)

1. IoC theory

The full name of IoC is Inversion of Control, which is translated as "inversion of control". It also has an alias called DI (Dependency Injection), which is Dependency Injection.

2. IoC method

Spring provides two ways for IoC, one is based on xml and the other is based on annotations.

  • <Bean> tag to define beans for management.
  • @Bean annotation is used to define beans and manage them.

In this article, we will analyze the IoC principle based on annotations. Before reading the article, we can ask some questions, which will help us better understand.

  1. What is @Bean for?
  2. What are @Controller and @Service for?
  3. How does the @ComponentScan annotation work?
  4. How does Spring discover classes modified by annotations such as @Bean, @Controller, and @Service?
  5. How is it registered in the IOC container after discovery?
  6. What exactly is an IOC container?

3. Source code analysis

First look at the following code:

AnnotationConfigApplicationContext aac =
				new AnnotationConfigApplicationContext("com.mydemo");

AnnotationConfigApplicationContext can implement Java-based configuration classes (including various annotations) to load Spring's application context. Avoid using application.xml for configuration. Compared with XML configuration, it is more convenient.

3.1, class structure diagram

Main class or interface description:

  • GenericApplicationContext - Generic application context, which holds an instance of DefaultListableBeanFactory internally. This class implements the BeanDefinitionRegistry interface and can use any bean definition reader on it. A typical use case is: register bean definitions through the BeanFactoryRegistry interface, then call the refresh() method to initialize those beans with application context semantics (org.springframework.context.ApplicationContextAware), and automatically detect org.springframework.beans.factory.config .BeanFactoryPostProcessor etc.

  • BeanDefinitionRegistry - Registry interface for holding bean definitions like RootBeanDefinition and ChildBeanDefinition instances. DefaultListableBeanFactory implements this interface, so you can register beans in the beanFactory through the corresponding method. GenericApplicationContext has a built-in DefaultListableBeanFactory instance, and its implementation of this interface is actually realized by calling the corresponding method of this instance.

  • AbstractApplicationContext——The abstract implementation of the ApplicationContext interface does not mandate the storage type of the configuration, but only implements the general context function. This implementation uses the template method design pattern and requires concrete subclasses to implement its abstract methods. Instances of BeanFactoryPostProcessor, BeanPostProcessor and ApplicationListener are automatically registered through the registerBeanPostProcessors() method to detect special beans in the bean factory - comparison 1 analysis

  • AnnotationConfigRegistry - Annotation configuration registry. Generic interface for annotating configuration application contexts, with a method for registering configuration classes and scanning configuration classes.

3.2 Constructor

        //默认构造函数,初始化一个空容器,容器不包含任何 Bean 信息,需要在稍后通过调用其register()
	//方法注册配置类,并调用refresh()方法刷新容器,触发容器对注解Bean的载入、解析和注册过程
	public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	
	public AnnotationConfigApplicationContext(DefaultListableBeanFactory beanFactory) {
		super(beanFactory);
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

	
	//最常用的构造函数,通过将涉及到的配置类传递给该构造函数,以实现将相应配置类中的Bean自动注册到容器中
	public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {
		//调用无参构造函数,初始化AnnotatedBeanDefinitionReader 和 ClassPathBeanDefinitionScanner
		this();
		register(annotatedClasses);
		refresh();
	}

	
	//该构造函数会自动扫描以给定的包及其子包下的所有类,并自动识别所有的Spring Bean,将其注册到容器中
	public AnnotationConfigApplicationContext(String... basePackages) {
	        //初始化ClassPathBeanDefinitionScanner和AnnotatedBeanDefinitionReader
		this();//step1
		//扫描包、注册bean
		scan(basePackages);//step2
	        refresh();//step3
	}

Main properties:

  • AnnotatedBeanDefinitionReader - BeanDefinition parser used to parse annotated beans

  • ClassPathBeanDefinitionScanner - bean scanner used to scan classes

  • Register and parse the incoming configuration class (use the class configuration method for parsing)

  • Call the refresh method of the container to initialize the container

Here we use the last constructor, which is to pass in a package path.

3.3 IoC constructor initialization

First look at step1, calling the no-argument constructor of this class:

public AnnotationConfigApplicationContext() {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}

Then initialize AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner
Let's take a look at the constructor of ClassPathBeanDefinitionScanner

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry) {
		this(registry, true);
	}

Continue to trace, and finally call this method:

public ClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters,
			Environment environment, @Nullable ResourceLoader resourceLoader) {

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		//为容器设置加载Bean定义的注册器
		this.registry = registry;

		//是否使用默认过滤规则
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		//设置环境
		setEnvironment(environment);
		//为容器设置资源加载器
		setResourceLoader(resourceLoader);
	}

The most important thing here is the registerDefaultFilters() method, which initializes the default filtering rules for spring scanning, corresponding to the @ComponentScan annotation. If there are no custom rules, the default filtering rules are initialized.
What is called here is the registerDefaultFilters() method in the ClassPathScanningCandidateComponentProvider class:

//向容器注册过滤规则
@SuppressWarnings("unchecked")
protected void registerDefaultFilters() {
	//向要包含的过滤规则中添加@Component注解类
	//@Service和@Controller都是Component,因为这些注解都添加了@Component注解
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	//获取当前类的类加载器
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
		//向要包含的过滤规则添加JavaEE6的@ManagedBean注解
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.annotation.ManagedBean", cl)), false));
		logger.debug("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 {
		//向要包含的过滤规则添加@Named注解
		this.includeFilters.add(new AnnotationTypeFilter(
				((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Named", cl)), false));
		logger.debug("JSR-330 'javax.inject.Named' annotation found and supported for component scanning");
	}
	catch (ClassNotFoundException ex) {
		// JSR-330 API not available - simply skip.
	}
}

There are two key variables here:

  • private final List<TypeFilter> includeFilters = new LinkedList<>();

  • private final List<TypeFilter> excludeFilters = new LinkedList<>();

includeFilters indicates the annotations to be included, that is, only the annotations in includeFilters will be scanned.
excludeFilters indicates the annotations to be excluded, that is, the annotations included in excludeFilters will not be scanned

In this method, @Component, @ManagedBean of JavaEE6 and @Named of JSR-330 are added to the includeFilters collection,
and the excludeFilters collection has not changed, that is, there are no annotations to be excluded.

Summary:
So the default rule is that as long as any of the three annotations @Component, @ManagedBean of JavaEE6 and @Named of JSR-330 are included, they will be scanned

In the next article, we will take a look at the specific package scanning process of spring Spring IoC source code analysis (annotation-based) package scanning

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324197159&siteId=291194637