Углубленный анализ принципа реализации аннотации @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
Учебное пособие по Jetpack Compose для начинающих, базовые элементы управления и макет
boy illustration
Код js веб-страницы, фон частицы, код спецэффектов
boy illustration
【новый! Суперподробное】Полное руководство по свойствам компонентов Figma.
boy illustration
🎉Обязательно к прочтению новичкам: полное руководство по написанию мини-программ WeChat с использованием программного обеспечения Cursor.
boy illustration
[Забавный проект Docker] VoceChat — еще одно приложение для мгновенного чата (IM)! Может быть встроен в любую веб-страницу!
boy illustration
Как реализовать переход по странице в HTML (html переходит на указанную страницу)
boy illustration
Как решить проблему зависания и низкой скорости при установке зависимостей с помощью npm. Существуют ли доступные источники npm, которые могут решить эту проблему?
boy illustration
Серия From Zero to Fun: Uni-App WeChat Payment Practice WeChat авторизует вход в систему и украшает страницу заказа, создает интерфейс заказа и инициирует запрос заказа
boy illustration
Серия uni-app: uni.navigateЧтобы передать скачок значения
boy illustration
Апплет WeChat настраивает верхнюю панель навигации и адаптируется к различным моделям.
boy illustration
JS-время конвертации
boy illustration
Обеспечьте бесперебойную работу ChromeDriver 125: советы по решению проблемы chromedriver.exe не найдены
boy illustration
Поле комментария, щелчок мышью, специальные эффекты, js-код
boy illustration
Объект массива перемещения объекта JS
boy illustration
Как открыть разрешение на позиционирование апплета WeChat_Как использовать WeChat для определения местонахождения друзей
boy illustration
Я даю вам два набора из 18 простых в использовании фонов холста Power BI, так что вам больше не придется возиться с цветами!
boy illustration
Получить текущее время в js_Как динамически отображать дату и время в js
boy illustration
Вам необходимо изучить сочетания клавиш vsCode для форматирования и организации кода, чтобы вам больше не приходилось настраивать формат вручную.
boy illustration
У ChatGPT большое обновление. Всего за 45 минут пресс-конференция показывает, что OpenAI сделал еще один шаг вперед.
boy illustration
Copilot облачной разработки — упрощение разработки
boy illustration
Микросборка xChatGPT с низким кодом, создание апплета чат-бота с искусственным интеллектом за пять шагов
boy illustration
CUDA Out of Memory: идеальное решение проблемы нехватки памяти CUDA
boy illustration
Анализ кластеризации отдельных ячеек, который должен освоить каждый&MarkerгенетическийВизуализация
boy illustration
vLLM: мощный инструмент для ускорения вывода ИИ
boy illustration
CodeGeeX: мощный инструмент генерации кода искусственного интеллекта, который можно использовать бесплатно в дополнение к второму пилоту.
boy illustration
Машинное обучение Реальный бой LightGBM + настройка параметров случайного поиска: точность 96,67%
boy illustration
Бесшовная интеграция, мгновенный интеллект [1]: платформа больших моделей Dify-LLM, интеграция без кодирования и встраивание в сторонние системы, более 42 тысяч звезд, чтобы стать свидетелями эксклюзивных интеллектуальных решений.
boy illustration
LM Studio для создания локальных больших моделей
boy illustration
Как определить количество слоев и нейронов скрытых слоев нейронной сети?
boy illustration
[Отслеживание целей] Подробное объяснение ByteTrack и детали кода