All Downloads are FREE. Search and download functionalities are using the official Maven repository.

infra.beans.factory.annotation.QualifierAnnotationAutowireCandidateResolver Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2017 - 2024 the original author or authors.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program. If not, see [https://www.gnu.org/licenses/]
 */

package infra.beans.factory.annotation;

import java.lang.annotation.Annotation;
import java.lang.reflect.AnnotatedElement;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

import infra.beans.SimpleTypeConverter;
import infra.beans.factory.NoSuchBeanDefinitionException;
import infra.beans.factory.config.BeanDefinitionHolder;
import infra.beans.factory.config.DependencyDescriptor;
import infra.beans.factory.support.AutowireCandidateQualifier;
import infra.beans.factory.support.AutowireCandidateResolver;
import infra.beans.factory.support.GenericTypeAwareAutowireCandidateResolver;
import infra.beans.factory.support.RootBeanDefinition;
import infra.core.MethodParameter;
import infra.core.annotation.AnnotatedElementUtils;
import infra.core.annotation.AnnotationAttributes;
import infra.core.annotation.AnnotationUtils;
import infra.lang.Assert;
import infra.lang.Nullable;
import infra.util.ClassUtils;
import infra.util.ObjectUtils;

/**
 * {@link AutowireCandidateResolver} implementation that matches bean holder qualifiers
 * against {@link Qualifier qualifier annotations} on the field or parameter to be autowired.
 *
 * 

Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation, if available. * * @author Mark Fisher * @author Juergen Hoeller * @author Stephane Nicoll * @author Harry Yang * @see AutowireCandidateQualifier * @see Qualifier * @since 4.0 */ public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver { private final LinkedHashSet> qualifierTypes = new LinkedHashSet<>(2); private Class valueAnnotationType = Value.class; /** * Create a new QualifierAnnotationAutowireCandidateResolver * for Framework's standard {@link Qualifier} annotation. *

Also supports JSR-330's {@link jakarta.inject.Qualifier} annotation, if available. */ public QualifierAnnotationAutowireCandidateResolver() { this.qualifierTypes.add(Qualifier.class); try { this.qualifierTypes.add(ClassUtils.forName("jakarta.inject.Qualifier", QualifierAnnotationAutowireCandidateResolver.class.getClassLoader())); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. } } /** * Create a new QualifierAnnotationAutowireCandidateResolver * for the given qualifier annotation type. * * @param qualifierType the qualifier annotation to look for */ public QualifierAnnotationAutowireCandidateResolver(Class qualifierType) { Assert.notNull(qualifierType, "'qualifierType' is required"); this.qualifierTypes.add(qualifierType); } /** * Create a new QualifierAnnotationAutowireCandidateResolver * for the given qualifier annotation types. * * @param qualifierTypes the qualifier annotations to look for */ public QualifierAnnotationAutowireCandidateResolver(Set> qualifierTypes) { Assert.notNull(qualifierTypes, "'qualifierTypes' is required"); this.qualifierTypes.addAll(qualifierTypes); } /** * Set the 'value' annotation type, to be used on fields, method parameters * and constructor parameters. *

The default value annotation type is the Framework-provided * {@link Value} annotation. *

This setter property exists so that developers can provide their own * (non-Framework-specific) annotation type to indicate a default value * expression for a specific argument. */ public void setValueAnnotationType(Class valueAnnotationType) { this.valueAnnotationType = valueAnnotationType; } /** * Register the given type to be used as a qualifier when autowiring. *

This identifies qualifier annotations for direct use (on fields, * method parameters and constructor parameters) as well as meta * annotations that in turn identify actual qualifier annotations. *

This implementation only supports annotations as qualifier types. * The default is Framework's {@link Qualifier} annotation which serves * as a qualifier for direct use and also as a meta annotation. * * @param qualifierType the annotation type to register */ public void addQualifierType(Class qualifierType) { this.qualifierTypes.add(qualifierType); } /** * Determine whether the provided bean holder is an autowire candidate. *

To be considered a candidate the bean's autowire-candidate * attribute must not have been set to 'false'. Also, if an annotation on * the field or parameter to be autowired is recognized by this bean factory * as a qualifier, the bean must 'match' against the annotation as * well as any attributes it may contain. The bean holder must contain * the same qualifier or match by meta attributes. A "value" attribute will * fallback to match against the bean name or an alias if a qualifier or * attribute does not match. * * @see Qualifier */ @Override public boolean isAutowireCandidate(BeanDefinitionHolder holder, DependencyDescriptor descriptor) { if (!super.isAutowireCandidate(holder, descriptor)) { return false; } Boolean checked = checkQualifiers(holder, descriptor.getAnnotations()); if (checked != Boolean.FALSE) { MethodParameter methodParam = descriptor.getMethodParameter(); if (methodParam != null) { Method method = methodParam.getMethod(); if (method == null || void.class == method.getReturnType()) { Boolean methodChecked = checkQualifiers(holder, methodParam.getMethodAnnotations()); if (methodChecked != null && checked == null) { checked = methodChecked; } } } } return checked == Boolean.TRUE || (checked == null && ((RootBeanDefinition) holder.getBeanDefinition()).isDefaultCandidate()); } /** * Determine whether the given dependency declares an autowired annotation, * checking its required flag. * * @see Autowired#required() */ @Override public boolean isRequired(DependencyDescriptor descriptor) { if (!super.isRequired(descriptor)) { return false; } Autowired autowired = descriptor.getAnnotation(Autowired.class); return (autowired == null || autowired.required()); } /** * Match the given qualifier annotations against the candidate bean definition. * * @return {@code false} if a qualifier has been found but not matched, * {@code true} if a qualifier has been found and matched, * {@code null} if no qualifier has been found at all */ @Nullable protected Boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) { boolean qualifierFound = false; if (ObjectUtils.isNotEmpty(annotationsToSearch)) { SimpleTypeConverter typeConverter = new SimpleTypeConverter(); for (Annotation annotation : annotationsToSearch) { Class type = annotation.annotationType(); if (isPlainJavaAnnotation(type)) { continue; } boolean checkMeta = true; boolean fallbackToMeta = false; if (isQualifier(type)) { qualifierFound = true; if (!checkQualifier(bdHolder, annotation, typeConverter)) { fallbackToMeta = true; } else { checkMeta = false; } } if (checkMeta) { boolean foundMeta = false; for (Annotation metaAnn : type.getAnnotations()) { Class metaType = metaAnn.annotationType(); if (isPlainJavaAnnotation(metaType)) { continue; } if (isQualifier(metaType)) { qualifierFound = true; foundMeta = true; // Only accept fallback match if @Qualifier annotation has a value... // Otherwise, it is just a marker for a custom qualifier annotation. if ((fallbackToMeta && ObjectUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) || !checkQualifier(bdHolder, metaAnn, typeConverter)) { return false; } } } if (fallbackToMeta && !foundMeta) { return false; } } } } return (qualifierFound ? true : null); } /** * Check whether the given annotation type is a plain "java." annotation, * typically from {@code java.lang.annotation}. *

Aligned with * {@code org.springframework.core.annotation.AnnotationsScanner#hasPlainJavaAnnotationsOnly}. */ private boolean isPlainJavaAnnotation(Class annotationType) { return annotationType.getName().startsWith("java."); } /** * Checks whether the given annotation type is a recognized qualifier type. */ protected boolean isQualifier(Class annotationType) { for (Class qualifierType : this.qualifierTypes) { if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) { return true; } } return false; } /** * Match the given qualifier annotation against the candidate bean holder. */ protected boolean checkQualifier(BeanDefinitionHolder bdHolder, Annotation annotation, SimpleTypeConverter typeConverter) { Class type = annotation.annotationType(); RootBeanDefinition bd = (RootBeanDefinition) bdHolder.getBeanDefinition(); AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName()); if (qualifier == null) { qualifier = bd.getQualifier(ClassUtils.getShortName(type)); } if (qualifier == null) { // First, check annotation on qualified element, if any Annotation targetAnnotation = getQualifiedElementAnnotation(bd, type); // Then, check annotation on factory method, if applicable if (targetAnnotation == null) { targetAnnotation = getFactoryMethodAnnotation(bd, type); } if (targetAnnotation == null) { RootBeanDefinition dbd = getResolvedDecoratedDefinition(bd); if (dbd != null) { targetAnnotation = getFactoryMethodAnnotation(dbd, type); } } if (targetAnnotation == null) { // Look for matching annotation on the target class if (getBeanFactory() != null) { try { Class beanType = getBeanFactory().getType(bdHolder.getBeanName()); if (beanType != null) { targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(beanType), type); } } catch (NoSuchBeanDefinitionException ex) { // Not the usual case - simply forget about the type check... } } if (targetAnnotation == null && bd.hasBeanClass()) { targetAnnotation = AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type); } } if (targetAnnotation != null && targetAnnotation.equals(annotation)) { return true; } } Map attributes = AnnotationUtils.getAnnotationAttributes(annotation); if (attributes.isEmpty() && qualifier == null) { // If no attributes, the qualifier must be present return false; } for (Map.Entry entry : attributes.entrySet()) { String attributeName = entry.getKey(); Object expectedValue = entry.getValue(); Object actualValue = null; // Check qualifier first if (qualifier != null) { actualValue = qualifier.getAttribute(attributeName); } if (actualValue == null) { // Fall back on bean definition attribute actualValue = bd.getAttribute(attributeName); } if (actualValue == null && attributeName.equals(AutowireCandidateQualifier.VALUE_KEY) && expectedValue instanceof String && bdHolder.matchesName((String) expectedValue)) { // Fall back on bean name (or alias) match continue; } if (actualValue == null && qualifier != null) { // Fall back on default, but only if the qualifier is present actualValue = AnnotationUtils.getDefaultValue(annotation, attributeName); } if (actualValue != null) { actualValue = typeConverter.convertIfNecessary(actualValue, expectedValue.getClass()); } if (!ObjectUtils.nullSafeEquals(expectedValue, actualValue)) { return false; } } return true; } @Nullable protected Annotation getQualifiedElementAnnotation(RootBeanDefinition bd, Class type) { AnnotatedElement qualifiedElement = bd.getQualifiedElement(); return qualifiedElement != null ? AnnotationUtils.getAnnotation(qualifiedElement, type) : null; } @Nullable protected Annotation getFactoryMethodAnnotation(RootBeanDefinition bd, Class type) { Method resolvedFactoryMethod = bd.getResolvedFactoryMethod(); return resolvedFactoryMethod != null ? AnnotationUtils.getAnnotation(resolvedFactoryMethod, type) : null; } /** * Determine whether the given dependency declares a qualifier annotation. * * @see #isQualifier(Class) * @see Qualifier */ @Override public boolean hasQualifier(DependencyDescriptor descriptor) { for (Annotation ann : descriptor.getAnnotations()) { if (isQualifier(ann.annotationType())) { return true; } } return false; } @Override @Nullable public String getSuggestedName(DependencyDescriptor descriptor) { for (Annotation annotation : descriptor.getAnnotations()) { if (isQualifier(annotation.annotationType())) { Object value = AnnotationUtils.getValue(annotation); if (value instanceof String str) { return str; } } } return null; } /** * Determine whether the given dependency declares a value annotation. * * @see Value */ @Override @Nullable public Object getSuggestedValue(DependencyDescriptor descriptor) { Object value = findValue(descriptor.getAnnotations()); if (value == null) { MethodParameter methodParam = descriptor.getMethodParameter(); if (methodParam != null) { value = findValue(methodParam.getMethodAnnotations()); } } return value; } /** * Determine a suggested value from any of the given candidate annotations. */ @Nullable protected Object findValue(Annotation[] annotationsToSearch) { if (annotationsToSearch.length > 0) { // qualifier annotations have to be local AnnotationAttributes attr = AnnotatedElementUtils.getMergedAnnotationAttributes( AnnotatedElementUtils.forAnnotations(annotationsToSearch), this.valueAnnotationType); if (attr != null) { return extractValue(attr); } } return null; } /** * Extract the value attribute from the given annotation. */ protected Object extractValue(AnnotationAttributes attr) { Object value = attr.get(AnnotationUtils.VALUE); if (value == null) { throw new IllegalStateException("Value annotation must have a value attribute"); } return value; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy