/*
* Copyright 2002-2008 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.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.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.MutablePropertyValues;
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.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@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} annotation.
*
* <p>Only one constructor (at max) of any given bean class may carry this
* annotation with the 'required' parameter set to <code>true</code>,
* indicating <i>the</i> constructor to autowire when used as a Spring bean.
* If multiple <i>non-required</i> constructors carry 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 default constructor (if present) will be used.
* 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. Such 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.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @since 2.5
* @see #setAutowiredAnnotationType
* @see Autowired
* @see org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
*/
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
protected final Log logger = LogFactory.getLog(AutowiredAnnotationBeanPostProcessor.class);
private Class<? extends Annotation> autowiredAnnotationType = Autowired.class;
private String requiredParameterName = "required";
private boolean requiredParameterValue = true;
private int order = Ordered.LOWEST_PRECEDENCE - 2;
private ConfigurableListableBeanFactory beanFactory;
private final Map<Class<?>, Constructor[]> candidateConstructorsCache =
new ConcurrentHashMap<Class<?>, Constructor[]>();
private final Map<Class<?>, InjectionMetadata> injectionMetadataCache =
new ConcurrentHashMap<Class<?>, InjectionMetadata>();
/**
* 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.
* <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.autowiredAnnotationType = autowiredAnnotationType;
}
/**
* Return the 'autowired' annotation type.
*/
protected Class<? extends Annotation> getAutowiredAnnotationType() {
return this.autowiredAnnotationType;
}
/**
* 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
* <p>For example if using 'required=true' (the default),
* this value should be <code>true</code>; but if using
* 'optional=false', this value should be <code>false</code>.
* @see #setRequiredParameterName(String)
*/
public void setRequiredParameterValue(boolean requiredParameterValue) {
this.requiredParameterValue = requiredParameterValue;
}
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return this.order;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException(
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory");
}
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
if (beanType != null) {
InjectionMetadata metadata = findAutowiringMetadata(beanType);
metadata.checkConfigMembers(beanDefinition);
}
}
public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
Constructor[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
Constructor[] rawCandidates = beanClass.getDeclaredConstructors();
List<Constructor> candidates = new ArrayList<Constructor>(rawCandidates.length);
Constructor requiredConstructor = null;
Constructor defaultConstructor = null;
for (int i = 0; i < rawCandidates.length; i++) {
Constructor<?> candidate = rawCandidates[i];
Annotation annotation = candidate.getAnnotation(getAutowiredAnnotationType());
if (annotation != null) {
if (requiredConstructor != null) {
throw new BeanCreationException("Invalid autowire-marked constructor: " + candidate +
". Found another constructor with 'required' Autowired annotation: " + requiredConstructor);
}
if (candidate.getParameterTypes().length == 0) {
throw new IllegalStateException("Autowired annotation requires at least one argument: " + candidate);
}
boolean required = determineRequiredStatus(annotation);
if (required) {
if (!candidates.isEmpty()) {
throw new BeanCreationException("Invalid autowire-marked constructors: " + candidates +
". Found another constructor with 'required' Autowired annotation: " + requiredConstructor);
}
requiredConstructor = candidate;
}
candidates.add(candidate);
}
else if (candidate.getParameterTypes().length == 0) {
defaultConstructor = candidate;
}
}
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
if (requiredConstructor == null && defaultConstructor != null) {
candidates.add(defaultConstructor);
}
candidateConstructors = (Constructor[]) candidates.toArray(new Constructor[candidates.size()]);
}
else {
candidateConstructors = new Constructor[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
try {
metadata.injectFields(bean, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Autowiring of fields failed", ex);
}
return true;
}
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
try {
metadata.injectMethods(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Autowiring of methods failed", ex);
}
return pvs;
}
/**
* 'Native' processing method for direct calls with an arbitrary target
* instance, resolving all of its fields and methods which are annotated
* with <code>@Autowired</code>.
* @param bean the target instance to process
*/
public void processInjection(Object bean) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
try {
metadata.injectFields(bean, null);
metadata.injectMethods(bean, null, null);
}
catch (Throwable ex) {
throw new BeanCreationException("Autowiring of fields/methods failed", ex);
}
}
private InjectionMetadata findAutowiringMetadata(final Class clazz) {
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
if (metadata == null) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(clazz);
if (metadata == null) {
final InjectionMetadata newMetadata = new InjectionMetadata(clazz);
ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
public void doWith(Field field) {
Annotation annotation = field.getAnnotation(getAutowiredAnnotationType());
if (annotation != null) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("Autowired annotation is not supported on static fields");
}
boolean required = determineRequiredStatus(annotation);
newMetadata.addInjectedField(new AutowiredFieldElement(field, required));
}
}
});
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
Annotation annotation = method.getAnnotation(getAutowiredAnnotationType());
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("Autowired annotation is not supported on static methods");
}
if (method.getParameterTypes().length == 0) {
throw new IllegalStateException("Autowired annotation requires at least one argument: " + method);
}
boolean required = determineRequiredStatus(annotation);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
newMetadata.addInjectedMethod(new AutowiredMethodElement(method, required, pd));
}
}
});
metadata = newMetadata;
this.injectionMetadataCache.put(clazz, metadata);
}
}
}
return metadata;
}
/**
* 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 Map findAutowireCandidates(Class 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);
}
/**
* 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 annotation the Autowired annotation
* @return whether the annotation indicates that a dependency is required
*/
protected boolean determineRequiredStatus(Annotation annotation) {
try {
Method method = ReflectionUtils.findMethod(annotation.annotationType(), this.requiredParameterName);
return (this.requiredParameterValue == (Boolean) ReflectionUtils.invokeMethod(method, annotation));
}
catch (Exception ex) {
// required by default
return true;
}
}
/**
* Register the specified bean as dependent on the autowired beans.
*/
private void registerDependentBeans(String beanName, Set<String> autowiredBeanNames) {
if (beanName != null) {
for (Iterator it = autowiredBeanNames.iterator(); it.hasNext();) {
String autowiredBeanName = (String) it.next();
beanFactory.registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName +
"' to bean named '" + autowiredBeanName + "'");
}
}
}
}
/**
* Class representing injection information about an annotated field.
*/
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
private final boolean required;
private volatile boolean cached = false;
private volatile Object cachedFieldValue;
public AutowiredFieldElement(Field field, boolean required) {
super(field, null);
this.required = required;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
try {
Object value = null;
if (this.cached) {
if (this.cachedFieldValue instanceof DependencyDescriptor) {
DependencyDescriptor descriptor = (DependencyDescriptor) this.cachedFieldValue;
TypeConverter typeConverter = beanFactory.getTypeConverter();
value = beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
}
else if (this.cachedFieldValue instanceof RuntimeBeanReference) {
value = beanFactory.getBean(((RuntimeBeanReference) this.cachedFieldValue).getBeanName());
}
else {
value = this.cachedFieldValue;
}
}
else {
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required);
this.cachedFieldValue = descriptor;
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
if (value != null) {
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName)) {
if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName);
}
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
catch (Throwable ex) {
throw new BeanCreationException("Could not autowire field: " + field, ex);
}
}
}
/**
* Class representing injection information about an annotated method.
*/
private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {
private final boolean required;
private volatile boolean cached = false;
private volatile Object[] cachedMethodArguments;
public AutowiredMethodElement(Method method, boolean required, PropertyDescriptor pd) {
super(method, pd);
this.required = required;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
if (this.skip == null && this.pd != null && pvs != null && pvs.contains(this.pd.getName())) {
// Explicit value provided as part of the bean definition.
this.skip = Boolean.TRUE;
}
if (this.skip != null && this.skip.booleanValue()) {
return;
}
Method method = (Method) this.member;
try {
Object[] arguments = null;
if (this.cached) {
if (this.cachedMethodArguments != null) {
arguments = new Object[this.cachedMethodArguments.length];
for (int i = 0; i < arguments.length; i++) {
Object cachedArg = this.cachedMethodArguments[i];
if (cachedArg instanceof DependencyDescriptor) {
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArg;
TypeConverter typeConverter = beanFactory.getTypeConverter();
arguments[i] = beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
}
else if (cachedArg instanceof RuntimeBeanReference) {
arguments[i] = beanFactory.getBean(((RuntimeBeanReference) cachedArg).getBeanName());
}
else {
arguments[i] = cachedArg;
}
}
}
}
else {
Class[] paramTypes = method.getParameterTypes();
arguments = new Object[paramTypes.length];
Set<String> autowiredBeanNames = new LinkedHashSet<String>(arguments.length);
TypeConverter typeConverter = beanFactory.getTypeConverter();
this.cachedMethodArguments = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
GenericTypeResolver.resolveParameterType(methodParam, bean.getClass());
DependencyDescriptor descriptor = new DependencyDescriptor(methodParam, this.required);
this.cachedMethodArguments[i] = descriptor;
arguments[i] = beanFactory.resolveDependency(
descriptor, beanName, autowiredBeanNames, typeConverter);
if (arguments[i] == null) {
arguments = null;
break;
}
}
if (arguments != null) {
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == paramTypes.length) {
Iterator<String> it = autowiredBeanNames.iterator();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName)) {
if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
this.cachedMethodArguments[i] = new RuntimeBeanReference(autowiredBeanName);
}
}
else {
this.cachedMethodArguments[i] = arguments[i];
}
}
}
}
else {
this.cachedMethodArguments = null;
}
this.cached = true;
}
if (this.skip == null) {
if (this.pd != null && pvs instanceof MutablePropertyValues) {
((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
}
this.skip = Boolean.FALSE;
}
if (arguments != null) {
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);
}
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
catch (Throwable ex) {
throw new BeanCreationException("Could not autowire method: " + method, ex);
}
}
}
}