I. IoC theory

The IoC is known as the Inversion of Control, and it is also known as DI (Dependency Injection).

Two, IoC way

Spring provides two approaches for IoC, one based on XML and the other based on annotations.

  • Tag to define beans for management.
  • The @bean annotation defines beans for management.

In this article, we will analyze the principles of IoC based on annotations. Before we read the article, we can ask some questions to help us understand better.

  1. What does @bean do?
  2. What does @controller and @service do?
  3. How does the @compoentScan annotation work?
  4. How does Spring find classes decorated with @bean, @Controller, and @Service annotations?
  5. How do I register with the IOC container after discovery?
  6. What exactly is an IOC container?

Third, source code analysis

Let’s start with this code:

AnnotationConfigApplicationContext aac =
				new AnnotationConfigApplicationContext("com.mydemo");
Copy the code

AnnotationConfigApplicationContext can realize configuration based on Java classes (including various annotations) to load the Spring application context. Avoid using application.xml for configuration. More convenient than XML configuration.

3.1 Class structure diagram

  • GenericApplicationContext, general application context, hold a internal DefaultListableBeanFactory instances, this class implements a BeanDefinitionRegistry interface, You can use any bean Definition reader on it. Typical use cases are: Register bean Definitions through the BeanFactoryRegistry interface, Then call the refresh () method to initialize those with application context semantic (org. Springframework. Context. ApplicationContextAware) bean, Automatic detection of org. Springframework. Beans. Factory. Config. Spring BeanFactoryPostProcessor, etc.

  • BeanDefinitionRegistry — Registry interface for holding Bean Definitions like RootBeanDefinition and ChildBeanDefinition instances. DefaultListableBeanFactory implements this interface, so you can register it through the appropriate method to the beanFactory bean. GenericApplicationContext a built-in DefaultListableBeanFactory instance, it to the implementation of this interface is, in fact, by calling the corresponding method of this instance.

  • AbstractApplicationContext – ApplicationContext the abstract implementation of the interface, there is no mandatory configuration storage type, only to realize the function of general context. This implementation uses the template method design pattern and requires concrete subclasses to implement its abstract methods. Automatically by registerBeanPostProcessors registered spring BeanFactoryPostProcessor () method, Instances of BeanPostProcessor and ApplicationListener are used to detect special beans in the Bean Factory — contrast 1 analysis

  • AnnotationConfigRegistry — AnnotationConfiguration registry. A generic interface for annotating configuration application contexts, with a method for registering configuration classes and scanning configuration classes.

3.2 Constructors

        // The default constructor initializes an empty container that contains no Bean information and needs to be called later by calling its register()
	// method registers the configuration class and calls the refresh() method to refresh the container, triggering the loading, parsing, and registration of the annotation Bean by the container
	public AnnotationConfigApplicationContext(a) {
		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);
	}

	
	// The most commonly used constructor to automatically register beans in the corresponding configuration class into the container by passing the involved configuration class to the constructor
	public AnnotationConfigApplicationContext(Class
       ... annotatedClasses) {
		/ / call a no-parameter constructor, initialize AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner
		this(a); register(annotatedClasses); refresh(); }// The constructor automatically scans all classes under the given package and its subpackages, and automatically identifies all Spring beans and registers them with the container
	public AnnotationConfigApplicationContext(String... basePackages) {
	        / / initialize ClassPathBeanDefinitionScanner and AnnotatedBeanDefinitionReader
		this(a);//step1
		// Scan the package and register the bean
		scan(basePackages);//step2
	        refresh();//step3
	}
Copy the code

Main attributes:

  • AnnotatedBeanDefinitionReader – BeanDefinition parser to parse the annotated beans

  • ClassPathBeanDefinitionScanner – bean scanner Is used to scan the class

  • Register to resolve incoming configuration classes (using class configuration for resolution)

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

Here we use the last constructor, passing in a package path.

3.3 IoC constructor initialization

First look at step1, which calls the class’s no-argument constructor:

public AnnotationConfigApplicationContext(a) {
		this.reader = new AnnotatedBeanDefinitionReader(this);
		this.scanner = new ClassPathBeanDefinitionScanner(this);
	}
Copy the code

And then initializes AnnotatedBeanDefinitionReader and ClassPathBeanDefinitionScanner we’ll look at ClassPathBeanDefinitionScanner constructor

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

As we keep tracing, this method is finally called:

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

		Assert.notNull(registry, "BeanDefinitionRegistry must not be null");
		// Set the registry to load the Bean definition for the container
		this.registry = registry;

		// Whether to use the default filtering rules
		if (useDefaultFilters) {
			registerDefaultFilters();
		}
		// Set the environment
		setEnvironment(environment);
		// Set the resource loader for the container
		setResourceLoader(resourceLoader);
	}
Copy the code

The main one is registerDefaultFilters(), which initializes the default filters for Spring scanning, corresponding to the @ComponentScan annotation, and initializes the default filters if no custom rules exist. Here is called ClassPathScanningCandidateComponentProvider registerDefaultFilters in class () method:

// Register filtering rules with the container
@SuppressWarnings("unchecked")
protected void registerDefaultFilters(a) {
	// Add the @Component annotation class to the filter rule to include
	// @service and @Controller are both Components because they add the @Component annotation
	this.includeFilters.add(new AnnotationTypeFilter(Component.class));
	// Get the classloader for the current class
	ClassLoader cl = ClassPathScanningCandidateComponentProvider.class.getClassLoader();
	try {
		// Add JavaEE6's @ManagedBean annotation to the filter rule to include
		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 {
		// Add the @named annotation to the filter rule to include
		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.}}Copy the code

There are two key variables:

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

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

IncludeFilters indicate the annotations to be included, that is, only the annotations contained in the includeFilters will be scanned. ExcludeFilters indicate the annotations to be excluded

In this method, @Component, @ManagedBean for JavaEE6, and @named annotation for JSR-330 are added to the includeFilters collection, while the excludeFilters collection is left unchanged, i.e. there are no annotations to exclude

Conclusion:

So the default rule is that any annotation containing @Component, @ManagedBean in JavaEE6, or @named in JSR-330 will be scanned

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