/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.beans.factory.annotation;

import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.InjectionPoint;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.UnsatisfiedDependencyException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.support.LookupOverride;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;

BeanPostProcessor implementation that autowires annotated fields, setter methods and arbitrary config methods. Such members to be injected are detected through a Java 5 annotation: by default, Spring's @Autowired and @Value annotations.

Also supports JSR-330's @Inject annotation, if available, as a direct alternative to Spring's own @Autowired.

Only one constructor (at max) of any given bean class may declare this annotation with the 'required' parameter set to true, indicating the constructor to autowire when used as a Spring bean. If multiple non-required constructors declare the annotation, they will be considered as candidates for autowiring. The constructor with the greatest number of dependencies that can be satisfied by matching beans in the Spring container will be chosen. If none of the candidates can be satisfied, then a primary/default constructor (if present) will be used. If a class only declares a single constructor to begin with, it will always be used, even if not annotated. An annotated constructor does not have to be public.

Fields are injected right after construction of a bean, before any config methods are invoked. Such a config field does not have to be public.

Config methods may have an arbitrary name and any number of arguments; each of those arguments will be autowired with a matching bean in the Spring container. Bean property setter methods are effectively just a special case of such a general config method. Config methods do not have to be public.

Note: A default AutowiredAnnotationBeanPostProcessor will be registered by the "context:annotation-config" and "context:component-scan" XML tags. Remove or turn off the default annotation configuration there if you intend to specify a custom AutowiredAnnotationBeanPostProcessor bean definition.

NOTE: Annotation injection will be performed before XML injection; thus the latter configuration will override the former for properties wired through both approaches.

In addition to regular injection points as discussed above, this post-processor also handles Spring's @Lookup annotation which identifies lookup methods to be replaced by the container at runtime. This is essentially a type-safe version of getBean(Class, args) and getBean(String, args), See @Lookup's javadoc for details.

Author:Juergen Hoeller, Mark Fisher, Stephane Nicoll, Sebastien Deleuze
See Also:
Since:2.5
/** * {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation * that autowires annotated fields, setter methods and arbitrary config methods. * Such members to be injected are detected through a Java 5 annotation: by default, * Spring's {@link Autowired @Autowired} and {@link Value @Value} annotations. * * <p>Also supports JSR-330's {@link javax.inject.Inject @Inject} annotation, * if available, as a direct alternative to Spring's own {@code @Autowired}. * * <p>Only one constructor (at max) of any given bean class may declare this annotation * with the 'required' parameter set to {@code true}, indicating <i>the</i> constructor * to autowire when used as a Spring bean. If multiple <i>non-required</i> constructors * declare the annotation, they will be considered as candidates for autowiring. * The constructor with the greatest number of dependencies that can be satisfied by * matching beans in the Spring container will be chosen. If none of the candidates * can be satisfied, then a primary/default constructor (if present) will be used. * If a class only declares a single constructor to begin with, it will always be used, * even if not annotated. An annotated constructor does not have to be public. * * <p>Fields are injected right after construction of a bean, before any * config methods are invoked. Such a config field does not have to be public. * * <p>Config methods may have an arbitrary name and any number of arguments; each of * those arguments will be autowired with a matching bean in the Spring container. * Bean property setter methods are effectively just a special case of such a * general config method. Config methods do not have to be public. * * <p>Note: A default AutowiredAnnotationBeanPostProcessor will be registered * by the "context:annotation-config" and "context:component-scan" XML tags. * Remove or turn off the default annotation configuration there if you intend * to specify a custom AutowiredAnnotationBeanPostProcessor bean definition. * <p><b>NOTE:</b> Annotation injection will be performed <i>before</i> XML injection; * thus the latter configuration will override the former for properties wired through * both approaches. * * <p>In addition to regular injection points as discussed above, this post-processor * also handles Spring's {@link Lookup @Lookup} annotation which identifies lookup * methods to be replaced by the container at runtime. This is essentially a type-safe * version of {@code getBean(Class, args)} and {@code getBean(String, args)}, * See {@link Lookup @Lookup's javadoc} for details. * * @author Juergen Hoeller * @author Mark Fisher * @author Stephane Nicoll * @author Sebastien Deleuze * @since 2.5 * @see #setAutowiredAnnotationType * @see Autowired * @see Value */
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware { protected final Log logger = LogFactory.getLog(getClass()); private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>(4); private String requiredParameterName = "required"; private boolean requiredParameterValue = true; private int order = Ordered.LOWEST_PRECEDENCE - 2; @Nullable private ConfigurableListableBeanFactory beanFactory; private final Set<String> lookupMethodsChecked = Collections.newSetFromMap(new ConcurrentHashMap<>(256)); private final Map<Class<?>, Constructor<?>[]> candidateConstructorsCache = new ConcurrentHashMap<>(256); private final Map<String, InjectionMetadata> injectionMetadataCache = new ConcurrentHashMap<>(256);
Create a new AutowiredAnnotationBeanPostProcessor for Spring's standard Autowired annotation.

Also supports JSR-330's Inject annotation, if available.

/** * Create a new AutowiredAnnotationBeanPostProcessor * for Spring's standard {@link Autowired} annotation. * <p>Also supports JSR-330's {@link javax.inject.Inject} annotation, if available. */
@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. } }
Set the 'autowired' annotation type, to be used on constructors, fields, setter methods and arbitrary config methods.

The default autowired annotation type is the Spring-provided Autowired annotation, as well as Value.

This setter property exists so that developers can provide their own (non-Spring-specific) annotation type to indicate that a member is supposed to be autowired.

/** * Set the 'autowired' annotation type, to be used on constructors, fields, * setter methods and arbitrary config methods. * <p>The default autowired annotation type is the Spring-provided {@link Autowired} * annotation, as well as {@link Value}. * <p>This setter property exists so that developers can provide their own * (non-Spring-specific) annotation type to indicate that a member is supposed * to be autowired. */
public void setAutowiredAnnotationType(Class<? extends Annotation> autowiredAnnotationType) { Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null"); this.autowiredAnnotationTypes.clear(); this.autowiredAnnotationTypes.add(autowiredAnnotationType); }
Set the 'autowired' annotation types, to be used on constructors, fields, setter methods and arbitrary config methods.

The default autowired annotation type is the Spring-provided Autowired annotation, as well as Value.

This setter property exists so that developers can provide their own (non-Spring-specific) annotation types to indicate that a member is supposed to be autowired.

/** * Set the 'autowired' annotation types, to be used on constructors, fields, * setter methods and arbitrary config methods. * <p>The default autowired annotation type is the Spring-provided {@link Autowired} * annotation, as well as {@link Value}. * <p>This setter property exists so that developers can provide their own * (non-Spring-specific) annotation types to indicate that a member is supposed * to be autowired. */
public void setAutowiredAnnotationTypes(Set<Class<? extends Annotation>> autowiredAnnotationTypes) { Assert.notEmpty(autowiredAnnotationTypes, "'autowiredAnnotationTypes' must not be empty"); this.autowiredAnnotationTypes.clear(); this.autowiredAnnotationTypes.addAll(autowiredAnnotationTypes); }
Set the name of a parameter of the annotation that specifies whether it is required.
See Also:
  • setRequiredParameterValue(boolean)
/** * Set the name of a parameter of the annotation that specifies whether it is required. * @see #setRequiredParameterValue(boolean) */
public void setRequiredParameterName(String requiredParameterName) { this.requiredParameterName = requiredParameterName; }
Set the boolean value that marks a dependency as required

For example if using 'required=true' (the default), this value should be true; but if using 'optional=false', this value should be false.

See Also:
/** * Set the boolean value that marks a dependency as required * <p>For example if using 'required=true' (the default), this value should be * {@code true}; but if using 'optional=false', this value should be {@code false}. * @see #setRequiredParameterName(String) */
public void setRequiredParameterValue(boolean requiredParameterValue) { this.requiredParameterValue = requiredParameterValue; } public void setOrder(int order) { this.order = order; } @Override public int getOrder() { return this.order; } @Override public void setBeanFactory(BeanFactory beanFactory) { if (!(beanFactory instanceof ConfigurableListableBeanFactory)) { throw new IllegalArgumentException( "AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory: " + beanFactory); } this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } @Override public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null); metadata.checkConfigMembers(beanDefinition); } @Override public void resetBeanDefinition(String beanName) { this.lookupMethodsChecked.remove(beanName); this.injectionMetadataCache.remove(beanName); } @Override @Nullable public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, final String beanName) throws BeanCreationException { // Let's check for lookup methods here.. if (!this.lookupMethodsChecked.contains(beanName)) { try { ReflectionUtils.doWithMethods(beanClass, method -> { Lookup lookup = method.getAnnotation(Lookup.class); if (lookup != null) { Assert.state(this.beanFactory != null, "No BeanFactory available"); LookupOverride override = new LookupOverride(method, lookup.value()); try { RootBeanDefinition mbd = (RootBeanDefinition) this.beanFactory.getMergedBeanDefinition(beanName); mbd.getMethodOverrides().addOverride(override); } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(beanName, "Cannot apply @Lookup to beans without corresponding bean definition"); } } }); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Lookup method resolution failed", ex); } this.lookupMethodsChecked.add(beanName); } // Quick check on the concurrent map first, with minimal locking. Constructor<?>[] candidateConstructors = this.candidateConstructorsCache.get(beanClass); if (candidateConstructors == null) { // Fully synchronized resolution now... synchronized (this.candidateConstructorsCache) { candidateConstructors = this.candidateConstructorsCache.get(beanClass); if (candidateConstructors == null) { Constructor<?>[] rawCandidates; try { rawCandidates = beanClass.getDeclaredConstructors(); } catch (Throwable ex) { throw new BeanCreationException(beanName, "Resolution of declared constructors on bean Class [" + beanClass.getName() + "] from ClassLoader [" + beanClass.getClassLoader() + "] failed", ex); } List<Constructor<?>> candidates = new ArrayList<>(rawCandidates.length); Constructor<?> requiredConstructor = null; Constructor<?> defaultConstructor = null; Constructor<?> primaryConstructor = BeanUtils.findPrimaryConstructor(beanClass); int nonSyntheticConstructors = 0; for (Constructor<?> candidate : rawCandidates) { if (!candidate.isSynthetic()) { nonSyntheticConstructors++; } else if (primaryConstructor != null) { continue; } AnnotationAttributes ann = findAutowiredAnnotation(candidate); if (ann == null) { Class<?> userClass = ClassUtils.getUserClass(beanClass); if (userClass != beanClass) { try { Constructor<?> superCtor = userClass.getDeclaredConstructor(candidate.getParameterTypes()); ann = findAutowiredAnnotation(superCtor); } catch (NoSuchMethodException ex) { // Simply proceed, no equivalent superclass constructor found... } } } if (ann != null) { if (requiredConstructor != null) { throw new BeanCreationException(beanName, "Invalid autowire-marked constructor: " + candidate + ". Found constructor with 'required' Autowired annotation already: " + requiredConstructor); } boolean required = determineRequiredStatus(ann); if (required) { if (!candidates.isEmpty()) { throw new BeanCreationException(beanName, "Invalid autowire-marked constructors: " + candidates + ". Found constructor with 'required' Autowired annotation: " + candidate); } requiredConstructor = candidate; } candidates.add(candidate); } else if (candidate.getParameterCount() == 0) { defaultConstructor = candidate; } } if (!candidates.isEmpty()) { // Add default constructor to list of optional constructors, as fallback. if (requiredConstructor == null) { if (defaultConstructor != null) { candidates.add(defaultConstructor); } else if (candidates.size() == 1 && logger.isInfoEnabled()) { logger.info("Inconsistent constructor declaration on bean with name '" + beanName + "': single autowire-marked constructor flagged as optional - " + "this constructor is effectively required since there is no " + "default constructor to fall back to: " + candidates.get(0)); } } candidateConstructors = candidates.toArray(new Constructor<?>[0]); } else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) { candidateConstructors = new Constructor<?>[] {rawCandidates[0]}; } else if (nonSyntheticConstructors == 2 && primaryConstructor != null && defaultConstructor != null && !primaryConstructor.equals(defaultConstructor)) { candidateConstructors = new Constructor<?>[] {primaryConstructor, defaultConstructor}; } else if (nonSyntheticConstructors == 1 && primaryConstructor != null) { candidateConstructors = new Constructor<?>[] {primaryConstructor}; } else { candidateConstructors = new Constructor<?>[0]; } this.candidateConstructorsCache.put(beanClass, candidateConstructors); } } } return (candidateConstructors.length > 0 ? candidateConstructors : null); } @Override public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) { InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs); try { 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; } @Deprecated @Override public PropertyValues postProcessPropertyValues( PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) { return postProcessProperties(pvs, bean, beanName); }
'Native' processing method for direct calls with an arbitrary target instance, resolving all of its fields and methods which are annotated with @Autowired.
Params:
  • bean – the target instance to process
Throws:
/** * 'Native' processing method for direct calls with an arbitrary target instance, * resolving all of its fields and methods which are annotated with {@code @Autowired}. * @param bean the target instance to process * @throws BeanCreationException if autowiring failed */
public void processInjection(Object bean) throws BeanCreationException { Class<?> clazz = bean.getClass(); InjectionMetadata metadata = findAutowiringMetadata(clazz.getName(), clazz, null); try { metadata.inject(bean, null, null); } catch (BeanCreationException ex) { throw ex; } catch (Throwable ex) { throw new BeanCreationException( "Injection of autowired dependencies failed for class [" + clazz + "]", ex); } } 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; } private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) { List<InjectionMetadata.InjectedElement> elements = new ArrayList<>(); Class<?> targetClass = clazz; do { final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>(); ReflectionUtils.doWithLocalFields(targetClass, field -> { AnnotationAttributes 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)); } }); ReflectionUtils.doWithLocalMethods(targetClass, method -> { Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method); if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) { return; } AnnotationAttributes 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)); } }); elements.addAll(0, currElements); targetClass = targetClass.getSuperclass(); } while (targetClass != null && targetClass != Object.class); return new InjectionMetadata(clazz, elements); } @Nullable private AnnotationAttributes findAutowiredAnnotation(AccessibleObject ao) { if (ao.getAnnotations().length > 0) { // autowiring annotations have to be local for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) { AnnotationAttributes attributes = AnnotatedElementUtils.getMergedAnnotationAttributes(ao, type); if (attributes != null) { return attributes; } } } return null; }
Determine if the annotated field or method requires its dependency.

A 'required' dependency means that autowiring should fail when no beans are found. Otherwise, the autowiring process will simply bypass the field or method when no beans are found.

Params:
  • ann – the Autowired annotation
Returns:whether the annotation indicates that a dependency is required
/** * Determine if the annotated field or method requires its dependency. * <p>A 'required' dependency means that autowiring should fail when no beans * are found. Otherwise, the autowiring process will simply bypass the field * or method when no beans are found. * @param ann the Autowired annotation * @return whether the annotation indicates that a dependency is required */
protected boolean determineRequiredStatus(AnnotationAttributes ann) { return (!ann.containsKey(this.requiredParameterName) || this.requiredParameterValue == ann.getBoolean(this.requiredParameterName)); }
Obtain all beans of the given type as autowire candidates.
Params:
  • type – the type of the bean
Throws:
Returns:the target beans, or an empty Collection if no bean of this type is found
/** * Obtain all beans of the given type as autowire candidates. * @param type the type of the bean * @return the target beans, or an empty Collection if no bean of this type is found * @throws BeansException if bean retrieval failed */
protected <T> Map<String, T> findAutowireCandidates(Class<T> type) throws BeansException { if (this.beanFactory == null) { throw new IllegalStateException("No BeanFactory configured - " + "override the getBeanOfType method or specify the 'beanFactory' property"); } return BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type); }
Register the specified bean as dependent on the autowired beans.
/** * Register the specified bean as dependent on the autowired beans. */
private void registerDependentBeans(@Nullable String beanName, Set<String> autowiredBeanNames) { if (beanName != null) { for (String autowiredBeanName : autowiredBeanNames) { if (this.beanFactory != null && this.beanFactory.containsBean(autowiredBeanName)) { this.beanFactory.registerDependentBean(autowiredBeanName, beanName); } if (logger.isTraceEnabled()) { logger.trace("Autowiring by type from bean name '" + beanName + "' to bean named '" + autowiredBeanName + "'"); } } } }
Resolve the specified cached method argument or field value.
/** * Resolve the specified cached method argument or field value. */
@Nullable private Object resolvedCachedArgument(@Nullable String beanName, @Nullable Object cachedArgument) { if (cachedArgument instanceof DependencyDescriptor) { DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument; Assert.state(this.beanFactory != null, "No BeanFactory available"); return this.beanFactory.resolveDependency(descriptor, beanName, null, null); } else { return cachedArgument; } }
Class representing injection information about an annotated field.
/** * Class representing injection information about an annotated field. */
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement { private final boolean required; private volatile boolean cached = false; @Nullable private volatile Object cachedFieldValue; public AutowiredFieldElement(Field field, boolean required) { super(field, null); this.required = required; } @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { Field field = (Field) this.member; Object value; if (this.cached) { value = resolvedCachedArgument(beanName, this.cachedFieldValue); } else { 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(); 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) { if (value != null || this.required) { this.cachedFieldValue = desc; registerDependentBeans(beanName, autowiredBeanNames); if (autowiredBeanNames.size() == 1) { String autowiredBeanName = autowiredBeanNames.iterator().next(); if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, field.getType())) { this.cachedFieldValue = new ShortcutDependencyDescriptor( desc, autowiredBeanName, field.getType()); } } } else { this.cachedFieldValue = null; } this.cached = true; } } } if (value != null) { ReflectionUtils.makeAccessible(field); field.set(bean, value); } } }
Class representing injection information about an annotated method.
/** * Class representing injection information about an annotated method. */
private class AutowiredMethodElement extends InjectionMetadata.InjectedElement { private final boolean required; private volatile boolean cached = false; @Nullable private volatile Object[] cachedMethodArguments; public AutowiredMethodElement(Method method, boolean required, @Nullable PropertyDescriptor pd) { super(method, pd); this.required = required; } @Override protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable { if (checkPropertySkipping(pvs)) { return; } Method method = (Method) this.member; Object[] arguments; if (this.cached) { // Shortcut for avoiding synchronization... arguments = resolveCachedArguments(beanName); } else { Class<?>[] paramTypes = method.getParameterTypes(); arguments = new Object[paramTypes.length]; DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length]; Set<String> autowiredBeans = new LinkedHashSet<>(paramTypes.length); Assert.state(beanFactory != null, "No BeanFactory available"); TypeConverter typeConverter = beanFactory.getTypeConverter(); for (int i = 0; i < arguments.length; i++) { MethodParameter methodParam = new MethodParameter(method, i); DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required); currDesc.setContainingClass(bean.getClass()); descriptors[i] = currDesc; try { Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter); if (arg == null && !this.required) { arguments = null; break; } arguments[i] = arg; } catch (BeansException ex) { throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex); } } synchronized (this) { if (!this.cached) { if (arguments != null) { Object[] cachedMethodArguments = new Object[paramTypes.length]; System.arraycopy(descriptors, 0, cachedMethodArguments, 0, arguments.length); registerDependentBeans(beanName, autowiredBeans); if (autowiredBeans.size() == paramTypes.length) { Iterator<String> it = autowiredBeans.iterator(); for (int i = 0; i < paramTypes.length; i++) { String autowiredBeanName = it.next(); if (beanFactory.containsBean(autowiredBeanName) && beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) { cachedMethodArguments[i] = new ShortcutDependencyDescriptor( descriptors[i], autowiredBeanName, paramTypes[i]); } } } this.cachedMethodArguments = cachedMethodArguments; } else { this.cachedMethodArguments = null; } this.cached = true; } } } if (arguments != null) { try { ReflectionUtils.makeAccessible(method); method.invoke(bean, arguments); } catch (InvocationTargetException ex) { throw ex.getTargetException(); } } } @Nullable private Object[] resolveCachedArguments(@Nullable String beanName) { Object[] cachedMethodArguments = this.cachedMethodArguments; if (cachedMethodArguments == null) { return null; } Object[] arguments = new Object[cachedMethodArguments.length]; for (int i = 0; i < arguments.length; i++) { arguments[i] = resolvedCachedArgument(beanName, cachedMethodArguments[i]); } return arguments; } }
DependencyDescriptor variant with a pre-resolved target bean name.
/** * DependencyDescriptor variant with a pre-resolved target bean name. */
@SuppressWarnings("serial") private static class ShortcutDependencyDescriptor extends DependencyDescriptor { private final String shortcut; private final Class<?> requiredType; public ShortcutDependencyDescriptor(DependencyDescriptor original, String shortcut, Class<?> requiredType) { super(original); this.shortcut = shortcut; this.requiredType = requiredType; } @Override public Object resolveShortcut(BeanFactory beanFactory) { return beanFactory.getBean(this.shortcut, this.requiredType); } } }