Анализ исходного кода Spring IoC (на основе аннотаций) 1

Java

1. Теория ИО

Полное название IoC — Inversion of Control, что переводится как «инверсия управления».Также у него есть псевдоним под названием DI (Dependency Injection), который называется Dependency Injection.

2. Метод ИОЦ

Spring предоставляет два способа для IoC: один основан на xml, а другой — на аннотациях.

  • теги для определения bean-компонентов для управления.
  • Аннотация @Bean используется для определения bean-компонентов и управления ими.

В этой статье мы проанализируем принцип IoC на основе аннотаций.Перед чтением статьи мы можем задать несколько вопросов, которые помогут нам лучше понять.

  1. Для чего нужен @Bean?
  2. Для чего нужны @Controller и @Service?
  3. Как работает аннотация @ComponentScan?
  4. Как Spring обнаруживает классы, измененные такими аннотациями, как @Bean, @Controller и @Service?
  5. Как он регистрируется в IOC-контейнере после обнаружения?
  6. Контейнер IOC в конце концов - GESHA?

3. Анализ исходного кода

Сначала посмотрите на следующий код:

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

AnnotationConfigApplicationContextВы можете реализовать классы конфигурации на основе Java (включая различные аннотации) для загрузки контекста приложения Spring. избегать использованияapplication.xmlнастроить. По сравнению с конфигурацией XML это более удобно.

3.1, схема структуры классов

Описание основного класса или интерфейса:

  • GenericApplicationContext- Общий контекст приложения, который внутри содержит экземпляр DefaultListableBeanFactory, этот класс реализует интерфейс BeanDefinitionRegistry и может использовать в нем любой модуль чтения определений компонентов. Типичный пример использования: регистрация определений bean-компонентов через интерфейс BeanFactoryRegistry, затем вызов метода refresh() для инициализации этих bean-компонентов с семантикой контекста приложения (org.springframework.context.ApplicationContextAware) и автоматическое обнаружение org.springframework.beans.factory. конфиг .BeanFactoryPostProcessor и т.д.

  • BeanDefinitionRegistry- Интерфейс реестра для хранения определений компонентов, таких как экземпляры RootBeanDefinition и ChildBeanDefinition. DefaultListableBeanFactory реализует этот интерфейс, поэтому вы можете зарегистрировать компоненты в beanFactory с помощью соответствующего метода. GenericApplicationContext имеет встроенный экземпляр DefaultListableBeanFactory, и его реализация этого интерфейса фактически реализована путем вызова соответствующего метода этого экземпляра.

  • AbstractApplicationContext—— Абстрактная реализация интерфейса ApplicationContext не определяет тип хранилища конфигурации, а реализует только функцию общего контекста. Эта реализация использует шаблон проектирования метода шаблона и требует, чтобы конкретные подклассы реализовывали его абстрактные методы. Экземпляры BeanFactoryPostProcessor, BeanPostProcessor и ApplicationListener автоматически регистрируются с помощью метода registerBeanPostProcessors() для обнаружения специальных компонентов в фабрике компонентов — анализ сравнение 1

  • AnnotationConfigRegistry- Аннотировать реестр конфигурации. Общий интерфейс для аннотирования контекстов приложений конфигурации с методом регистрации классов конфигурации и сканирования классов конфигурации.

3.2 Конструктор

        //默认构造函数,初始化一个空容器,容器不包含任何 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
	}

Основные атрибуты:

  • AnnotatedBeanDefinitionReader- Парсер BeanDefinition, используемый для анализа аннотированных компонентов.

  • ClassPathBeanDefinitionScanner-- bean-сканер для сканирования классов

  • Зарегистрируйте и проанализируйте входящий класс конфигурации (используйте метод конфигурации класса для анализа)

  • Вызовите метод обновления контейнера, чтобы инициализировать контейнер

Здесь мы используем последний конструктор, который должен передать путь к пакету.

3.3 Инициализация конструктора IoC

Сначала посмотрите на Step1, вызовите непреднамеренный конструктор этого класса:

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

затем инициализируйтеAnnotatedBeanDefinitionReaderа такжеClassPathBeanDefinitionScanner
Давайте взглянем на конструктор ClassPathBeanDefinitionScanner.

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

Продолжайте отслеживать и, наконец, вызовите этот метод:

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

Главное здесьregisterDefaultFilters()метод для инициализации правил фильтрации Spring Scan по умолчанию, соответствующих@ComponentScanОбратите внимание: если настраиваемых правил нет, инициализируются правила фильтрации по умолчанию.
Вот звонокClassPathScanningCandidateComponentProviderМетод registerDefaultFilters() в классе:

//向容器注册过滤规则
@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.
	}
}

Здесь есть две ключевые переменные:

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

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

includeFiltersУказывает аннотации, которые необходимо включить, то есть будут сканироваться только аннотации, включенные в includeFilters.
excludeFiltersУказывает исключаемые аннотации, то есть аннотации, включенные в excludeFilters, не будут сканироваться.

В этом методе коллекция includeFilters добавляет@Component、JavaEE6的@ManagedBean和JSR-330的@Named注解Коллекция excludeFilters не изменилась, то есть исключаемых аннотаций нет.

Суммировать:

Таким образом, правило по умолчанию заключается в том, что если включена любая из трех аннотаций @Component, @ManagedBean из JavaEE6 и @Named из JSR-330, они будут сканироваться.

В следующей статье мы рассмотрим конкретный процесс сканирования пакетов spring.Сканирование пакетов анализа исходного кода Spring IoC (на основе аннотаций)