/*
* Copyright 2002-2009 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.config;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Type;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.util.Assert;
/**
* Descriptor for a specific dependency that is about to be injected.
* Wraps a constructor parameter, a method parameter or a field,
* allowing unified access to their metadata.
*
* @author Juergen Hoeller
* @since 2.5
*/
public class DependencyDescriptor implements Serializable {
private transient MethodParameter methodParameter;
private transient Field field;
private Class declaringClass;
private String methodName;
private Class[] parameterTypes;
private int parameterIndex;
private String fieldName;
private final boolean required;
private final boolean eager;
private transient Annotation[] fieldAnnotations;
/**
* Create a new descriptor for a method or constructor parameter.
* Considers the dependency as 'eager'.
* @param methodParameter the MethodParameter to wrap
* @param required whether the dependency is required
*/
public DependencyDescriptor(MethodParameter methodParameter, boolean required) {
this(methodParameter, required, true);
}
/**
* Create a new descriptor for a method or constructor parameter.
* @param methodParameter the MethodParameter to wrap
* @param required whether the dependency is required
* @param eager whether this dependency is 'eager' in the sense of
* eagerly resolving potential target beans for type matching
*/
public DependencyDescriptor(MethodParameter methodParameter, boolean required, boolean eager) {
Assert.notNull(methodParameter, "MethodParameter must not be null");
this.methodParameter = methodParameter;
this.declaringClass = methodParameter.getDeclaringClass();
if (this.methodParameter.getMethod() != null) {
this.methodName = methodParameter.getMethod().getName();
this.parameterTypes = methodParameter.getMethod().getParameterTypes();
}
else {
this.parameterTypes = methodParameter.getConstructor().getParameterTypes();
}
this.parameterIndex = methodParameter.getParameterIndex();
this.required = required;
this.eager = eager;
}
/**
* Create a new descriptor for a field.
* Considers the dependency as 'eager'.
* @param field the field to wrap
* @param required whether the dependency is required
*/
public DependencyDescriptor(Field field, boolean required) {
this(field, required, true);
}
/**
* Create a new descriptor for a field.
* @param field the field to wrap
* @param required whether the dependency is required
* @param eager whether this dependency is 'eager' in the sense of
* eagerly resolving potential target beans for type matching
*/
public DependencyDescriptor(Field field, boolean required, boolean eager) {
Assert.notNull(field, "Field must not be null");
this.field = field;
this.declaringClass = field.getDeclaringClass();
this.fieldName = field.getName();
this.required = required;
this.eager = eager;
}
/**
* Return the wrapped MethodParameter, if any.
* <p>Note: Either MethodParameter or Field is available.
* @return the MethodParameter, or <code>null</code> if none
*/
public MethodParameter getMethodParameter() {
return this.methodParameter;
}
/**
* Return the wrapped Field, if any.
* <p>Note: Either MethodParameter or Field is available.
* @return the Field, or <code>null</code> if none
*/
public Field getField() {
return this.field;
}
/**
* Return whether this dependency is required.
*/
public boolean isRequired() {
return this.required;
}
/**
* Return whether this dependency is 'eager' in the sense of
* eagerly resolving potential target beans for type matching.
*/
public boolean isEager() {
return this.eager;
}
/**
* Initialize parameter name discovery for the underlying method parameter, if any.
* <p>This method does not actually try to retrieve the parameter name at
* this point; it just allows discovery to happen when the application calls
* {@link #getDependencyName()} (if ever).
*/
public void initParameterNameDiscovery(ParameterNameDiscoverer parameterNameDiscoverer) {
if (this.methodParameter != null) {
this.methodParameter.initParameterNameDiscovery(parameterNameDiscoverer);
}
}
/**
* Determine the name of the wrapped parameter/field.
* @return the declared name (never <code>null</code>)
*/
public String getDependencyName() {
return (this.field != null ? this.field.getName() : this.methodParameter.getParameterName());
}
/**
* Determine the declared (non-generic) type of the wrapped parameter/field.
* @return the declared type (never <code>null</code>)
*/
public Class<?> getDependencyType() {
return (this.field != null ? this.field.getType() : this.methodParameter.getParameterType());
}
/**
* Determine the generic type of the wrapped parameter/field.
* @return the generic type (never <code>null</code>)
*/
public Type getGenericDependencyType() {
return (this.field != null ? this.field.getGenericType() : this.methodParameter.getGenericParameterType());
}
/**
* Determine the generic element type of the wrapped Collection parameter/field, if any.
* @return the generic type, or <code>null</code> if none
*/
public Class<?> getCollectionType() {
return (this.field != null ?
GenericCollectionTypeResolver.getCollectionFieldType(this.field) :
GenericCollectionTypeResolver.getCollectionParameterType(this.methodParameter));
}
/**
* Determine the generic key type of the wrapped Map parameter/field, if any.
* @return the generic type, or <code>null</code> if none
*/
public Class<?> getMapKeyType() {
return (this.field != null ?
GenericCollectionTypeResolver.getMapKeyFieldType(this.field) :
GenericCollectionTypeResolver.getMapKeyParameterType(this.methodParameter));
}
/**
* Determine the generic value type of the wrapped Map parameter/field, if any.
* @return the generic type, or <code>null</code> if none
*/
public Class<?> getMapValueType() {
return (this.field != null ?
GenericCollectionTypeResolver.getMapValueFieldType(this.field) :
GenericCollectionTypeResolver.getMapValueParameterType(this.methodParameter));
}
/**
* Obtain the annotations associated with the wrapped parameter/field, if any.
*/
public Annotation[] getAnnotations() {
if (this.field != null) {
if (this.fieldAnnotations == null) {
this.fieldAnnotations = this.field.getAnnotations();
}
return this.fieldAnnotations;
}
else {
return this.methodParameter.getParameterAnnotations();
}
}
//---------------------------------------------------------------------
// Serialization support
//---------------------------------------------------------------------
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// Rely on default serialization; just initialize state after deserialization.
ois.defaultReadObject();
// Restore reflective handles (which are unfortunately not serializable)
try {
if (this.fieldName != null) {
this.field = this.declaringClass.getDeclaredField(this.fieldName);
}
else if (this.methodName != null) {
this.methodParameter = new MethodParameter(
this.declaringClass.getDeclaredMethod(this.methodName, this.parameterTypes), this.parameterIndex);
}
else {
this.methodParameter = new MethodParameter(
this.declaringClass.getDeclaredConstructor(this.parameterTypes), this.parameterIndex);
}
}
catch (Throwable ex) {
throw new IllegalStateException("Could not find original class structure", ex);
}
}
}
|