Углубленный анализ принципа реализации аннотации @Autowired в Spring Framework
Углубленный анализ принципа реализации аннотации @Autowired в Spring Framework

краткое содержание

О роли аннотации @Autowired

@Autowired Роль аннотаций в Spring заключается в реализации внедрения зависимостей (Dependency Injection), который используется для автопроводки Spring Bean зависимости. Конкретно, @Autowired Аннотации имеют следующие функции:

  1. Зависимости автосвязывания:через поля в классе、Конструктор、Используется в параметрах метода и т. д. @Autowired аннотация,Spring Контейнер автоматически определит зависимости, которые необходимо внедрить, и применит соответствующие Bean Экземпляр внедряется в целевой компонент.
  2. Уменьшите количество ручной настройки:использовать @Autowired аннотация Может Уменьшите количество ручной работа зависимостей настроек,потому чтодля Он автоматически обнаруживает и управляет зависимостями между компонентами.,Это снижает сложность настройки.
  3. Улучшение ремонтопригодности@Autowired аннотация Четко определите зависимости классов,Сделайте код более простым для понимания и поддержки.,потому чтодля Он четко выражает взаимосвязь между компонентами。
  4. развязка:Передав внедрение зависимостей в Spring Обработка контейнеров обеспечивает слабую связь, что упрощает замену, расширение и тестирование компонентов, одновременно уменьшая связь между компонентами.
  5. Поддерживает несколько режимов сборки.@Autowired Предусмотрены различные режимы сборки, в том числе по типу, по имени, по квалификатору и т. д., для удовлетворения различных потребностей сборки.
Spring Frameworkи@Autowired

Наиболее важными концепциями Spring Framework являются IoC и DI. Благодаря этим двум функциям платформа может управлять зависимостями между объектами, создавать зависимости между объектами, а зависимые объекты можно автоматически внедрять в классы, которые в них нуждаются. При их использовании нет необходимости вручную создавать или находить зависимые объекты. Основные методы внедрения зависимостей следующие:

  • Внедрить через конфигурацию XML
  • Введено через @Autowired и др. аннотацию
  • В настоящее время Spring Framework рекомендует внедрение метода конструктора.

Независимо от того, какой метод инъекции,Spring получит элементы конфигурации компонента (определение компонента и зависимости).,Затем я проанализирую это на уровне исходного кода.@AutowiredПроцесс внедрения зависимостей。

Процесс создания бина

doCreateBean — создает экземпляр Bean
Язык кода:javascript
копировать
	/**
	 * Central method of this class: creates a bean instance,
	 * populates the bean instance, applies post-processors, etc.
	 * @see #doCreateBean
	 */
	@Override
	protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
			throws BeanCreationException {

    	//Пропускаем другие коды и показываем основной процесс
		try {
			Object beanInstance = doCreateBean(beanName, mbdToUse, args);
			if (logger.isTraceEnabled()) {
				logger.trace("Finished creating instance of bean '" + beanName + "'");
			}
			return beanInstance;
		}
		catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
			// A previously detected exception with proper bean creation context already,
			// or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(
					mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
		}
	}

создаватьBeanОсновная логика метода заключается в следующем.doCreateBeanсередина,Spring FrameworkпроходитьdoCreateBeanсоздавать指定Bean,В методе середина,ЧтосерединапроходитьpopulateBean()Обход соответствующего постпроцессора,То есть: когда класс, отмеченный аннотацией, вводится в контейнер Spring.,Сначала будет создан объект Bean.,создавать后调用populateBean方法以遍历后置处理器проходить后置处理器获取到需要изvalue,Воля@Autowiredаннотациясерединаизсвойство(Юаньданные)назначен наBeanсередина。

Постпроцессор populateBean-traversal
Язык кода:javascript
копировать
  	// Give any InstantiationAwareBeanPostProcessors the opportunity to modify the
    //InstantiationAwareBeanPostProcessors может обеспечить постобработку для @Autowiredаннотации,
		// state of the bean before properties are set. This can be used, for example,
		// to support styles of field injection.
		if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
      //Обходим все связанные постпроцессоры и получаем необходимое значение
			for (InstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().instantiationAware) {
				if (!bp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
					return;
				}
			}
		}
	}

@Autowiredаннотация所需из后置处理器是:AutowiredAnnotationBeanPostProcessor。СледующийSpring容器开始использовать该аннотацияиз后置处理器去获取对应изсвойствоvalue,Предположим, мы не знаем логику @Autowiredаннотации, соответствующей постпроцессору.,Затем, исходя из этого требования, угадайте имя метода соответствующей логики постпроцессора: у него должна быть обработка и атрибуты.,Тогда соответствующие слова: Процесс, Свойства.,Найдите соответствующий,postProcessProperties()это целевой метод。

Язык кода:javascript
копировать
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    //По данным BeanNameПолучить внедренные метаданные
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        //Элемент dataValue вводится в целевой Beanсередина
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

Получить внедренные метаданные

Язык кода:javascript
копировать
//Используется для кэширования данных элементов Bean, анализируемых Spring
private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);
Язык кода:javascript
копировать
	private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
		// Fall back to class name as cache key, for backwards compatibility with custom callers.
		String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
		// Запросить, существуют ли соответствующие данные элемента в кеше середина-Quick check on the concurrent map first, with minimal locking.
		InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
		if (InjectionMetadata.needsRefresh(metadata, clazz)) {
			synchronized (this.injectionMetadataCache) {
				metadata = this.injectionMetadataCache.get(cacheKey);
				if (InjectionMetadata.needsRefresh(metadata, clazz)) {
					if (metadata != null) {
						metadata.clear(pvs);
					}
                    //Когда кеш середина не содержит метаданных указанного компонента, создаем метаданные
					metadata = buildAutowiringMetadata(clazz);
					this.injectionMetadataCache.put(cacheKey, metadata);
				}
			}
		}
		return metadata;
	}
Как создать метаданные

Spring FrameworkпроходитьbuildAutowiringMetadata()Метод анализааннотациясерединаизданные。

Язык кода:javascript
копировать
//Комбинация аннотаций, которую необходимо разобрать
private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4);


if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
	return InjectionMetadata.EMPTY;
}
Язык кода:javascript
копировать
/**
	 * Create a new {@code AutowiredAnnotationBeanPostProcessor} for Spring's
	 * standard {@link Autowired @Autowired} and {@link Value @Value} annotations.
	 * <p>Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation,
	 * if available.
     * для Spring Создайте новый AutowiredAnnotationBeanPostProcessor для стандартных аннотаций @Autowired и @Value.
	 */
	@SuppressWarnings("unchecked")
	public AutowiredAnnotationBeanPostProcessor() {
		this.autowiredAnnotationTypes.add(Autowired.class);
		this.autowiredAnnotationTypes.add(Value.class);
		try {
			this.autowiredAnnotationTypes.add((Class<? extends Annotation>)
					ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader()));
			logger.trace("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring");
		}
		catch (ClassNotFoundException ex) {
			// JSR-330 API not available - simply skip.
		}
	}

Этот код сначала вызовет isCandidateClass Метод определяет, является ли текущий класс классом-кандидатом. Основанием для принятия решения является. добрый、свойство、Содержит ли методautowiredAnnotationTypes собиратьсередина初始化из值(@Autowired@Value@Inject),Когда определение середина Bean содержит множество середина соответствующего типа аннотация,судитьдля候选добрый,Затем получите данные элемента, соответствующие аннотациисередина для этого класса.

Если Bean середина не имеет указанного типа аннотации,Возвращает пустой объект Внедрение метаданных. Если указана аннотация,Затем начните получать данные аннотациисередина в юанях.

Способ получения метаданных — через отражение. Ниже представлена ​​логика получения соответствующих аннотаций в классах, свойствах и методах посредством отражения.

Возьмем в качестве примера DruidDataSourceWrapper:

Получить атрибуты в полях указанного класса посредством отражения
Язык кода:javascript
копировать
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
		if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
			return InjectionMetadata.EMPTY;
		}

		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;

		do {
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

            //Получить атрибуты в полях указанного класса посредством отражения
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				MergedAnnotation<?> ann = findAutowiredAnnotation(field);
				if (ann != null) {
					if (Modifier.isStatic(field.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static fields: " + field);
						}
						return;
					}
					boolean required = determineRequiredStatus(ann);
					currElements.add(new AutowiredFieldElement(field, required));
				}
			});
	}

Возьмем DruidDataSource в качестве примера.

Получить атрибуты в методе указанного класса посредством отражения.
Язык кода:javascript
копировать
         //Получить атрибуты в методе указанного класса посредством отражения.
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				MergedAnnotation<?> ann = findAutowiredAnnotation(bridgedMethod);
				if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					if (Modifier.isStatic(method.getModifiers())) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation is not supported on static methods: " + method);
						}
						return;
					}
					if (method.getParameterCount() == 0) {
						if (logger.isInfoEnabled()) {
							logger.info("Autowired annotation should only be used on methods with parameters: " +
									method);
						}
					}
					boolean required = determineRequiredStatus(ann);
					PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
					currElements.add(new AutowiredMethodElement(method, required, pd));
				}
			});

Внедрение метаданных

Получить значение из кэша

AutowiredAnnotationBeanPostProcessorдобрыйсерединаизinject方法用来注入Юаньданные。

Этот метод сначала получит данные элемента из кэша середина.,Если кэш середина не работает,затем выполнитеresolvedCachedArgumentАнализ значения поля。

Язык кода:javascript
копировать
		@Override
		protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
			Field field = (Field) this.member;
			Object value;
			if (this.cached) {
				try {
					value = resolvedCachedArgument(beanName, this.cachedFieldValue);
				}
				catch (NoSuchBeanDefinitionException ex) {
					// Unexpected removal of target bean for cached argument -> re-resolve
					value = resolveFieldValue(field, bean, beanName);
				}
			}
			else {
				value = resolveFieldValue(field, bean, beanName);
			}
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}
Анализ значения поля
Язык кода:javascript
копировать
@Nullable
private ConfigurableListableBeanFactory beanFactory;

@Nullable
		private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {
			DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
			desc.setContainingClass(bean.getClass());
			Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
			Assert.state(beanFactory != null, "No BeanFactory available");
			TypeConverter typeConverter = beanFactory.getTypeConverter();
			Object value;
			try {
                //Разбираем основной метод
				value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
			}
			catch (BeansException ex) {
				throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
			}
			synchronized (this) {
				if (!this.cached) {
					Object cachedFieldValue = null;
					if (value != null || this.required) {
						cachedFieldValue = desc;
						registerDependentBeans(beanName, autowiredBeanNames);
						if (autowiredBeanNames.size() == 1) {
							String autowiredBeanName = autowiredBeanNames.iterator().next();
							if (beanFactory.containsBean(autowiredBeanName) &&
									beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
								cachedFieldValue = new ShortcutDependencyDescriptor(
										desc, autowiredBeanName, field.getType());
							}
						}
					}
					this.cachedFieldValue = cachedFieldValue;
					this.cached = true;
				}
			}
			return value;
		}

DependencyDescriptor: представляет и обрабатывает зависимости между компонентами Bean.

resolveDependencyМетод — это интерфейсBeanFactory接口提供из,DefaultListableBeanFactory — это класс реализации BeanFactory.

resolveDependencyМетоды анализа и разрешения зависимостей,Функция этого метода основана на данном объекте DependencyDescriptor.,Разбирать и возвращать зависимые объекты разных типов,Нынешний подход в конечном итоге исчезнет.doResolveDependency

Язык кода:javascript
копировать
@Override
@Nullable
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
                                @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    if (Optional.class == descriptor.getDependencyType()) {
        return createOptionalDependency(descriptor, requestingBeanName);
    }
    else if (ObjectFactory.class == descriptor.getDependencyType() ||
             ObjectProvider.class == descriptor.getDependencyType()) {
        return new DependencyObjectProvider(descriptor, requestingBeanName);
    }
    else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
        return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    }
    else {
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
            descriptor, requestingBeanName);
        if (result == null) {
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}
метод doResolveDependency
Язык кода:javascript
копировать
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}

			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			if (matchingBeans.isEmpty()) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				return null;
			}

			String autowiredBeanName;
			Object instanceCandidate;

			if (matchingBeans.size() > 1) {
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				if (autowiredBeanName == null) {
					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
						return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
					}
					else {
						// In case of an optional Collection/Map, silently ignore a non-unique case:
						// possibly it was meant to be an empty collection of multiple regular beans
						// (before 4.3 in particular when we didn't even look for collection beans).
						return null;
					}
				}
				instanceCandidate = matchingBeans.get(autowiredBeanName);
			}
			else {
				// We have exactly one match.
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
				autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}

			if (autowiredBeanNames != null) {
				autowiredBeanNames.add(autowiredBeanName);
			}
			if (instanceCandidate instanceof Class) {
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}
			Object result = instanceCandidate;
			if (result instanceof NullBean) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				result = null;
			}
			if (!ClassUtils.isAssignableValue(type, result)) {
				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
			}
			return result;
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
  • solveMultipleBean: Как следует из названия, он разрешает несколько Bean-компонентов. Во время внедрения в текущем классе могут быть bean-компоненты разных типов, такие как bean-компоненты, массивы, коллекции, карты и т. д. Этот метод ищет и возвращает различные типы bean-компонентов.
  • findAutowireCandidates: поиск компонентов, соответствующих условиям. С помощью этого метода можно найти один или несколько компонентов.

Выше список объектов-кандидатов, соответствующих условиям, заполнен и введен.

boy illustration
Неразрушающее увеличение изображений одним щелчком мыши, чтобы сделать их более четкими артефактами искусственного интеллекта, включая руководства по установке и использованию.
boy illustration
Копикодер: этот инструмент отлично работает с Cursor, Bolt и V0! Предоставьте более качественные подсказки для разработки интерфейса (создание навигационного веб-сайта с использованием искусственного интеллекта).
boy illustration
Новый бесплатный RooCline превосходит Cline v3.1? ! Быстрее, умнее и лучше вилка Cline! (Независимое программирование AI, порог 0)
boy illustration
Разработав более 10 проектов с помощью Cursor, я собрал 10 примеров и 60 подсказок.
boy illustration
Я потратил 72 часа на изучение курсорных агентов, и вот неоспоримые факты, которыми я должен поделиться!
boy illustration
Идеальная интеграция Cursor и DeepSeek API
boy illustration
DeepSeek V3 снижает затраты на обучение больших моделей
boy illustration
Артефакт, увеличивающий количество очков: на основе улучшения характеристик препятствия малым целям Yolov8 (SEAM, MultiSEAM).
boy illustration
DeepSeek V3 раскручивался уже три дня. Сегодня я попробовал самопровозглашенную модель «ChatGPT».
boy illustration
Open Devin — инженер-программист искусственного интеллекта с открытым исходным кодом, который меньше программирует и больше создает.
boy illustration
Эксклюзивное оригинальное улучшение YOLOv8: собственная разработка SPPF | SPPF сочетается с воспринимаемой большой сверткой ядра UniRepLK, а свертка с большим ядром + без расширения улучшает восприимчивое поле
boy illustration
Популярное и подробное объяснение DeepSeek-V3: от его появления до преимуществ и сравнения с GPT-4o.
boy illustration
9 основных словесных инструкций по доработке академических работ с помощью ChatGPT, эффективных и практичных, которые стоит собрать
boy illustration
Вызовите deepseek в vscode для реализации программирования с помощью искусственного интеллекта.
boy illustration
Познакомьтесь с принципами сверточных нейронных сетей (CNN) в одной статье (суперподробно)
boy illustration
50,3 тыс. звезд! Immich: автономное решение для резервного копирования фотографий и видео, которое экономит деньги и избавляет от беспокойства.
boy illustration
Cloud Native|Практика: установка Dashbaord для K8s, графика неплохая
boy illustration
Краткий обзор статьи — использование синтетических данных при обучении больших моделей и оптимизации производительности
boy illustration
MiniPerplx: новая поисковая система искусственного интеллекта с открытым исходным кодом, спонсируемая xAI и Vercel.
boy illustration
Конструкция сервиса Synology Drive сочетает проникновение в интрасеть и синхронизацию папок заметок Obsidian в облаке.
boy illustration
Центр конфигурации————Накос
boy illustration
Начинаем с нуля при разработке в облаке Copilot: начать разработку с минимальным использованием кода стало проще
boy illustration
[Серия Docker] Docker создает мультиплатформенные образы: практика архитектуры Arm64
boy illustration
Обновление новых возможностей coze | Я использовал coze для создания апплета помощника по исправлению домашних заданий по математике
boy illustration
Советы по развертыванию Nginx: практическое создание статических веб-сайтов на облачных серверах
boy illustration
Feiniu fnos использует Docker для развертывания личного блокнота Notepad
boy illustration
Сверточная нейронная сеть VGG реализует классификацию изображений Cifar10 — практический опыт Pytorch
boy illustration
Начало работы с EdgeonePages — новым недорогим решением для хостинга веб-сайтов
boy illustration
[Зона легкого облачного игрового сервера] Управление игровыми архивами
boy illustration
Развертывание SpringCloud-проекта на базе Docker и Docker-Compose