Rename modules {org.springframework.*=>spring-*}

This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.

Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example

    $ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history up until the renaming event, where

    $ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history for all changes to the file, before and after the
renaming.

See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
Chris Beams
2012-01-20 22:51:02 +01:00
parent b6cb514d38
commit 02a4473c62
5671 changed files with 20 additions and 32 deletions

View File

@@ -0,0 +1,139 @@
/*
* 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;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/**
* Abstract implementation of the {@link PropertyAccessor} interface.
* Provides base implementations of all convenience methods, with the
* implementation of actual property access left to subclasses.
*
* @author Juergen Hoeller
* @since 2.0
* @see #getPropertyValue
* @see #setPropertyValue
*/
public abstract class AbstractPropertyAccessor extends PropertyEditorRegistrySupport
implements ConfigurablePropertyAccessor {
private boolean extractOldValueForEditor = false;
public void setExtractOldValueForEditor(boolean extractOldValueForEditor) {
this.extractOldValueForEditor = extractOldValueForEditor;
}
public boolean isExtractOldValueForEditor() {
return this.extractOldValueForEditor;
}
public void setPropertyValue(PropertyValue pv) throws BeansException {
setPropertyValue(pv.getName(), pv.getValue());
}
public void setPropertyValues(Map<?, ?> map) throws BeansException {
setPropertyValues(new MutablePropertyValues(map));
}
public void setPropertyValues(PropertyValues pvs) throws BeansException {
setPropertyValues(pvs, false, false);
}
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown) throws BeansException {
setPropertyValues(pvs, ignoreUnknown, false);
}
public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException {
List<PropertyAccessException> propertyAccessExceptions = null;
List<PropertyValue> propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
for (PropertyValue pv : propertyValues) {
try {
// This method may throw any BeansException, which won't be caught
// here, if there is a critical failure such as no matching field.
// We can attempt to deal only with less serious exceptions.
setPropertyValue(pv);
}
catch (NotWritablePropertyException ex) {
if (!ignoreUnknown) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (NullValueInNestedPathException ex) {
if (!ignoreInvalid) {
throw ex;
}
// Otherwise, just ignore it and continue...
}
catch (PropertyAccessException ex) {
if (propertyAccessExceptions == null) {
propertyAccessExceptions = new LinkedList<PropertyAccessException>();
}
propertyAccessExceptions.add(ex);
}
}
// If we encountered individual exceptions, throw the composite exception.
if (propertyAccessExceptions != null) {
PropertyAccessException[] paeArray =
propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
throw new PropertyBatchUpdateException(paeArray);
}
}
public <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException {
return convertIfNecessary(value, requiredType, null);
}
// Redefined with public visibility.
@Override
public Class getPropertyType(String propertyPath) {
return null;
}
/**
* Actually get the value of a property.
* @param propertyName name of the property to get the value of
* @return the value of the property
* @throws InvalidPropertyException if there is no such property or
* if the property isn't readable
* @throws PropertyAccessException if the property was valid but the
* accessor method failed
*/
public abstract Object getPropertyValue(String propertyName) throws BeansException;
/**
* Actually set a property value.
* @param propertyName name of the property to set value of
* @param value the new value
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyAccessException if the property was valid but the
* accessor method failed or a type mismatch occured
*/
public abstract void setPropertyValue(String propertyName, Object value) throws BeansException;
}

View File

@@ -0,0 +1,58 @@
/*
* Copyright 2002-2006 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;
/**
* Exception thrown when instantiation of a bean failed.
* Carries the offending bean class.
*
* @author Juergen Hoeller
* @since 1.2.8
*/
public class BeanInstantiationException extends FatalBeanException {
private Class beanClass;
/**
* Create a new BeanInstantiationException.
* @param beanClass the offending bean class
* @param msg the detail message
*/
public BeanInstantiationException(Class beanClass, String msg) {
this(beanClass, msg, null);
}
/**
* Create a new BeanInstantiationException.
* @param beanClass the offending bean class
* @param msg the detail message
* @param cause the root cause
*/
public BeanInstantiationException(Class beanClass, String msg, Throwable cause) {
super("Could not instantiate bean class [" + beanClass.getName() + "]: " + msg, cause);
this.beanClass = beanClass;
}
/**
* Return the offending bean class.
*/
public Class getBeanClass() {
return beanClass;
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2002-2007 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;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Holder for a key-value style attribute that is part of a bean definition.
* Keeps track of the definition source in addition to the key-value pair.
*
* @author Juergen Hoeller
* @since 2.5
*/
public class BeanMetadataAttribute implements BeanMetadataElement {
private final String name;
private final Object value;
private Object source;
/**
* Create a new AttributeValue instance.
* @param name the name of the attribute (never <code>null</code>)
* @param value the value of the attribute (possibly before type conversion)
*/
public BeanMetadataAttribute(String name, Object value) {
Assert.notNull(name, "Name must not be null");
this.name = name;
this.value = value;
}
/**
* Return the name of the attribute.
*/
public String getName() {
return this.name;
}
/**
* Return the value of the attribute.
*/
public Object getValue() {
return this.value;
}
/**
* Set the configuration source <code>Object</code> for this metadata element.
* <p>The exact type of the object will depend on the configuration mechanism used.
*/
public void setSource(Object source) {
this.source = source;
}
public Object getSource() {
return this.source;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof BeanMetadataAttribute)) {
return false;
}
BeanMetadataAttribute otherMa = (BeanMetadataAttribute) other;
return (this.name.equals(otherMa.name) &&
ObjectUtils.nullSafeEquals(this.value, otherMa.value) &&
ObjectUtils.nullSafeEquals(this.source, otherMa.source));
}
@Override
public int hashCode() {
return this.name.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.value);
}
@Override
public String toString() {
return "metadata attribute '" + this.name + "'";
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright 2002-2007 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;
import org.springframework.core.AttributeAccessorSupport;
/**
* Extension of {@link org.springframework.core.AttributeAccessorSupport},
* holding attributes as {@link BeanMetadataAttribute} objects in order
* to keep track of the definition source.
*
* @author Juergen Hoeller
* @since 2.5
*/
public class BeanMetadataAttributeAccessor extends AttributeAccessorSupport implements BeanMetadataElement {
private Object source;
/**
* Set the configuration source <code>Object</code> for this metadata element.
* <p>The exact type of the object will depend on the configuration mechanism used.
*/
public void setSource(Object source) {
this.source = source;
}
public Object getSource() {
return this.source;
}
/**
* Add the given BeanMetadataAttribute to this accessor's set of attributes.
* @param attribute the BeanMetadataAttribute object to register
*/
public void addMetadataAttribute(BeanMetadataAttribute attribute) {
super.setAttribute(attribute.getName(), attribute);
}
/**
* Look up the given BeanMetadataAttribute in this accessor's set of attributes.
* @param name the name of the attribute
* @return the corresponding BeanMetadataAttribute object,
* or <code>null</code> if no such attribute defined
*/
public BeanMetadataAttribute getMetadataAttribute(String name) {
return (BeanMetadataAttribute) super.getAttribute(name);
}
@Override
public void setAttribute(String name, Object value) {
super.setAttribute(name, new BeanMetadataAttribute(name, value));
}
@Override
public Object getAttribute(String name) {
BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);
return (attribute != null ? attribute.getValue() : null);
}
@Override
public Object removeAttribute(String name) {
BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.removeAttribute(name);
return (attribute != null ? attribute.getValue() : null);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright 2002-2006 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;
/**
* Interface to be implemented by bean metadata elements
* that carry a configuration source object.
*
* @author Juergen Hoeller
* @since 2.0
*/
public interface BeanMetadataElement {
/**
* Return the configuration source <code>Object</code> for this metadata element
* (may be <code>null</code>).
*/
Object getSource();
}

View File

@@ -0,0 +1,627 @@
/*
* Copyright 2002-2010 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;
import java.beans.PropertyDescriptor;
import java.beans.PropertyEditor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Static convenience methods for JavaBeans: for instantiating beans,
* checking bean property types, copying bean properties, etc.
*
* <p>Mainly for use within the framework, but to some degree also
* useful for application classes.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @author Sam Brannen
*/
public abstract class BeanUtils {
private static final Log logger = LogFactory.getLog(BeanUtils.class);
private static final Map<Class<?>, Boolean> unknownEditorTypes =
Collections.synchronizedMap(new WeakHashMap<Class<?>, Boolean>());
/**
* Convenience method to instantiate a class using its no-arg constructor.
* As this method doesn't try to load classes by name, it should avoid
* class-loading issues.
* @param clazz class to instantiate
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
*/
public static <T> T instantiate(Class<T> clazz) throws BeanInstantiationException {
Assert.notNull(clazz, "Class must not be null");
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
return clazz.newInstance();
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(clazz, "Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(clazz, "Is the constructor accessible?", ex);
}
}
/**
* Instantiate a class using its no-arg constructor.
* As this method doesn't try to load classes by name, it should avoid
* class-loading issues.
* <p>Note that this method tries to set the constructor accessible
* if given a non-accessible (that is, non-public) constructor.
* @param clazz class to instantiate
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
*/
public static <T> T instantiateClass(Class<T> clazz) throws BeanInstantiationException {
Assert.notNull(clazz, "Class must not be null");
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
return instantiateClass(clazz.getDeclaredConstructor());
}
catch (NoSuchMethodException ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
/**
* Instantiate a class using its no-arg constructor and return the new instance
* as the the specified assignable type.
* <p>Useful in cases where
* the type of the class to instantiate (clazz) is not available, but the type
* desired (assignableTo) is known.
* <p>As this method doesn't try to load classes by name, it should avoid
* class-loading issues.
* <p>Note that this method tries to set the constructor accessible
* if given a non-accessible (that is, non-public) constructor.
* @param clazz class to instantiate
* @param assignableTo type that clazz must be assignableTo
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
*/
@SuppressWarnings("unchecked")
public static <T> T instantiateClass(Class<?> clazz, Class<T> assignableTo) throws BeanInstantiationException {
Assert.isAssignable(assignableTo, clazz);
return (T)instantiateClass(clazz);
}
/**
* Convenience method to instantiate a class using the given constructor.
* As this method doesn't try to load classes by name, it should avoid
* class-loading issues.
* <p>Note that this method tries to set the constructor accessible
* if given a non-accessible (that is, non-public) constructor.
* @param ctor the constructor to instantiate
* @param args the constructor arguments to apply
* @return the new instance
* @throws BeanInstantiationException if the bean cannot be instantiated
*/
public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
Assert.notNull(ctor, "Constructor must not be null");
try {
ReflectionUtils.makeAccessible(ctor);
return ctor.newInstance(args);
}
catch (InstantiationException ex) {
throw new BeanInstantiationException(ctor.getDeclaringClass(),
"Is it an abstract class?", ex);
}
catch (IllegalAccessException ex) {
throw new BeanInstantiationException(ctor.getDeclaringClass(),
"Is the constructor accessible?", ex);
}
catch (IllegalArgumentException ex) {
throw new BeanInstantiationException(ctor.getDeclaringClass(),
"Illegal arguments for constructor", ex);
}
catch (InvocationTargetException ex) {
throw new BeanInstantiationException(ctor.getDeclaringClass(),
"Constructor threw exception", ex.getTargetException());
}
}
/**
* Find a method with the given method name and the given parameter types,
* declared on the given class or one of its superclasses. Prefers public methods,
* but will return a protected, package access, or private method too.
* <p>Checks <code>Class.getMethod</code> first, falling back to
* <code>findDeclaredMethod</code>. This allows to find public methods
* without issues even in environments with restricted Java security settings.
* @param clazz the class to check
* @param methodName the name of the method to find
* @param paramTypes the parameter types of the method to find
* @return the Method object, or <code>null</code> if not found
* @see java.lang.Class#getMethod
* @see #findDeclaredMethod
*/
public static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
try {
return clazz.getMethod(methodName, paramTypes);
}
catch (NoSuchMethodException ex) {
return findDeclaredMethod(clazz, methodName, paramTypes);
}
}
/**
* Find a method with the given method name and the given parameter types,
* declared on the given class or one of its superclasses. Will return a public,
* protected, package access, or private method.
* <p>Checks <code>Class.getDeclaredMethod</code>, cascading upwards to all superclasses.
* @param clazz the class to check
* @param methodName the name of the method to find
* @param paramTypes the parameter types of the method to find
* @return the Method object, or <code>null</code> if not found
* @see java.lang.Class#getDeclaredMethod
*/
public static Method findDeclaredMethod(Class<?> clazz, String methodName, Class<?>[] paramTypes) {
try {
return clazz.getDeclaredMethod(methodName, paramTypes);
}
catch (NoSuchMethodException ex) {
if (clazz.getSuperclass() != null) {
return findDeclaredMethod(clazz.getSuperclass(), methodName, paramTypes);
}
return null;
}
}
/**
* Find a method with the given method name and minimal parameters (best case: none),
* declared on the given class or one of its superclasses. Prefers public methods,
* but will return a protected, package access, or private method too.
* <p>Checks <code>Class.getMethods</code> first, falling back to
* <code>findDeclaredMethodWithMinimalParameters</code>. This allows for finding public
* methods without issues even in environments with restricted Java security settings.
* @param clazz the class to check
* @param methodName the name of the method to find
* @return the Method object, or <code>null</code> if not found
* @throws IllegalArgumentException if methods of the given name were found but
* could not be resolved to a unique method with minimal parameters
* @see java.lang.Class#getMethods
* @see #findDeclaredMethodWithMinimalParameters
*/
public static Method findMethodWithMinimalParameters(Class<?> clazz, String methodName)
throws IllegalArgumentException {
Method targetMethod = findMethodWithMinimalParameters(clazz.getMethods(), methodName);
if (targetMethod == null) {
targetMethod = findDeclaredMethodWithMinimalParameters(clazz, methodName);
}
return targetMethod;
}
/**
* Find a method with the given method name and minimal parameters (best case: none),
* declared on the given class or one of its superclasses. Will return a public,
* protected, package access, or private method.
* <p>Checks <code>Class.getDeclaredMethods</code>, cascading upwards to all superclasses.
* @param clazz the class to check
* @param methodName the name of the method to find
* @return the Method object, or <code>null</code> if not found
* @throws IllegalArgumentException if methods of the given name were found but
* could not be resolved to a unique method with minimal parameters
* @see java.lang.Class#getDeclaredMethods
*/
public static Method findDeclaredMethodWithMinimalParameters(Class<?> clazz, String methodName)
throws IllegalArgumentException {
Method targetMethod = findMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName);
if (targetMethod == null && clazz.getSuperclass() != null) {
targetMethod = findDeclaredMethodWithMinimalParameters(clazz.getSuperclass(), methodName);
}
return targetMethod;
}
/**
* Find a method with the given method name and minimal parameters (best case: none)
* in the given list of methods.
* @param methods the methods to check
* @param methodName the name of the method to find
* @return the Method object, or <code>null</code> if not found
* @throws IllegalArgumentException if methods of the given name were found but
* could not be resolved to a unique method with minimal parameters
*/
public static Method findMethodWithMinimalParameters(Method[] methods, String methodName)
throws IllegalArgumentException {
Method targetMethod = null;
int numMethodsFoundWithCurrentMinimumArgs = 0;
for (Method method : methods) {
if (method.getName().equals(methodName)) {
int numParams = method.getParameterTypes().length;
if (targetMethod == null || numParams < targetMethod.getParameterTypes().length) {
targetMethod = method;
numMethodsFoundWithCurrentMinimumArgs = 1;
}
else {
if (targetMethod.getParameterTypes().length == numParams) {
// Additional candidate with same length.
numMethodsFoundWithCurrentMinimumArgs++;
}
}
}
}
if (numMethodsFoundWithCurrentMinimumArgs > 1) {
throw new IllegalArgumentException("Cannot resolve method '" + methodName +
"' to a unique method. Attempted to resolve to overloaded method with " +
"the least number of parameters, but there were " +
numMethodsFoundWithCurrentMinimumArgs + " candidates.");
}
return targetMethod;
}
/**
* Parse a method signature in the form <code>methodName[([arg_list])]</code>,
* where <code>arg_list</code> is an optional, comma-separated list of fully-qualified
* type names, and attempts to resolve that signature against the supplied <code>Class</code>.
* <p>When not supplying an argument list (<code>methodName</code>) the method whose name
* matches and has the least number of parameters will be returned. When supplying an
* argument type list, only the method whose name and argument types match will be returned.
* <p>Note then that <code>methodName</code> and <code>methodName()</code> are <strong>not</strong>
* resolved in the same way. The signature <code>methodName</code> means the method called
* <code>methodName</code> with the least number of arguments, whereas <code>methodName()</code>
* means the method called <code>methodName</code> with exactly 0 arguments.
* <p>If no method can be found, then <code>null</code> is returned.
* @param signature the method signature as String representation
* @param clazz the class to resolve the method signature against
* @return the resolved Method
* @see #findMethod
* @see #findMethodWithMinimalParameters
*/
public static Method resolveSignature(String signature, Class<?> clazz) {
Assert.hasText(signature, "'signature' must not be empty");
Assert.notNull(clazz, "Class must not be null");
int firstParen = signature.indexOf("(");
int lastParen = signature.indexOf(")");
if (firstParen > -1 && lastParen == -1) {
throw new IllegalArgumentException("Invalid method signature '" + signature +
"': expected closing ')' for args list");
}
else if (lastParen > -1 && firstParen == -1) {
throw new IllegalArgumentException("Invalid method signature '" + signature +
"': expected opening '(' for args list");
}
else if (firstParen == -1 && lastParen == -1) {
return findMethodWithMinimalParameters(clazz, signature);
}
else {
String methodName = signature.substring(0, firstParen);
String[] parameterTypeNames =
StringUtils.commaDelimitedListToStringArray(signature.substring(firstParen + 1, lastParen));
Class<?>[] parameterTypes = new Class[parameterTypeNames.length];
for (int i = 0; i < parameterTypeNames.length; i++) {
String parameterTypeName = parameterTypeNames[i].trim();
try {
parameterTypes[i] = ClassUtils.forName(parameterTypeName, clazz.getClassLoader());
}
catch (Throwable ex) {
throw new IllegalArgumentException("Invalid method signature: unable to resolve type [" +
parameterTypeName + "] for argument " + i + ". Root cause: " + ex);
}
}
return findMethod(clazz, methodName, parameterTypes);
}
}
/**
* Retrieve the JavaBeans <code>PropertyDescriptor</code>s of a given class.
* @param clazz the Class to retrieve the PropertyDescriptors for
* @return an array of <code>PropertyDescriptors</code> for the given class
* @throws BeansException if PropertyDescriptor look fails
*/
public static PropertyDescriptor[] getPropertyDescriptors(Class<?> clazz) throws BeansException {
CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
return cr.getPropertyDescriptors();
}
/**
* Retrieve the JavaBeans <code>PropertyDescriptors</code> for the given property.
* @param clazz the Class to retrieve the PropertyDescriptor for
* @param propertyName the name of the property
* @return the corresponding PropertyDescriptor, or <code>null</code> if none
* @throws BeansException if PropertyDescriptor lookup fails
*/
public static PropertyDescriptor getPropertyDescriptor(Class<?> clazz, String propertyName)
throws BeansException {
CachedIntrospectionResults cr = CachedIntrospectionResults.forClass(clazz);
return cr.getPropertyDescriptor(propertyName);
}
/**
* Find a JavaBeans <code>PropertyDescriptor</code> for the given method,
* with the method either being the read method or the write method for
* that bean property.
* @param method the method to find a corresponding PropertyDescriptor for
* @return the corresponding PropertyDescriptor, or <code>null</code> if none
* @throws BeansException if PropertyDescriptor lookup fails
*/
public static PropertyDescriptor findPropertyForMethod(Method method) throws BeansException {
Assert.notNull(method, "Method must not be null");
PropertyDescriptor[] pds = getPropertyDescriptors(method.getDeclaringClass());
for (PropertyDescriptor pd : pds) {
if (method.equals(pd.getReadMethod()) || method.equals(pd.getWriteMethod())) {
return pd;
}
}
return null;
}
/**
* Find a JavaBeans PropertyEditor following the 'Editor' suffix convention
* (e.g. "mypackage.MyDomainClass" -> "mypackage.MyDomainClassEditor").
* <p>Compatible to the standard JavaBeans convention as implemented by
* {@link java.beans.PropertyEditorManager} but isolated from the latter's
* registered default editors for primitive types.
* @param targetType the type to find an editor for
* @return the corresponding editor, or <code>null</code> if none found
*/
public static PropertyEditor findEditorByConvention(Class<?> targetType) {
if (targetType == null || targetType.isArray() || unknownEditorTypes.containsKey(targetType)) {
return null;
}
ClassLoader cl = targetType.getClassLoader();
if (cl == null) {
try {
cl = ClassLoader.getSystemClassLoader();
if (cl == null) {
return null;
}
}
catch (Throwable ex) {
// e.g. AccessControlException on Google App Engine
if (logger.isDebugEnabled()) {
logger.debug("Could not access system ClassLoader: " + ex);
}
return null;
}
}
String editorName = targetType.getName() + "Editor";
try {
Class<?> editorClass = cl.loadClass(editorName);
if (!PropertyEditor.class.isAssignableFrom(editorClass)) {
if (logger.isWarnEnabled()) {
logger.warn("Editor class [" + editorName +
"] does not implement [java.beans.PropertyEditor] interface");
}
unknownEditorTypes.put(targetType, Boolean.TRUE);
return null;
}
return (PropertyEditor) instantiateClass(editorClass);
}
catch (ClassNotFoundException ex) {
if (logger.isDebugEnabled()) {
logger.debug("No property editor [" + editorName + "] found for type " +
targetType.getName() + " according to 'Editor' suffix convention");
}
unknownEditorTypes.put(targetType, Boolean.TRUE);
return null;
}
}
/**
* Determine the bean property type for the given property from the
* given classes/interfaces, if possible.
* @param propertyName the name of the bean property
* @param beanClasses the classes to check against
* @return the property type, or <code>Object.class</code> as fallback
*/
public static Class<?> findPropertyType(String propertyName, Class<?>[] beanClasses) {
if (beanClasses != null) {
for (Class<?> beanClass : beanClasses) {
PropertyDescriptor pd = getPropertyDescriptor(beanClass, propertyName);
if (pd != null) {
return pd.getPropertyType();
}
}
}
return Object.class;
}
/**
* Obtain a new MethodParameter object for the write method of the
* specified property.
* @param pd the PropertyDescriptor for the property
* @return a corresponding MethodParameter object
*/
public static MethodParameter getWriteMethodParameter(PropertyDescriptor pd) {
if (pd instanceof GenericTypeAwarePropertyDescriptor) {
return new MethodParameter(
((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodParameter());
}
else {
return new MethodParameter(pd.getWriteMethod(), 0);
}
}
/**
* Check if the given type represents a "simple" property:
* a primitive, a String or other CharSequence, a Number, a Date,
* a URI, a URL, a Locale, a Class, or a corresponding array.
* <p>Used to determine properties to check for a "simple" dependency-check.
* @param clazz the type to check
* @return whether the given type represents a "simple" property
* @see org.springframework.beans.factory.support.RootBeanDefinition#DEPENDENCY_CHECK_SIMPLE
* @see org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#checkDependencies
*/
public static boolean isSimpleProperty(Class<?> clazz) {
Assert.notNull(clazz, "Class must not be null");
return isSimpleValueType(clazz) || (clazz.isArray() && isSimpleValueType(clazz.getComponentType()));
}
/**
* Check if the given type represents a "simple" value type:
* a primitive, a String or other CharSequence, a Number, a Date,
* a URI, a URL, a Locale or a Class.
* @param clazz the type to check
* @return whether the given type represents a "simple" value type
*/
public static boolean isSimpleValueType(Class<?> clazz) {
return ClassUtils.isPrimitiveOrWrapper(clazz) || clazz.isEnum() ||
CharSequence.class.isAssignableFrom(clazz) ||
Number.class.isAssignableFrom(clazz) ||
Date.class.isAssignableFrom(clazz) ||
clazz.equals(URI.class) || clazz.equals(URL.class) ||
clazz.equals(Locale.class) || clazz.equals(Class.class);
}
/**
* Copy the property values of the given source bean into the target bean.
* <p>Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
* consider using a full BeanWrapper.
* @param source the source bean
* @param target the target bean
* @throws BeansException if the copying failed
* @see BeanWrapper
*/
public static void copyProperties(Object source, Object target) throws BeansException {
copyProperties(source, target, null, null);
}
/**
* Copy the property values of the given source bean into the given target bean,
* only setting properties defined in the given "editable" class (or interface).
* <p>Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
* consider using a full BeanWrapper.
* @param source the source bean
* @param target the target bean
* @param editable the class (or interface) to restrict property setting to
* @throws BeansException if the copying failed
* @see BeanWrapper
*/
public static void copyProperties(Object source, Object target, Class<?> editable)
throws BeansException {
copyProperties(source, target, editable, null);
}
/**
* Copy the property values of the given source bean into the given target bean,
* ignoring the given "ignoreProperties".
* <p>Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* <p>This is just a convenience method. For more complex transfer needs,
* consider using a full BeanWrapper.
* @param source the source bean
* @param target the target bean
* @param ignoreProperties array of property names to ignore
* @throws BeansException if the copying failed
* @see BeanWrapper
*/
public static void copyProperties(Object source, Object target, String[] ignoreProperties)
throws BeansException {
copyProperties(source, target, null, ignoreProperties);
}
/**
* Copy the property values of the given source bean into the given target bean.
* <p>Note: The source and target classes do not have to match or even be derived
* from each other, as long as the properties match. Any bean properties that the
* source bean exposes but the target bean does not will silently be ignored.
* @param source the source bean
* @param target the target bean
* @param editable the class (or interface) to restrict property setting to
* @param ignoreProperties array of property names to ignore
* @throws BeansException if the copying failed
* @see BeanWrapper
*/
private static void copyProperties(Object source, Object target, Class<?> editable, String[] ignoreProperties)
throws BeansException {
Assert.notNull(source, "Source must not be null");
Assert.notNull(target, "Target must not be null");
Class<?> actualEditable = target.getClass();
if (editable != null) {
if (!editable.isInstance(target)) {
throw new IllegalArgumentException("Target class [" + target.getClass().getName() +
"] not assignable to Editable class [" + editable.getName() + "]");
}
actualEditable = editable;
}
PropertyDescriptor[] targetPds = getPropertyDescriptors(actualEditable);
List<String> ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
for (PropertyDescriptor targetPd : targetPds) {
if (targetPd.getWriteMethod() != null &&
(ignoreProperties == null || (!ignoreList.contains(targetPd.getName())))) {
PropertyDescriptor sourcePd = getPropertyDescriptor(source.getClass(), targetPd.getName());
if (sourcePd != null && sourcePd.getReadMethod() != null) {
try {
Method readMethod = sourcePd.getReadMethod();
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(source);
Method writeMethod = targetPd.getWriteMethod();
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, value);
}
catch (Throwable ex) {
throw new FatalBeanException("Could not copy properties from source to target", ex);
}
}
}
}
}
}

View File

@@ -0,0 +1,106 @@
/*
* Copyright 2002-2011 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;
import java.beans.PropertyDescriptor;
/**
* The central interface of Spring's low-level JavaBeans infrastructure.
*
* <p>Typically not used directly but rather implicitly via a
* {@link org.springframework.beans.factory.BeanFactory} or a
* {@link org.springframework.validation.DataBinder}.
*
* <p>Provides operations to analyze and manipulate standard JavaBeans:
* the ability to get and set property values (individually or in bulk),
* get property descriptors, and query the readability/writability of properties.
*
* <p>This interface supports <b>nested properties</b> enabling the setting
* of properties on subproperties to an unlimited depth.
*
* <p>A BeanWrapper's default for the "extractOldValueForEditor" setting
* is "false", to avoid side effects caused by getter method invocations.
* Turn this to "true" to expose present property values to custom editors.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 13 April 2001
* @see PropertyAccessor
* @see PropertyEditorRegistry
* @see PropertyAccessorFactory#forBeanPropertyAccess
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.validation.BeanPropertyBindingResult
* @see org.springframework.validation.DataBinder#initBeanPropertyAccess()
*/
public interface BeanWrapper extends ConfigurablePropertyAccessor {
/**
* Return the bean instance wrapped by this object, if any.
* @return the bean instance, or <code>null</code> if none set
*/
Object getWrappedInstance();
/**
* Return the type of the wrapped JavaBean object.
* @return the type of the wrapped bean instance,
* or <code>null</code> if no wrapped object has been set
*/
Class getWrappedClass();
/**
* Obtain the PropertyDescriptors for the wrapped object
* (as determined by standard JavaBeans introspection).
* @return the PropertyDescriptors for the wrapped object
*/
PropertyDescriptor[] getPropertyDescriptors();
/**
* Obtain the property descriptor for a specific property
* of the wrapped object.
* @param propertyName the property to obtain the descriptor for
* (may be a nested path, but no indexed/mapped property)
* @return the property descriptor for the specified property
* @throws InvalidPropertyException if there is no such property
*/
PropertyDescriptor getPropertyDescriptor(String propertyName) throws InvalidPropertyException;
/**
* Set whether this BeanWrapper should attempt to "auto-grow" a nested path that contains a null value.
* <p>If "true", a null path location will be populated with a default object value and traversed
* instead of resulting in a {@link NullValueInNestedPathException}. Turning this flag on also
* enables auto-growth of collection elements when accessing an out-of-bounds index.
* <p>Default is "false" on a plain BeanWrapper.
*/
void setAutoGrowNestedPaths(boolean autoGrowNestedPaths);
/**
* Return whether "auto-growing" of nested paths has been activated.
*/
boolean isAutoGrowNestedPaths();
/**
* Specify a limit for array and collection auto-growing.
* <p>Default is unlimited on a plain BeanWrapper.
*/
void setAutoGrowCollectionLimit(int autoGrowCollectionLimit);
/**
* Return the limit for array and collection auto-growing.
*/
int getAutoGrowCollectionLimit();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,71 @@
/*
* 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;
import org.springframework.core.NestedRuntimeException;
import org.springframework.util.ObjectUtils;
/**
* Abstract superclass for all exceptions thrown in the beans package
* and subpackages.
*
* <p>Note that this is a runtime (unchecked) exception. Beans exceptions
* are usually fatal; there is no reason for them to be checked.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public abstract class BeansException extends NestedRuntimeException {
/**
* Create a new BeansException with the specified message.
* @param msg the detail message
*/
public BeansException(String msg) {
super(msg);
}
/**
* Create a new BeansException with the specified message
* and root cause.
* @param msg the detail message
* @param cause the root cause
*/
public BeansException(String msg, Throwable cause) {
super(msg, cause);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof BeansException)) {
return false;
}
BeansException otherBe = (BeansException) other;
return (getMessage().equals(otherBe.getMessage()) &&
ObjectUtils.nullSafeEquals(getCause(), otherBe.getCause()));
}
@Override
public int hashCode() {
return getMessage().hashCode();
}
}

View File

@@ -0,0 +1,308 @@
/*
* Copyright 2002-2010 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;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Internal class that caches JavaBeans {@link java.beans.PropertyDescriptor}
* information for a Java class. Not intended for direct use by application code.
*
* <p>Necessary for own caching of descriptors within the application's
* ClassLoader, rather than rely on the JDK's system-wide BeanInfo cache
* (in order to avoid leaks on ClassLoader shutdown).
*
* <p>Information is cached statically, so we don't need to create new
* objects of this class for every JavaBean we manipulate. Hence, this class
* implements the factory design pattern, using a private constructor and
* a static {@link #forClass(Class)} factory method to obtain instances.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 05 May 2001
* @see #acceptClassLoader(ClassLoader)
* @see #clearClassLoader(ClassLoader)
* @see #forClass(Class)
*/
public class CachedIntrospectionResults {
private static final Log logger = LogFactory.getLog(CachedIntrospectionResults.class);
/**
* Set of ClassLoaders that this CachedIntrospectionResults class will always
* accept classes from, even if the classes do not qualify as cache-safe.
*/
static final Set<ClassLoader> acceptedClassLoaders = Collections.synchronizedSet(new HashSet<ClassLoader>());
/**
* Map keyed by class containing CachedIntrospectionResults.
* Needs to be a WeakHashMap with WeakReferences as values to allow
* for proper garbage collection in case of multiple class loaders.
*/
static final Map<Class, Object> classCache = Collections.synchronizedMap(new WeakHashMap<Class, Object>());
/**
* Accept the given ClassLoader as cache-safe, even if its classes would
* not qualify as cache-safe in this CachedIntrospectionResults class.
* <p>This configuration method is only relevant in scenarios where the Spring
* classes reside in a 'common' ClassLoader (e.g. the system ClassLoader)
* whose lifecycle is not coupled to the application. In such a scenario,
* CachedIntrospectionResults would by default not cache any of the application's
* classes, since they would create a leak in the common ClassLoader.
* <p>Any <code>acceptClassLoader</code> call at application startup should
* be paired with a {@link #clearClassLoader} call at application shutdown.
* @param classLoader the ClassLoader to accept
*/
public static void acceptClassLoader(ClassLoader classLoader) {
if (classLoader != null) {
acceptedClassLoaders.add(classLoader);
}
}
/**
* Clear the introspection cache for the given ClassLoader, removing the
* introspection results for all classes underneath that ClassLoader,
* and deregistering the ClassLoader (and any of its children) from the
* acceptance list.
* @param classLoader the ClassLoader to clear the cache for
*/
public static void clearClassLoader(ClassLoader classLoader) {
if (classLoader == null) {
return;
}
synchronized (classCache) {
for (Iterator<Class> it = classCache.keySet().iterator(); it.hasNext();) {
Class beanClass = it.next();
if (isUnderneathClassLoader(beanClass.getClassLoader(), classLoader)) {
it.remove();
}
}
}
synchronized (acceptedClassLoaders) {
for (Iterator<ClassLoader> it = acceptedClassLoaders.iterator(); it.hasNext();) {
ClassLoader registeredLoader = it.next();
if (isUnderneathClassLoader(registeredLoader, classLoader)) {
it.remove();
}
}
}
}
/**
* Create CachedIntrospectionResults for the given bean class.
* <P>We don't want to use synchronization here. Object references are atomic,
* so we can live with doing the occasional unnecessary lookup at startup only.
* @param beanClass the bean class to analyze
* @return the corresponding CachedIntrospectionResults
* @throws BeansException in case of introspection failure
*/
static CachedIntrospectionResults forClass(Class beanClass) throws BeansException {
CachedIntrospectionResults results;
Object value = classCache.get(beanClass);
if (value instanceof Reference) {
Reference ref = (Reference) value;
results = (CachedIntrospectionResults) ref.get();
}
else {
results = (CachedIntrospectionResults) value;
}
if (results == null) {
// On JDK 1.5 and higher, it is almost always safe to cache the bean class...
// The sole exception is a custom BeanInfo class being provided in a non-safe ClassLoader.
boolean fullyCacheable =
ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
isClassLoaderAccepted(beanClass.getClassLoader());
if (fullyCacheable || !ClassUtils.isPresent(beanClass.getName() + "BeanInfo", beanClass.getClassLoader())) {
results = new CachedIntrospectionResults(beanClass, fullyCacheable);
classCache.put(beanClass, results);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
}
results = new CachedIntrospectionResults(beanClass, true);
classCache.put(beanClass, new WeakReference<CachedIntrospectionResults>(results));
}
}
return results;
}
/**
* Check whether this CachedIntrospectionResults class is configured
* to accept the given ClassLoader.
* @param classLoader the ClassLoader to check
* @return whether the given ClassLoader is accepted
* @see #acceptClassLoader
*/
private static boolean isClassLoaderAccepted(ClassLoader classLoader) {
// Iterate over array copy in order to avoid synchronization for the entire
// ClassLoader check (avoiding a synchronized acceptedClassLoaders Iterator).
ClassLoader[] acceptedLoaderArray =
acceptedClassLoaders.toArray(new ClassLoader[acceptedClassLoaders.size()]);
for (ClassLoader registeredLoader : acceptedLoaderArray) {
if (isUnderneathClassLoader(classLoader, registeredLoader)) {
return true;
}
}
return false;
}
/**
* Check whether the given ClassLoader is underneath the given parent,
* that is, whether the parent is within the candidate's hierarchy.
* @param candidate the candidate ClassLoader to check
* @param parent the parent ClassLoader to check for
*/
private static boolean isUnderneathClassLoader(ClassLoader candidate, ClassLoader parent) {
if (candidate == null) {
return false;
}
if (candidate == parent) {
return true;
}
ClassLoader classLoaderToCheck = candidate;
while (classLoaderToCheck != null) {
classLoaderToCheck = classLoaderToCheck.getParent();
if (classLoaderToCheck == parent) {
return true;
}
}
return false;
}
/** The BeanInfo object for the introspected bean class */
private final BeanInfo beanInfo;
/** PropertyDescriptor objects keyed by property name String */
private final Map<String, PropertyDescriptor> propertyDescriptorCache;
/**
* Create a new CachedIntrospectionResults instance for the given class.
* @param beanClass the bean class to analyze
* @throws BeansException in case of introspection failure
*/
private CachedIntrospectionResults(Class beanClass, boolean cacheFullMetadata) throws BeansException {
try {
if (logger.isTraceEnabled()) {
logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
}
this.beanInfo = new ExtendedBeanInfo(Introspector.getBeanInfo(beanClass));
// Immediately remove class from Introspector cache, to allow for proper
// garbage collection on class loader shutdown - we cache it here anyway,
// in a GC-friendly manner. In contrast to CachedIntrospectionResults,
// Introspector does not use WeakReferences as values of its WeakHashMap!
Class classToFlush = beanClass;
do {
Introspector.flushFromCaches(classToFlush);
classToFlush = classToFlush.getSuperclass();
}
while (classToFlush != null);
if (logger.isTraceEnabled()) {
logger.trace("Caching PropertyDescriptors for class [" + beanClass.getName() + "]");
}
this.propertyDescriptorCache = new LinkedHashMap<String, PropertyDescriptor>();
// This call is slow so we do it once.
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
for (PropertyDescriptor pd : pds) {
if (Class.class.equals(beanClass) && "classLoader".equals(pd.getName())) {
// Ignore Class.getClassLoader() method - nobody needs to bind to that
continue;
}
if (logger.isTraceEnabled()) {
logger.trace("Found bean property '" + pd.getName() + "'" +
(pd.getPropertyType() != null ? " of type [" + pd.getPropertyType().getName() + "]" : "") +
(pd.getPropertyEditorClass() != null ?
"; editor [" + pd.getPropertyEditorClass().getName() + "]" : ""));
}
if (cacheFullMetadata) {
pd = buildGenericTypeAwarePropertyDescriptor(beanClass, pd);
}
this.propertyDescriptorCache.put(pd.getName(), pd);
}
}
catch (IntrospectionException ex) {
throw new FatalBeanException("Failed to obtain BeanInfo for class [" + beanClass.getName() + "]", ex);
}
}
BeanInfo getBeanInfo() {
return this.beanInfo;
}
Class getBeanClass() {
return this.beanInfo.getBeanDescriptor().getBeanClass();
}
PropertyDescriptor getPropertyDescriptor(String name) {
PropertyDescriptor pd = this.propertyDescriptorCache.get(name);
if (pd == null && StringUtils.hasLength(name)) {
// Same lenient fallback checking as in PropertyTypeDescriptor...
pd = this.propertyDescriptorCache.get(name.substring(0, 1).toLowerCase() + name.substring(1));
if (pd == null) {
pd = this.propertyDescriptorCache.get(name.substring(0, 1).toUpperCase() + name.substring(1));
}
}
return (pd == null || pd instanceof GenericTypeAwarePropertyDescriptor ? pd :
buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd));
}
PropertyDescriptor[] getPropertyDescriptors() {
PropertyDescriptor[] pds = new PropertyDescriptor[this.propertyDescriptorCache.size()];
int i = 0;
for (PropertyDescriptor pd : this.propertyDescriptorCache.values()) {
pds[i] = (pd instanceof GenericTypeAwarePropertyDescriptor ? pd :
buildGenericTypeAwarePropertyDescriptor(getBeanClass(), pd));
i++;
}
return pds;
}
private PropertyDescriptor buildGenericTypeAwarePropertyDescriptor(Class beanClass, PropertyDescriptor pd) {
try {
return new GenericTypeAwarePropertyDescriptor(beanClass, pd.getName(), pd.getReadMethod(),
pd.getWriteMethod(), pd.getPropertyEditorClass());
}
catch (IntrospectionException ex) {
throw new FatalBeanException("Failed to re-introspect class [" + beanClass.getName() + "]", ex);
}
}
}

View File

@@ -0,0 +1,57 @@
/*
* 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;
import org.springframework.core.convert.ConversionService;
/**
* Interface that encapsulates configuration methods for a PropertyAccessor.
* Also extends the PropertyEditorRegistry interface, which defines methods
* for PropertyEditor management.
*
* <p>Serves as base interface for {@link BeanWrapper}.
*
* @author Juergen Hoeller
* @since 2.0
* @see BeanWrapper
*/
public interface ConfigurablePropertyAccessor extends PropertyAccessor, PropertyEditorRegistry, TypeConverter {
/**
* Specify a Spring 3.0 ConversionService to use for converting
* property values, as an alternative to JavaBeans PropertyEditors.
*/
void setConversionService(ConversionService conversionService);
/**
* Return the associated ConversionService, if any.
*/
ConversionService getConversionService();
/**
* Set whether to extract the old property value when applying a
* property editor to a new value for a property.
*/
void setExtractOldValueForEditor(boolean extractOldValueForEditor);
/**
* Return whether to extract the old property value when applying a
* property editor to a new value for a property.
*/
boolean isExtractOldValueForEditor();
}

View File

@@ -0,0 +1,50 @@
/*
* 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;
import java.beans.PropertyChangeEvent;
/**
* Exception thrown when no suitable editor or converter can be found for a bean property.
*
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 3.0
*/
public class ConversionNotSupportedException extends TypeMismatchException {
/**
* Create a new ConversionNotSupportedException.
* @param propertyChangeEvent the PropertyChangeEvent that resulted in the problem
* @param requiredType the required target type (or <code>null</code> if not known)
* @param cause the root cause (may be <code>null</code>)
*/
public ConversionNotSupportedException(PropertyChangeEvent propertyChangeEvent, Class requiredType, Throwable cause) {
super(propertyChangeEvent, requiredType, cause);
}
/**
* Create a new ConversionNotSupportedException.
* @param value the offending value that couldn't be converted (may be <code>null</code>)
* @param requiredType the required target type (or <code>null</code> if not known)
* @param cause the root cause (may be <code>null</code>)
*/
public ConversionNotSupportedException(Object value, Class requiredType, Throwable cause) {
super(value, requiredType, cause);
}
}

View File

@@ -0,0 +1,169 @@
/*
* Copyright 2002-2011 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;
import java.beans.PropertyChangeEvent;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConverterNotFoundException;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
/**
* {@link PropertyAccessor} implementation that directly accesses instance fields.
* Allows for direct binding to fields instead of going through JavaBean setters.
*
* <p>This implementation just supports fields in the actual target object.
* It is not able to traverse nested fields.
*
* <p>A DirectFieldAccessor's default for the "extractOldValueForEditor" setting
* is "true", since a field can always be read without side effects.
*
* @author Juergen Hoeller
* @since 2.0
* @see #setExtractOldValueForEditor
* @see BeanWrapper
* @see org.springframework.validation.DirectFieldBindingResult
* @see org.springframework.validation.DataBinder#initDirectFieldAccess()
*/
public class DirectFieldAccessor extends AbstractPropertyAccessor {
private final Object target;
private final Map<String, Field> fieldMap = new HashMap<String, Field>();
private final TypeConverterDelegate typeConverterDelegate;
/**
* Create a new DirectFieldAccessor for the given target object.
* @param target the target object to access
*/
public DirectFieldAccessor(final Object target) {
Assert.notNull(target, "Target object must not be null");
this.target = target;
ReflectionUtils.doWithFields(this.target.getClass(), new ReflectionUtils.FieldCallback() {
public void doWith(Field field) {
if (fieldMap.containsKey(field.getName())) {
// ignore superclass declarations of fields already found in a subclass
} else {
fieldMap.put(field.getName(), field);
}
}
});
this.typeConverterDelegate = new TypeConverterDelegate(this, target);
registerDefaultEditors();
setExtractOldValueForEditor(true);
}
public boolean isReadableProperty(String propertyName) throws BeansException {
return this.fieldMap.containsKey(propertyName);
}
public boolean isWritableProperty(String propertyName) throws BeansException {
return this.fieldMap.containsKey(propertyName);
}
@Override
public Class<?> getPropertyType(String propertyName) throws BeansException {
Field field = this.fieldMap.get(propertyName);
if (field != null) {
return field.getType();
}
return null;
}
public TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException {
Field field = this.fieldMap.get(propertyName);
if (field != null) {
return new TypeDescriptor(field);
}
return null;
}
@Override
public Object getPropertyValue(String propertyName) throws BeansException {
Field field = this.fieldMap.get(propertyName);
if (field == null) {
throw new NotReadablePropertyException(
this.target.getClass(), propertyName, "Field '" + propertyName + "' does not exist");
}
try {
ReflectionUtils.makeAccessible(field);
return field.get(this.target);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(this.target.getClass(), propertyName, "Field is not accessible", ex);
}
}
@Override
public void setPropertyValue(String propertyName, Object newValue) throws BeansException {
Field field = this.fieldMap.get(propertyName);
if (field == null) {
throw new NotWritablePropertyException(
this.target.getClass(), propertyName, "Field '" + propertyName + "' does not exist");
}
Object oldValue = null;
try {
ReflectionUtils.makeAccessible(field);
oldValue = field.get(this.target);
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(
field.getName(), oldValue, newValue, field.getType(), new TypeDescriptor(field));
field.set(this.target, convertedValue);
}
catch (ConverterNotFoundException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.target, propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, field.getType(), ex);
}
catch (ConversionException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.target, propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, field.getType(), ex);
}
catch (IllegalStateException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.target, propertyName, oldValue, newValue);
throw new ConversionNotSupportedException(pce, field.getType(), ex);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.target, propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, field.getType(), ex);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(this.target.getClass(), propertyName, "Field is not accessible", ex);
}
}
public <T> T convertIfNecessary(
Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
catch (IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
catch (IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
}
}

View File

@@ -0,0 +1,368 @@
/*
* Copyright 2002-2011 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;
import static java.lang.String.format;
import java.awt.Image;
import java.beans.BeanDescriptor;
import java.beans.BeanInfo;
import java.beans.EventSetDescriptor;
import java.beans.IndexedPropertyDescriptor;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.Comparator;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
/**
* Decorates a standard {@link BeanInfo} object (likely created created by
* {@link Introspector#getBeanInfo(Class)}) by including non-void returning setter
* methods in the collection of {@link #getPropertyDescriptors() property descriptors}.
* Both regular and
* <a href="http://download.oracle.com/javase/tutorial/javabeans/properties/indexed.html">
* indexed properties</a> are fully supported.
*
* <p>The wrapped {@code BeanInfo} object is not modified in any way.
*
* @author Chris Beams
* @since 3.1
* @see CachedIntrospectionResults
*/
class ExtendedBeanInfo implements BeanInfo {
private final Log logger = LogFactory.getLog(getClass());
private final BeanInfo delegate;
private final SortedSet<PropertyDescriptor> propertyDescriptors =
new TreeSet<PropertyDescriptor>(new PropertyDescriptorComparator());
/**
* Wrap the given delegate {@link BeanInfo} instance and find any non-void returning
* setter methods, creating and adding a {@link PropertyDescriptor} for each.
*
* <p>Note that the wrapped {@code BeanInfo} is modified by this process.
*
* @see #getPropertyDescriptors()
* @throws IntrospectionException if any problems occur creating and adding new {@code PropertyDescriptors}
*/
public ExtendedBeanInfo(BeanInfo delegate) throws IntrospectionException {
this.delegate = delegate;
// PropertyDescriptor instances from the delegate object are never added directly, but always
// copied to the local collection of #propertyDescriptors and returned by calls to
// #getPropertyDescriptors(). this algorithm iterates through all methods (method descriptors)
// in the wrapped BeanInfo object, copying any existing PropertyDescriptor or creating a new
// one for any non-standard setter methods found.
ALL_METHODS:
for (MethodDescriptor md : delegate.getMethodDescriptors()) {
Method method = md.getMethod();
// bypass non-getter java.lang.Class methods for efficiency
if (ReflectionUtils.isObjectMethod(method) && !method.getName().startsWith("get")) {
continue ALL_METHODS;
}
// is the method a NON-INDEXED setter? ignore return type in order to capture non-void signatures
if (method.getName().startsWith("set") && method.getParameterTypes().length == 1) {
String propertyName = propertyNameFor(method);
if(propertyName.length() == 0) {
continue ALL_METHODS;
}
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
Method readMethod = pd.getReadMethod();
Method writeMethod = pd.getWriteMethod();
// has the setter already been found by the wrapped BeanInfo?
if (writeMethod != null
&& writeMethod.getName().equals(method.getName())) {
// yes -> copy it, including corresponding getter method (if any -- may be null)
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod);
continue ALL_METHODS;
}
// has a getter corresponding to this setter already been found by the wrapped BeanInfo?
if (readMethod != null
&& readMethod.getName().equals(getterMethodNameFor(propertyName))
&& readMethod.getReturnType().equals(method.getParameterTypes()[0])) {
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, method);
continue ALL_METHODS;
}
}
// the setter method was not found by the wrapped BeanInfo -> add a new PropertyDescriptor for it
// no corresponding getter was detected, so the 'read method' parameter is null.
this.addOrUpdatePropertyDescriptor(null, propertyName, null, method);
continue ALL_METHODS;
}
// is the method an INDEXED setter? ignore return type in order to capture non-void signatures
if (method.getName().startsWith("set") && method.getParameterTypes().length == 2 && method.getParameterTypes()[0].equals(int.class)) {
String propertyName = propertyNameFor(method);
if(propertyName.length() == 0) {
continue ALL_METHODS;
}
DELEGATE_PD:
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
if (!(pd instanceof IndexedPropertyDescriptor)) {
continue DELEGATE_PD;
}
IndexedPropertyDescriptor ipd = (IndexedPropertyDescriptor) pd;
Method readMethod = ipd.getReadMethod();
Method writeMethod = ipd.getWriteMethod();
Method indexedReadMethod = ipd.getIndexedReadMethod();
Method indexedWriteMethod = ipd.getIndexedWriteMethod();
// has the setter already been found by the wrapped BeanInfo?
if (indexedWriteMethod != null
&& indexedWriteMethod.getName().equals(method.getName())) {
// yes -> copy it, including corresponding getter method (if any -- may be null)
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
continue ALL_METHODS;
}
// has a getter corresponding to this setter already been found by the wrapped BeanInfo?
if (indexedReadMethod != null
&& indexedReadMethod.getName().equals(getterMethodNameFor(propertyName))
&& indexedReadMethod.getReturnType().equals(method.getParameterTypes()[1])) {
this.addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, indexedReadMethod, method);
continue ALL_METHODS;
}
}
// the INDEXED setter method was not found by the wrapped BeanInfo -> add a new PropertyDescriptor
// for it. no corresponding INDEXED getter was detected, so the 'indexed read method' parameter is null.
this.addOrUpdatePropertyDescriptor(null, propertyName, null, null, null, method);
continue ALL_METHODS;
}
// the method is not a setter, but is it a getter?
for (PropertyDescriptor pd : delegate.getPropertyDescriptors()) {
// have we already copied this read method to a property descriptor locally?
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
if (method.equals(pd.getReadMethod())
&& existingPD.getName().equals(pd.getName())) {
if (existingPD.getReadMethod() == null) {
// no -> add it now
this.addOrUpdatePropertyDescriptor(pd, pd.getName(), method, pd.getWriteMethod());
}
// yes -> do not add a duplicate
continue ALL_METHODS;
}
}
if (method == pd.getReadMethod()
|| (pd instanceof IndexedPropertyDescriptor && method == ((IndexedPropertyDescriptor) pd).getIndexedReadMethod())) {
// yes -> copy it, including corresponding setter method (if any -- may be null)
if (pd instanceof IndexedPropertyDescriptor) {
this.addOrUpdatePropertyDescriptor(pd, pd.getName(), pd.getReadMethod(), pd.getWriteMethod(), ((IndexedPropertyDescriptor)pd).getIndexedReadMethod(), ((IndexedPropertyDescriptor)pd).getIndexedWriteMethod());
} else {
this.addOrUpdatePropertyDescriptor(pd, pd.getName(), pd.getReadMethod(), pd.getWriteMethod());
}
continue ALL_METHODS;
}
}
}
}
private void addOrUpdatePropertyDescriptor(PropertyDescriptor pd, String propertyName, Method readMethod, Method writeMethod) throws IntrospectionException {
addOrUpdatePropertyDescriptor(pd, propertyName, readMethod, writeMethod, null, null);
}
private void addOrUpdatePropertyDescriptor(PropertyDescriptor pd, String propertyName, Method readMethod, Method writeMethod, Method indexedReadMethod, Method indexedWriteMethod) throws IntrospectionException {
Assert.notNull(propertyName, "propertyName may not be null");
propertyName = pd == null ? propertyName : pd.getName();
for (PropertyDescriptor existingPD : this.propertyDescriptors) {
if (existingPD.getName().equals(propertyName)) {
// is there already a descriptor that captures this read method or its corresponding write method?
if (existingPD.getReadMethod() != null) {
if (readMethod != null && existingPD.getReadMethod().getReturnType() != readMethod.getReturnType()
|| writeMethod != null && existingPD.getReadMethod().getReturnType() != writeMethod.getParameterTypes()[0]) {
// no -> add a new descriptor for it below
break;
}
}
// update the existing descriptor's read method
if (readMethod != null) {
try {
existingPD.setReadMethod(readMethod);
} catch (IntrospectionException ex) {
// there is a conflicting setter method present -> null it out and try again
existingPD.setWriteMethod(null);
existingPD.setReadMethod(readMethod);
}
}
// is there already a descriptor that captures this write method or its corresponding read method?
if (existingPD.getWriteMethod() != null) {
if (readMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != readMethod.getReturnType()
|| writeMethod != null && existingPD.getWriteMethod().getParameterTypes()[0] != writeMethod.getParameterTypes()[0]) {
// no -> add a new descriptor for it below
break;
}
}
// update the existing descriptor's write method
if (writeMethod != null) {
existingPD.setWriteMethod(writeMethod);
}
// is this descriptor indexed?
if (existingPD instanceof IndexedPropertyDescriptor) {
IndexedPropertyDescriptor existingIPD = (IndexedPropertyDescriptor) existingPD;
// is there already a descriptor that captures this indexed read method or its corresponding indexed write method?
if (existingIPD.getIndexedReadMethod() != null) {
if (indexedReadMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedReadMethod.getReturnType()
|| indexedWriteMethod != null && existingIPD.getIndexedReadMethod().getReturnType() != indexedWriteMethod.getParameterTypes()[1]) {
// no -> add a new descriptor for it below
break;
}
}
// update the existing descriptor's indexed read method
try {
if (indexedReadMethod != null) {
existingIPD.setIndexedReadMethod(indexedReadMethod);
}
} catch (IntrospectionException ex) {
// there is a conflicting indexed setter method present -> null it out and try again
existingIPD.setIndexedWriteMethod(null);
existingIPD.setIndexedReadMethod(indexedReadMethod);
}
// is there already a descriptor that captures this indexed write method or its corresponding indexed read method?
if (existingIPD.getIndexedWriteMethod() != null) {
if (indexedReadMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedReadMethod.getReturnType()
|| indexedWriteMethod != null && existingIPD.getIndexedWriteMethod().getParameterTypes()[1] != indexedWriteMethod.getParameterTypes()[1]) {
// no -> add a new descriptor for it below
break;
}
}
// update the existing descriptor's indexed write method
if (indexedWriteMethod != null) {
existingIPD.setIndexedWriteMethod(indexedWriteMethod);
}
}
// the descriptor has been updated -> return immediately
return;
}
}
// we haven't yet seen read or write methods for this property -> add a new descriptor
if (pd == null) {
try {
if (indexedReadMethod == null && indexedWriteMethod == null) {
pd = new PropertyDescriptor(propertyName, readMethod, writeMethod);
}
else {
pd = new IndexedPropertyDescriptor(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
}
this.propertyDescriptors.add(pd);
} catch (IntrospectionException ex) {
logger.warn(format("Could not create new PropertyDescriptor for readMethod [%s] writeMethod [%s] " +
"indexedReadMethod [%s] indexedWriteMethod [%s] for property [%s]. Reason: %s",
readMethod, writeMethod, indexedReadMethod, indexedWriteMethod, propertyName, ex.getMessage()));
// suppress exception and attempt to continue
}
}
else {
pd.setReadMethod(readMethod);
try {
pd.setWriteMethod(writeMethod);
} catch (IntrospectionException ex) {
logger.warn(format("Could not add write method [%s] for property [%s]. Reason: %s",
writeMethod, propertyName, ex.getMessage()));
// fall through -> add property descriptor as best we can
}
this.propertyDescriptors.add(pd);
}
}
private String propertyNameFor(Method method) {
return Introspector.decapitalize(method.getName().substring(3,method.getName().length()));
}
private Object getterMethodNameFor(String name) {
return "get" + StringUtils.capitalize(name);
}
public BeanInfo[] getAdditionalBeanInfo() {
return delegate.getAdditionalBeanInfo();
}
public BeanDescriptor getBeanDescriptor() {
return delegate.getBeanDescriptor();
}
public int getDefaultEventIndex() {
return delegate.getDefaultEventIndex();
}
public int getDefaultPropertyIndex() {
return delegate.getDefaultPropertyIndex();
}
public EventSetDescriptor[] getEventSetDescriptors() {
return delegate.getEventSetDescriptors();
}
public Image getIcon(int arg0) {
return delegate.getIcon(arg0);
}
public MethodDescriptor[] getMethodDescriptors() {
return delegate.getMethodDescriptors();
}
/**
* Return the set of {@link PropertyDescriptor}s from the wrapped {@link BeanInfo}
* object as well as {@code PropertyDescriptor}s for each non-void returning setter
* method found during construction.
* @see #ExtendedBeanInfo(BeanInfo)
*/
public PropertyDescriptor[] getPropertyDescriptors() {
return this.propertyDescriptors.toArray(new PropertyDescriptor[this.propertyDescriptors.size()]);
}
/**
* Sorts PropertyDescriptor instances alphanumerically to emulate the behavior of {@link java.beans.BeanInfo#getPropertyDescriptors()}.
*
* @see ExtendedBeanInfo#propertyDescriptors
*/
static class PropertyDescriptorComparator implements Comparator<PropertyDescriptor> {
public int compare(PropertyDescriptor desc1, PropertyDescriptor desc2) {
String left = desc1.getName();
String right = desc2.getName();
for (int i = 0; i < left.length(); i++) {
if (right.length() == i) {
return 1;
}
int result = left.getBytes()[i] - right.getBytes()[i];
if (result != 0) {
return result;
}
}
return left.length() - right.length();
}
}
}

View File

@@ -0,0 +1,45 @@
/*
* Copyright 2002-2006 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;
/**
* Thrown on an unrecoverable problem encountered in the
* beans packages or sub-packages, e.g. bad class or field.
*
* @author Rod Johnson
*/
public class FatalBeanException extends BeansException {
/**
* Create a new FatalBeanException with the specified message.
* @param msg the detail message
*/
public FatalBeanException(String msg) {
super(msg);
}
/**
* Create a new FatalBeanException with the specified message
* and root cause.
* @param msg the detail message
* @param cause the root cause
*/
public FatalBeanException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,155 @@
/*
* 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;
import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Extension of the standard JavaBeans PropertyDescriptor class,
* overriding <code>getPropertyType()</code> such that a generically
* declared type will be resolved against the containing bean class.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
class GenericTypeAwarePropertyDescriptor extends PropertyDescriptor {
private final Class beanClass;
private final Method readMethod;
private final Method writeMethod;
private final Class propertyEditorClass;
private volatile Set<Method> ambiguousWriteMethods;
private Class propertyType;
private MethodParameter writeMethodParameter;
public GenericTypeAwarePropertyDescriptor(Class beanClass, String propertyName,
Method readMethod, Method writeMethod, Class propertyEditorClass)
throws IntrospectionException {
super(propertyName, null, null);
this.beanClass = beanClass;
this.propertyEditorClass = propertyEditorClass;
Method readMethodToUse = BridgeMethodResolver.findBridgedMethod(readMethod);
Method writeMethodToUse = BridgeMethodResolver.findBridgedMethod(writeMethod);
if (writeMethodToUse == null && readMethodToUse != null) {
// Fallback: Original JavaBeans introspection might not have found matching setter
// method due to lack of bridge method resolution, in case of the getter using a
// covariant return type whereas the setter is defined for the concrete property type.
writeMethodToUse = ClassUtils.getMethodIfAvailable(this.beanClass,
"set" + StringUtils.capitalize(getName()), readMethodToUse.getReturnType());
}
this.readMethod = readMethodToUse;
this.writeMethod = writeMethodToUse;
if (this.writeMethod != null && this.readMethod == null) {
// Write method not matched against read method: potentially ambiguous through
// several overloaded variants, in which case an arbitrary winner has been chosen
// by the JDK's JavaBeans Introspector...
Set<Method> ambiguousCandidates = new HashSet<Method>();
for (Method method : beanClass.getMethods()) {
if (method.getName().equals(writeMethodToUse.getName()) &&
!method.equals(writeMethodToUse) && !method.isBridge()) {
ambiguousCandidates.add(method);
}
}
if (!ambiguousCandidates.isEmpty()) {
this.ambiguousWriteMethods = ambiguousCandidates;
}
}
}
public Class<?> getBeanClass() {
return this.beanClass;
}
@Override
public Method getReadMethod() {
return this.readMethod;
}
@Override
public Method getWriteMethod() {
return this.writeMethod;
}
public Method getWriteMethodForActualAccess() {
Set<Method> ambiguousCandidates = this.ambiguousWriteMethods;
if (ambiguousCandidates != null) {
this.ambiguousWriteMethods = null;
LogFactory.getLog(GenericTypeAwarePropertyDescriptor.class).warn("Invalid JavaBean property '" +
getName() + "' being accessed! Ambiguous write methods found next to actually used [" +
this.writeMethod + "]: " + ambiguousCandidates);
}
return this.writeMethod;
}
@Override
public Class getPropertyEditorClass() {
return this.propertyEditorClass;
}
@Override
public synchronized Class getPropertyType() {
if (this.propertyType == null) {
if (this.readMethod != null) {
this.propertyType = GenericTypeResolver.resolveReturnType(this.readMethod, this.beanClass);
}
else {
MethodParameter writeMethodParam = getWriteMethodParameter();
if (writeMethodParam != null) {
this.propertyType = writeMethodParam.getParameterType();
}
else {
this.propertyType = super.getPropertyType();
}
}
}
return this.propertyType;
}
public synchronized MethodParameter getWriteMethodParameter() {
if (this.writeMethod == null) {
return null;
}
if (this.writeMethodParameter == null) {
this.writeMethodParameter = new MethodParameter(this.writeMethod, 0);
GenericTypeResolver.resolveParameterType(this.writeMethodParameter, this.beanClass);
}
return this.writeMethodParameter;
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2002-2006 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;
/**
* Exception thrown when referring to an invalid bean property.
* Carries the offending bean class and property name.
*
* @author Juergen Hoeller
* @since 1.0.2
*/
public class InvalidPropertyException extends FatalBeanException {
private Class beanClass;
private String propertyName;
/**
* Create a new InvalidPropertyException.
* @param beanClass the offending bean class
* @param propertyName the offending property
* @param msg the detail message
*/
public InvalidPropertyException(Class beanClass, String propertyName, String msg) {
this(beanClass, propertyName, msg, null);
}
/**
* Create a new InvalidPropertyException.
* @param beanClass the offending bean class
* @param propertyName the offending property
* @param msg the detail message
* @param cause the root cause
*/
public InvalidPropertyException(Class beanClass, String propertyName, String msg, Throwable cause) {
super("Invalid property '" + propertyName + "' of bean class [" + beanClass.getName() + "]: " + msg, cause);
this.beanClass = beanClass;
this.propertyName = propertyName;
}
/**
* Return the offending bean class.
*/
public Class getBeanClass() {
return beanClass;
}
/**
* Return the name of the offending property.
*/
public String getPropertyName() {
return propertyName;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2006 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;
/**
* Interface representing an object whose value set can be merged with
* that of a parent object.
*
* @author Rob Harrop
* @since 2.0
* @see org.springframework.beans.factory.support.ManagedSet
* @see org.springframework.beans.factory.support.ManagedList
* @see org.springframework.beans.factory.support.ManagedMap
* @see org.springframework.beans.factory.support.ManagedProperties
*/
public interface Mergeable {
/**
* Is merging enabled for this particular instance?
*/
boolean isMergeEnabled();
/**
* Merge the current value set with that of the supplied object.
* <p>The supplied object is considered the parent, and values in
* the callee's value set must override those of the supplied object.
* @param parent the object to merge with
* @return the result of the merge operation
* @throws IllegalArgumentException if the supplied parent is <code>null</code>
* @exception IllegalStateException if merging is not enabled for this instance
* (i.e. <code>mergeEnabled</code> equals <code>false</code>).
*/
Object merge(Object parent);
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2002-2006 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;
import java.beans.PropertyChangeEvent;
/**
* Thrown when a bean property getter or setter method throws an exception,
* analogous to an InvocationTargetException.
*
* @author Rod Johnson
*/
public class MethodInvocationException extends PropertyAccessException {
/**
* Error code that a method invocation error will be registered with.
*/
public static final String ERROR_CODE = "methodInvocation";
/**
* Create a new MethodInvocationException.
* @param propertyChangeEvent PropertyChangeEvent that resulted in an exception
* @param cause the Throwable raised by the invoked method
*/
public MethodInvocationException(PropertyChangeEvent propertyChangeEvent, Throwable cause) {
super(propertyChangeEvent, "Property '" + propertyChangeEvent.getPropertyName() + "' threw exception", cause);
}
public String getErrorCode() {
return ERROR_CODE;
}
}

View File

@@ -0,0 +1,349 @@
/*
* Copyright 2002-2010 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;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.util.StringUtils;
/**
* Default implementation of the {@link PropertyValues} interface.
* Allows simple manipulation of properties, and provides constructors
* to support deep copy and construction from a Map.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @since 13 May 2001
*/
public class MutablePropertyValues implements PropertyValues, Serializable {
private final List<PropertyValue> propertyValueList;
private Set<String> processedProperties;
private volatile boolean converted = false;
/**
* Creates a new empty MutablePropertyValues object.
* <p>Property values can be added with the <code>add</code> method.
* @see #add(String, Object)
*/
public MutablePropertyValues() {
this.propertyValueList = new ArrayList<PropertyValue>(0);
}
/**
* Deep copy constructor. Guarantees PropertyValue references
* are independent, although it can't deep copy objects currently
* referenced by individual PropertyValue objects.
* @param original the PropertyValues to copy
* @see #addPropertyValues(PropertyValues)
*/
public MutablePropertyValues(PropertyValues original) {
// We can optimize this because it's all new:
// There is no replacement of existing property values.
if (original != null) {
PropertyValue[] pvs = original.getPropertyValues();
this.propertyValueList = new ArrayList<PropertyValue>(pvs.length);
for (PropertyValue pv : pvs) {
this.propertyValueList.add(new PropertyValue(pv));
}
}
else {
this.propertyValueList = new ArrayList<PropertyValue>(0);
}
}
/**
* Construct a new MutablePropertyValues object from a Map.
* @param original Map with property values keyed by property name Strings
* @see #addPropertyValues(Map)
*/
public MutablePropertyValues(Map<?, ?> original) {
// We can optimize this because it's all new:
// There is no replacement of existing property values.
if (original != null) {
this.propertyValueList = new ArrayList<PropertyValue>(original.size());
for (Map.Entry entry : original.entrySet()) {
this.propertyValueList.add(new PropertyValue(entry.getKey().toString(), entry.getValue()));
}
}
else {
this.propertyValueList = new ArrayList<PropertyValue>(0);
}
}
/**
* Construct a new MutablePropertyValues object using the given List of
* PropertyValue objects as-is.
* <p>This is a constructor for advanced usage scenarios.
* It is not intended for typical programmatic use.
* @param propertyValueList List of PropertyValue objects
*/
public MutablePropertyValues(List<PropertyValue> propertyValueList) {
this.propertyValueList =
(propertyValueList != null ? propertyValueList : new ArrayList<PropertyValue>());
}
/**
* Return the underlying List of PropertyValue objects in its raw form.
* The returned List can be modified directly, although this is not recommended.
* <p>This is an accessor for optimized access to all PropertyValue objects.
* It is not intended for typical programmatic use.
*/
public List<PropertyValue> getPropertyValueList() {
return this.propertyValueList;
}
/**
* Return the number of PropertyValue entries in the list.
*/
public int size() {
return this.propertyValueList.size();
}
/**
* Copy all given PropertyValues into this object. Guarantees PropertyValue
* references are independent, although it can't deep copy objects currently
* referenced by individual PropertyValue objects.
* @param other the PropertyValues to copy
* @return this in order to allow for adding multiple property values in a chain
*/
public MutablePropertyValues addPropertyValues(PropertyValues other) {
if (other != null) {
PropertyValue[] pvs = other.getPropertyValues();
for (PropertyValue pv : pvs) {
addPropertyValue(new PropertyValue(pv));
}
}
return this;
}
/**
* Add all property values from the given Map.
* @param other Map with property values keyed by property name,
* which must be a String
* @return this in order to allow for adding multiple property values in a chain
*/
public MutablePropertyValues addPropertyValues(Map<?, ?> other) {
if (other != null) {
for (Map.Entry<?, ?> entry : other.entrySet()) {
addPropertyValue(new PropertyValue(entry.getKey().toString(), entry.getValue()));
}
}
return this;
}
/**
* Add a PropertyValue object, replacing any existing one for the
* corresponding property or getting merged with it (if applicable).
* @param pv PropertyValue object to add
* @return this in order to allow for adding multiple property values in a chain
*/
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
for (int i = 0; i < this.propertyValueList.size(); i++) {
PropertyValue currentPv = this.propertyValueList.get(i);
if (currentPv.getName().equals(pv.getName())) {
pv = mergeIfRequired(pv, currentPv);
setPropertyValueAt(pv, i);
return this;
}
}
this.propertyValueList.add(pv);
return this;
}
/**
* Overloaded version of <code>addPropertyValue</code> that takes
* a property name and a property value.
* <p>Note: As of Spring 3.0, we recommend using the more concise
* and chaining-capable variant {@link #add}.
* @param propertyName name of the property
* @param propertyValue value of the property
* @see #addPropertyValue(PropertyValue)
*/
public void addPropertyValue(String propertyName, Object propertyValue) {
addPropertyValue(new PropertyValue(propertyName, propertyValue));
}
/**
* Add a PropertyValue object, replacing any existing one for the
* corresponding property or getting merged with it (if applicable).
* @param propertyName name of the property
* @param propertyValue value of the property
* @return this in order to allow for adding multiple property values in a chain
*/
public MutablePropertyValues add(String propertyName, Object propertyValue) {
addPropertyValue(new PropertyValue(propertyName, propertyValue));
return this;
}
/**
* Modify a PropertyValue object held in this object.
* Indexed from 0.
*/
public void setPropertyValueAt(PropertyValue pv, int i) {
this.propertyValueList.set(i, pv);
}
/**
* Merges the value of the supplied 'new' {@link PropertyValue} with that of
* the current {@link PropertyValue} if merging is supported and enabled.
* @see Mergeable
*/
private PropertyValue mergeIfRequired(PropertyValue newPv, PropertyValue currentPv) {
Object value = newPv.getValue();
if (value instanceof Mergeable) {
Mergeable mergeable = (Mergeable) value;
if (mergeable.isMergeEnabled()) {
Object merged = mergeable.merge(currentPv.getValue());
return new PropertyValue(newPv.getName(), merged);
}
}
return newPv;
}
/**
* Remove the given PropertyValue, if contained.
* @param pv the PropertyValue to remove
*/
public void removePropertyValue(PropertyValue pv) {
this.propertyValueList.remove(pv);
}
/**
* Overloaded version of <code>removePropertyValue</code> that takes a property name.
* @param propertyName name of the property
* @see #removePropertyValue(PropertyValue)
*/
public void removePropertyValue(String propertyName) {
this.propertyValueList.remove(getPropertyValue(propertyName));
}
public PropertyValue[] getPropertyValues() {
return this.propertyValueList.toArray(new PropertyValue[this.propertyValueList.size()]);
}
public PropertyValue getPropertyValue(String propertyName) {
for (PropertyValue pv : this.propertyValueList) {
if (pv.getName().equals(propertyName)) {
return pv;
}
}
return null;
}
public PropertyValues changesSince(PropertyValues old) {
MutablePropertyValues changes = new MutablePropertyValues();
if (old == this) {
return changes;
}
// for each property value in the new set
for (PropertyValue newPv : this.propertyValueList) {
// if there wasn't an old one, add it
PropertyValue pvOld = old.getPropertyValue(newPv.getName());
if (pvOld == null) {
changes.addPropertyValue(newPv);
}
else if (!pvOld.equals(newPv)) {
// it's changed
changes.addPropertyValue(newPv);
}
}
return changes;
}
public boolean contains(String propertyName) {
return (getPropertyValue(propertyName) != null ||
(this.processedProperties != null && this.processedProperties.contains(propertyName)));
}
public boolean isEmpty() {
return this.propertyValueList.isEmpty();
}
/**
* Register the specified property as "processed" in the sense
* of some processor calling the corresponding setter method
* outside of the PropertyValue(s) mechanism.
* <p>This will lead to <code>true</code> being returned from
* a {@link #contains} call for the specified property.
* @param propertyName the name of the property.
*/
public void registerProcessedProperty(String propertyName) {
if (this.processedProperties == null) {
this.processedProperties = new HashSet<String>();
}
this.processedProperties.add(propertyName);
}
/**
* Mark this holder as containing converted values only
* (i.e. no runtime resolution needed anymore).
*/
public void setConverted() {
this.converted = true;
}
/**
* Return whether this holder contains converted values only (<code>true</code>),
* or whether the values still need to be converted (<code>false</code>).
*/
public boolean isConverted() {
return this.converted;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof MutablePropertyValues)) {
return false;
}
MutablePropertyValues that = (MutablePropertyValues) other;
return this.propertyValueList.equals(that.propertyValueList);
}
@Override
public int hashCode() {
return this.propertyValueList.hashCode();
}
@Override
public String toString() {
PropertyValue[] pvs = getPropertyValues();
StringBuilder sb = new StringBuilder("PropertyValues: length=").append(pvs.length);
if (pvs.length > 0) {
sb.append("; ").append(StringUtils.arrayToDelimitedString(pvs, "; "));
}
return sb.toString();
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2006 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;
/**
* Exception thrown on an attempt to get the value of a property
* that isn't readable, because there's no getter method.
*
* @author Juergen Hoeller
* @since 1.0.2
*/
public class NotReadablePropertyException extends InvalidPropertyException {
/**
* Create a new NotReadablePropertyException.
* @param beanClass the offending bean class
* @param propertyName the offending property
*/
public NotReadablePropertyException(Class beanClass, String propertyName) {
super(beanClass, propertyName,
"Bean property '" + propertyName + "' is not readable or has an invalid getter method: " +
"Does the return type of the getter match the parameter type of the setter?");
}
/**
* Create a new NotReadablePropertyException.
* @param beanClass the offending bean class
* @param propertyName the offending property
* @param msg the detail message
*/
public NotReadablePropertyException(Class beanClass, String propertyName, String msg) {
super(beanClass, propertyName, msg);
}
}

View File

@@ -0,0 +1,86 @@
/*
* 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;
/**
* Exception thrown on an attempt to set the value of a property that
* is not writable (typically because there is no setter method).
*
* @author Rod Johnson
* @author Alef Arendsen
* @author Arjen Poutsma
*/
public class NotWritablePropertyException extends InvalidPropertyException {
private String[] possibleMatches = null;
/**
* Create a new NotWritablePropertyException.
* @param beanClass the offending bean class
* @param propertyName the offending property name
*/
public NotWritablePropertyException(Class beanClass, String propertyName) {
super(beanClass, propertyName,
"Bean property '" + propertyName + "' is not writable or has an invalid setter method: " +
"Does the return type of the getter match the parameter type of the setter?");
}
/**
* Create a new NotWritablePropertyException.
* @param beanClass the offending bean class
* @param propertyName the offending property name
* @param msg the detail message
*/
public NotWritablePropertyException(Class beanClass, String propertyName, String msg) {
super(beanClass, propertyName, msg);
}
/**
* Create a new NotWritablePropertyException.
* @param beanClass the offending bean class
* @param propertyName the offending property name
* @param msg the detail message
* @param cause the root cause
*/
public NotWritablePropertyException(Class beanClass, String propertyName, String msg, Throwable cause) {
super(beanClass, propertyName, msg, cause);
}
/**
* Create a new NotWritablePropertyException.
* @param beanClass the offending bean class
* @param propertyName the offending property name
* @param msg the detail message
* @param possibleMatches suggestions for actual bean property names
* that closely match the invalid property name
*/
public NotWritablePropertyException(Class beanClass, String propertyName, String msg, String[] possibleMatches) {
super(beanClass, propertyName, msg);
this.possibleMatches = possibleMatches;
}
/**
* Return suggestions for actual bean property names that closely match
* the invalid property name, if any.
*/
public String[] getPossibleMatches() {
return this.possibleMatches;
}
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2006 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;
/**
* Exception thrown when navigation of a valid nested property
* path encounters a NullPointerException.
*
* <p>For example, navigating "spouse.age" could fail because the
* spouse property of the target object has a null value.
*
* @author Rod Johnson
*/
public class NullValueInNestedPathException extends InvalidPropertyException {
/**
* Create a new NullValueInNestedPathException.
* @param beanClass the offending bean class
* @param propertyName the offending property
*/
public NullValueInNestedPathException(Class beanClass, String propertyName) {
super(beanClass, propertyName, "Value of nested property '" + propertyName + "' is null");
}
/**
* Create a new NullValueInNestedPathException.
* @param beanClass the offending bean class
* @param propertyName the offending property
* @param msg the detail message
*/
public NullValueInNestedPathException(Class beanClass, String propertyName, String msg) {
super(beanClass, propertyName, msg);
}
}

View File

@@ -0,0 +1,79 @@
/*
* 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;
import java.beans.PropertyChangeEvent;
import org.springframework.core.ErrorCoded;
/**
* Superclass for exceptions related to a property access,
* such as type mismatch or invocation target exception.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public abstract class PropertyAccessException extends BeansException implements ErrorCoded {
private transient PropertyChangeEvent propertyChangeEvent;
/**
* Create a new PropertyAccessException.
* @param propertyChangeEvent the PropertyChangeEvent that resulted in the problem
* @param msg the detail message
* @param cause the root cause
*/
public PropertyAccessException(PropertyChangeEvent propertyChangeEvent, String msg, Throwable cause) {
super(msg, cause);
this.propertyChangeEvent = propertyChangeEvent;
}
/**
* Create a new PropertyAccessException without PropertyChangeEvent.
* @param msg the detail message
* @param cause the root cause
*/
public PropertyAccessException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Return the PropertyChangeEvent that resulted in the problem.
* <p>May be <code>null</code>; only available if an actual bean property
* was affected.
*/
public PropertyChangeEvent getPropertyChangeEvent() {
return this.propertyChangeEvent;
}
/**
* Return the name of the affected property, if available.
*/
public String getPropertyName() {
return (this.propertyChangeEvent != null ? this.propertyChangeEvent.getPropertyName() : null);
}
/**
* Return the affected value that was about to be set, if any.
*/
public Object getValue() {
return (this.propertyChangeEvent != null ? this.propertyChangeEvent.getNewValue() : null);
}
}

View File

@@ -0,0 +1,218 @@
/*
* 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;
import java.util.Map;
import org.springframework.core.convert.TypeDescriptor;
/**
* Common interface for classes that can access named properties
* (such as bean properties of an object or fields in an object)
* Serves as base interface for {@link BeanWrapper}.
*
* @author Juergen Hoeller
* @since 1.1
* @see BeanWrapper
* @see PropertyAccessorFactory#forBeanPropertyAccess
* @see PropertyAccessorFactory#forDirectFieldAccess
*/
public interface PropertyAccessor {
/**
* Path separator for nested properties.
* Follows normal Java conventions: getFoo().getBar() would be "foo.bar".
*/
String NESTED_PROPERTY_SEPARATOR = ".";
char NESTED_PROPERTY_SEPARATOR_CHAR = '.';
/**
* Marker that indicates the start of a property key for an
* indexed or mapped property like "person.addresses[0]".
*/
String PROPERTY_KEY_PREFIX = "[";
char PROPERTY_KEY_PREFIX_CHAR = '[';
/**
* Marker that indicates the end of a property key for an
* indexed or mapped property like "person.addresses[0]".
*/
String PROPERTY_KEY_SUFFIX = "]";
char PROPERTY_KEY_SUFFIX_CHAR = ']';
/**
* Determine whether the specified property is readable.
* <p>Returns <code>false</code> if the property doesn't exist.
* @param propertyName the property to check
* (may be a nested path and/or an indexed/mapped property)
* @return whether the property is readable
*/
boolean isReadableProperty(String propertyName);
/**
* Determine whether the specified property is writable.
* <p>Returns <code>false</code> if the property doesn't exist.
* @param propertyName the property to check
* (may be a nested path and/or an indexed/mapped property)
* @return whether the property is writable
*/
boolean isWritableProperty(String propertyName);
/**
* Determine the property type for the specified property,
* either checking the property descriptor or checking the value
* in case of an indexed or mapped element.
* @param propertyName the property to check
* (may be a nested path and/or an indexed/mapped property)
* @return the property type for the particular property,
* or <code>null</code> if not determinable
* @throws InvalidPropertyException if there is no such property or
* if the property isn't readable
* @throws PropertyAccessException if the property was valid but the
* accessor method failed
*/
Class getPropertyType(String propertyName) throws BeansException;
/**
* Return a type descriptor for the specified property:
* preferably from the read method, falling back to the write method.
* @param propertyName the property to check
* (may be a nested path and/or an indexed/mapped property)
* @return the property type for the particular property,
* or <code>null</code> if not determinable
* @throws InvalidPropertyException if there is no such property or
* if the property isn't readable
*/
TypeDescriptor getPropertyTypeDescriptor(String propertyName) throws BeansException;
/**
* Get the current value of the specified property.
* @param propertyName the name of the property to get the value of
* (may be a nested path and/or an indexed/mapped property)
* @return the value of the property
* @throws InvalidPropertyException if there is no such property or
* if the property isn't readable
* @throws PropertyAccessException if the property was valid but the
* accessor method failed
*/
Object getPropertyValue(String propertyName) throws BeansException;
/**
* Set the specified value as current property value.
* @param propertyName the name of the property to set the value of
* (may be a nested path and/or an indexed/mapped property)
* @param value the new value
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyAccessException if the property was valid but the
* accessor method failed or a type mismatch occured
*/
void setPropertyValue(String propertyName, Object value) throws BeansException;
/**
* Set the specified value as current property value.
* @param pv an object containing the new property value
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyAccessException if the property was valid but the
* accessor method failed or a type mismatch occured
*/
void setPropertyValue(PropertyValue pv) throws BeansException;
/**
* Perform a batch update from a Map.
* <p>Bulk updates from PropertyValues are more powerful: This method is
* provided for convenience. Behavior will be identical to that of
* the {@link #setPropertyValues(PropertyValues)} method.
* @param map Map to take properties from. Contains property value objects,
* keyed by property name
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions
* occured for specific properties during the batch update. This exception bundles
* all individual PropertyAccessExceptions. All other properties will have been
* successfully updated.
*/
void setPropertyValues(Map<?, ?> map) throws BeansException;
/**
* The preferred way to perform a batch update.
* <p>Note that performing a batch update differs from performing a single update,
* in that an implementation of this class will continue to update properties
* if a <b>recoverable</b> error (such as a type mismatch, but <b>not</b> an
* invalid field name or the like) is encountered, throwing a
* {@link PropertyBatchUpdateException} containing all the individual errors.
* This exception can be examined later to see all binding errors.
* Properties that were successfully updated remain changed.
* <p>Does not allow unknown fields or invalid fields.
* @param pvs PropertyValues to set on the target object
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions
* occured for specific properties during the batch update. This exception bundles
* all individual PropertyAccessExceptions. All other properties will have been
* successfully updated.
* @see #setPropertyValues(PropertyValues, boolean, boolean)
*/
void setPropertyValues(PropertyValues pvs) throws BeansException;
/**
* Perform a batch update with more control over behavior.
* <p>Note that performing a batch update differs from performing a single update,
* in that an implementation of this class will continue to update properties
* if a <b>recoverable</b> error (such as a type mismatch, but <b>not</b> an
* invalid field name or the like) is encountered, throwing a
* {@link PropertyBatchUpdateException} containing all the individual errors.
* This exception can be examined later to see all binding errors.
* Properties that were successfully updated remain changed.
* @param pvs PropertyValues to set on the target object
* @param ignoreUnknown should we ignore unknown properties (not found in the bean)
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions
* occured for specific properties during the batch update. This exception bundles
* all individual PropertyAccessExceptions. All other properties will have been
* successfully updated.
* @see #setPropertyValues(PropertyValues, boolean, boolean)
*/
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown)
throws BeansException;
/**
* Perform a batch update with full control over behavior.
* <p>Note that performing a batch update differs from performing a single update,
* in that an implementation of this class will continue to update properties
* if a <b>recoverable</b> error (such as a type mismatch, but <b>not</b> an
* invalid field name or the like) is encountered, throwing a
* {@link PropertyBatchUpdateException} containing all the individual errors.
* This exception can be examined later to see all binding errors.
* Properties that were successfully updated remain changed.
* @param pvs PropertyValues to set on the target object
* @param ignoreUnknown should we ignore unknown properties (not found in the bean)
* @param ignoreInvalid should we ignore invalid properties (found but not accessible)
* @throws InvalidPropertyException if there is no such property or
* if the property isn't writable
* @throws PropertyBatchUpdateException if one or more PropertyAccessExceptions
* occured for specific properties during the batch update. This exception bundles
* all individual PropertyAccessExceptions. All other properties will have been
* successfully updated.
*/
void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean ignoreInvalid)
throws BeansException;
}

View File

@@ -0,0 +1,51 @@
/*
* 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;
/**
* Simple factory facade for obtaining {@link PropertyAccessor} instances,
* in particular for {@link BeanWrapper} instances. Conceals the actual
* target implementation classes and their extended public signature.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public abstract class PropertyAccessorFactory {
/**
* Obtain a BeanWrapper for the given target object,
* accessing properties in JavaBeans style.
* @param target the target object to wrap
* @return the property accessor
* @see BeanWrapperImpl
*/
public static BeanWrapper forBeanPropertyAccess(Object target) {
return new BeanWrapperImpl(target);
}
/**
* Obtain a PropertyAccessor for the given target object,
* accessing properties in direct field style.
* @param target the target object to wrap
* @return the property accessor
* @see DirectFieldAccessor
*/
public static ConfigurablePropertyAccessor forDirectFieldAccess(Object target) {
return new DirectFieldAccessor(target);
}
}

View File

@@ -0,0 +1,185 @@
/*
* Copyright 2002-2010 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;
/**
* Utility methods for classes that perform bean property access
* according to the {@link PropertyAccessor} interface.
*
* @author Juergen Hoeller
* @since 1.2.6
*/
public abstract class PropertyAccessorUtils {
/**
* Return the actual property name for the given property path.
* @param propertyPath the property path to determine the property name
* for (can include property keys, for example for specifying a map entry)
* @return the actual property name, without any key elements
*/
public static String getPropertyName(String propertyPath) {
int separatorIndex = (propertyPath.endsWith(PropertyAccessor.PROPERTY_KEY_SUFFIX) ?
propertyPath.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR) : -1);
return (separatorIndex != -1 ? propertyPath.substring(0, separatorIndex) : propertyPath);
}
/**
* Check whether the given property path indicates an indexed or nested property.
* @param propertyPath the property path to check
* @return whether the path indicates an indexed or nested property
*/
public static boolean isNestedOrIndexedProperty(String propertyPath) {
if (propertyPath == null) {
return false;
}
for (int i = 0; i < propertyPath.length(); i++) {
char ch = propertyPath.charAt(i);
if (ch == PropertyAccessor.NESTED_PROPERTY_SEPARATOR_CHAR ||
ch == PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR) {
return true;
}
}
return false;
}
/**
* Determine the first nested property separator in the
* given property path, ignoring dots in keys (like "map[my.key]").
* @param propertyPath the property path to check
* @return the index of the nested property separator, or -1 if none
*/
public static int getFirstNestedPropertySeparatorIndex(String propertyPath) {
return getNestedPropertySeparatorIndex(propertyPath, false);
}
/**
* Determine the first nested property separator in the
* given property path, ignoring dots in keys (like "map[my.key]").
* @param propertyPath the property path to check
* @return the index of the nested property separator, or -1 if none
*/
public static int getLastNestedPropertySeparatorIndex(String propertyPath) {
return getNestedPropertySeparatorIndex(propertyPath, true);
}
/**
* Determine the first (or last) nested property separator in the
* given property path, ignoring dots in keys (like "map[my.key]").
* @param propertyPath the property path to check
* @param last whether to return the last separator rather than the first
* @return the index of the nested property separator, or -1 if none
*/
private static int getNestedPropertySeparatorIndex(String propertyPath, boolean last) {
boolean inKey = false;
int length = propertyPath.length();
int i = (last ? length - 1 : 0);
while (last ? i >= 0 : i < length) {
switch (propertyPath.charAt(i)) {
case PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR:
case PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR:
inKey = !inKey;
break;
case PropertyAccessor.NESTED_PROPERTY_SEPARATOR_CHAR:
if (!inKey) {
return i;
}
}
if (last) {
i--;
}
else {
i++;
}
}
return -1;
}
/**
* Determine whether the given registered path matches the given property path,
* either indicating the property itself or an indexed element of the property.
* @param propertyPath the property path (typically without index)
* @param registeredPath the registered path (potentially with index)
* @return whether the paths match
*/
public static boolean matchesProperty(String registeredPath, String propertyPath) {
if (!registeredPath.startsWith(propertyPath)) {
return false;
}
if (registeredPath.length() == propertyPath.length()) {
return true;
}
if (registeredPath.charAt(propertyPath.length()) != PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR) {
return false;
}
return (registeredPath.indexOf(PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR, propertyPath.length() + 1) ==
registeredPath.length() - 1);
}
/**
* Determine the canonical name for the given property path.
* Removes surrounding quotes from map keys:<br>
* <code>map['key']</code> -> <code>map[key]</code><br>
* <code>map["key"]</code> -> <code>map[key]</code>
* @param propertyName the bean property path
* @return the canonical representation of the property path
*/
public static String canonicalPropertyName(String propertyName) {
if (propertyName == null) {
return "";
}
StringBuilder sb = new StringBuilder(propertyName);
int searchIndex = 0;
while (searchIndex != -1) {
int keyStart = sb.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX, searchIndex);
searchIndex = -1;
if (keyStart != -1) {
int keyEnd = sb.indexOf(
PropertyAccessor.PROPERTY_KEY_SUFFIX, keyStart + PropertyAccessor.PROPERTY_KEY_PREFIX.length());
if (keyEnd != -1) {
String key = sb.substring(keyStart + PropertyAccessor.PROPERTY_KEY_PREFIX.length(), keyEnd);
if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) {
sb.delete(keyStart + 1, keyStart + 2);
sb.delete(keyEnd - 2, keyEnd - 1);
keyEnd = keyEnd - 2;
}
searchIndex = keyEnd + PropertyAccessor.PROPERTY_KEY_SUFFIX.length();
}
}
}
return sb.toString();
}
/**
* Determine the canonical names for the given property paths.
* @param propertyNames the bean property paths (as array)
* @return the canonical representation of the property paths
* (as array of the same size)
* @see #canonicalPropertyName(String)
*/
public static String[] canonicalPropertyNames(String[] propertyNames) {
if (propertyNames == null) {
return null;
}
String[] result = new String[propertyNames.length];
for (int i = 0; i < propertyNames.length; i++) {
result[i] = canonicalPropertyName(propertyNames[i]);
}
return result;
}
}

View File

@@ -0,0 +1,147 @@
/*
* 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;
import java.io.PrintStream;
import java.io.PrintWriter;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Combined exception, composed of individual PropertyAccessException instances.
* An object of this class is created at the beginning of the binding
* process, and errors added to it as necessary.
*
* <p>The binding process continues when it encounters application-level
* PropertyAccessExceptions, applying those changes that can be applied
* and storing rejected changes in an object of this class.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 18 April 2001
*/
public class PropertyBatchUpdateException extends BeansException {
/** List of PropertyAccessException objects */
private PropertyAccessException[] propertyAccessExceptions;
/**
* Create a new PropertyBatchUpdateException.
* @param propertyAccessExceptions the List of PropertyAccessExceptions
*/
public PropertyBatchUpdateException(PropertyAccessException[] propertyAccessExceptions) {
super(null);
Assert.notEmpty(propertyAccessExceptions, "At least 1 PropertyAccessException required");
this.propertyAccessExceptions = propertyAccessExceptions;
}
/**
* If this returns 0, no errors were encountered during binding.
*/
public final int getExceptionCount() {
return this.propertyAccessExceptions.length;
}
/**
* Return an array of the propertyAccessExceptions stored in this object.
* <p>Will return the empty array (not <code>null</code>) if there were no errors.
*/
public final PropertyAccessException[] getPropertyAccessExceptions() {
return this.propertyAccessExceptions;
}
/**
* Return the exception for this field, or <code>null</code> if there isn't any.
*/
public PropertyAccessException getPropertyAccessException(String propertyName) {
for (PropertyAccessException pae : this.propertyAccessExceptions) {
if (ObjectUtils.nullSafeEquals(propertyName, pae.getPropertyName())) {
return pae;
}
}
return null;
}
@Override
public String getMessage() {
StringBuilder sb = new StringBuilder("Failed properties: ");
for (int i = 0; i < this.propertyAccessExceptions.length; i++) {
sb.append(this.propertyAccessExceptions[i].getMessage());
if (i < this.propertyAccessExceptions.length - 1) {
sb.append("; ");
}
}
return sb.toString();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getName()).append("; nested PropertyAccessExceptions (");
sb.append(getExceptionCount()).append(") are:");
for (int i = 0; i < this.propertyAccessExceptions.length; i++) {
sb.append('\n').append("PropertyAccessException ").append(i + 1).append(": ");
sb.append(this.propertyAccessExceptions[i]);
}
return sb.toString();
}
@Override
public void printStackTrace(PrintStream ps) {
synchronized (ps) {
ps.println(getClass().getName() + "; nested PropertyAccessException details (" +
getExceptionCount() + ") are:");
for (int i = 0; i < this.propertyAccessExceptions.length; i++) {
ps.println("PropertyAccessException " + (i + 1) + ":");
this.propertyAccessExceptions[i].printStackTrace(ps);
}
}
}
@Override
public void printStackTrace(PrintWriter pw) {
synchronized (pw) {
pw.println(getClass().getName() + "; nested PropertyAccessException details (" +
getExceptionCount() + ") are:");
for (int i = 0; i < this.propertyAccessExceptions.length; i++) {
pw.println("PropertyAccessException " + (i + 1) + ":");
this.propertyAccessExceptions[i].printStackTrace(pw);
}
}
}
@Override
public boolean contains(Class exType) {
if (exType == null) {
return false;
}
if (exType.isInstance(this)) {
return true;
}
for (PropertyAccessException pae : this.propertyAccessExceptions) {
if (pae.contains(exType)) {
return true;
}
}
return false;
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2002-2007 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;
/**
* Interface for strategies that register custom
* {@link java.beans.PropertyEditor property editors} with a
* {@link org.springframework.beans.PropertyEditorRegistry property editor registry}.
*
* <p>This is particularly useful when you need to use the same set of
* property editors in several different situations: write a corresponding
* registrar and reuse that in each case.
*
* @author Juergen Hoeller
* @since 1.2.6
* @see PropertyEditorRegistry
* @see java.beans.PropertyEditor
*/
public interface PropertyEditorRegistrar {
/**
* Register custom {@link java.beans.PropertyEditor PropertyEditors} with
* the given <code>PropertyEditorRegistry</code>.
* <p>The passed-in registry will usually be a {@link BeanWrapper} or a
* {@link org.springframework.validation.DataBinder DataBinder}.
* <p>It is expected that implementations will create brand new
* <code>PropertyEditors</code> instances for each invocation of this
* method (since <code>PropertyEditors</code> are not threadsafe).
* @param registry the <code>PropertyEditorRegistry</code> to register the
* custom <code>PropertyEditors</code> with
*/
void registerCustomEditors(PropertyEditorRegistry registry);
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2002-2007 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;
import java.beans.PropertyEditor;
/**
* Encapsulates methods for registering JavaBeans {@link PropertyEditor PropertyEditors}.
* This is the central interface that a {@link PropertyEditorRegistrar} operates on.
*
* <p>Extended by {@link BeanWrapper}; implemented by {@link BeanWrapperImpl}
* and {@link org.springframework.validation.DataBinder}.
*
* @author Juergen Hoeller
* @since 1.2.6
* @see java.beans.PropertyEditor
* @see PropertyEditorRegistrar
* @see BeanWrapper
* @see org.springframework.validation.DataBinder
*/
public interface PropertyEditorRegistry {
/**
* Register the given custom property editor for all properties of the given type.
* @param requiredType the type of the property
* @param propertyEditor the editor to register
*/
void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor);
/**
* Register the given custom property editor for the given type and
* property, or for all properties of the given type.
* <p>If the property path denotes an array or Collection property,
* the editor will get applied either to the array/Collection itself
* (the {@link PropertyEditor} has to create an array or Collection value) or
* to each element (the <code>PropertyEditor</code> has to create the element type),
* depending on the specified required type.
* <p>Note: Only one single registered custom editor per property path
* is supported. In the case of a Collection/array, do not register an editor
* for both the Collection/array and each element on the same property.
* <p>For example, if you wanted to register an editor for "items[n].quantity"
* (for all values n), you would use "items.quantity" as the value of the
* 'propertyPath' argument to this method.
* @param requiredType the type of the property. This may be <code>null</code>
* if a property is given but should be specified in any case, in particular in
* case of a Collection - making clear whether the editor is supposed to apply
* to the entire Collection itself or to each of its entries. So as a general rule:
* <b>Do not specify <code>null</code> here in case of a Collection/array!</b>
* @param propertyPath the path of the property (name or nested path), or
* <code>null</code> if registering an editor for all properties of the given type
* @param propertyEditor editor to register
*/
void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor);
/**
* Find a custom property editor for the given type and property.
* @param requiredType the type of the property (can be <code>null</code> if a property
* is given but should be specified in any case for consistency checking)
* @param propertyPath the path of the property (name or nested path), or
* <code>null</code> if looking for an editor for all properties of the given type
* @return the registered editor, or <code>null</code> if none
*/
PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath);
}

View File

@@ -0,0 +1,568 @@
/*
* Copyright 2002-2011 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;
import java.beans.PropertyEditor;
import java.io.File;
import java.io.InputStream;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Collection;
import java.util.Currency;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedMap;
import java.util.SortedSet;
import java.util.TimeZone;
import java.util.UUID;
import java.util.regex.Pattern;
import org.xml.sax.InputSource;
import org.springframework.beans.propertyeditors.ByteArrayPropertyEditor;
import org.springframework.beans.propertyeditors.CharArrayPropertyEditor;
import org.springframework.beans.propertyeditors.CharacterEditor;
import org.springframework.beans.propertyeditors.CharsetEditor;
import org.springframework.beans.propertyeditors.ClassArrayEditor;
import org.springframework.beans.propertyeditors.ClassEditor;
import org.springframework.beans.propertyeditors.CurrencyEditor;
import org.springframework.beans.propertyeditors.CustomBooleanEditor;
import org.springframework.beans.propertyeditors.CustomCollectionEditor;
import org.springframework.beans.propertyeditors.CustomMapEditor;
import org.springframework.beans.propertyeditors.CustomNumberEditor;
import org.springframework.beans.propertyeditors.FileEditor;
import org.springframework.beans.propertyeditors.InputSourceEditor;
import org.springframework.beans.propertyeditors.InputStreamEditor;
import org.springframework.beans.propertyeditors.LocaleEditor;
import org.springframework.beans.propertyeditors.PatternEditor;
import org.springframework.beans.propertyeditors.PropertiesEditor;
import org.springframework.beans.propertyeditors.StringArrayPropertyEditor;
import org.springframework.beans.propertyeditors.TimeZoneEditor;
import org.springframework.beans.propertyeditors.URIEditor;
import org.springframework.beans.propertyeditors.URLEditor;
import org.springframework.beans.propertyeditors.UUIDEditor;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.ResourceArrayPropertyEditor;
import org.springframework.util.ClassUtils;
/**
* Base implementation of the {@link PropertyEditorRegistry} interface.
* Provides management of default editors and custom editors.
* Mainly serves as base class for {@link BeanWrapperImpl}.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 1.2.6
* @see java.beans.PropertyEditorManager
* @see java.beans.PropertyEditorSupport#setAsText
* @see java.beans.PropertyEditorSupport#setValue
*/
public class PropertyEditorRegistrySupport implements PropertyEditorRegistry {
private ConversionService conversionService;
private boolean defaultEditorsActive = false;
private boolean configValueEditorsActive = false;
private Map<Class<?>, PropertyEditor> defaultEditors;
private Map<Class<?>, PropertyEditor> overriddenDefaultEditors;
private Map<Class<?>, PropertyEditor> customEditors;
private Map<String, CustomEditorHolder> customEditorsForPath;
private Set<PropertyEditor> sharedEditors;
private Map<Class<?>, PropertyEditor> customEditorCache;
/**
* Specify a Spring 3.0 ConversionService to use for converting
* property values, as an alternative to JavaBeans PropertyEditors.
*/
public void setConversionService(ConversionService conversionService) {
this.conversionService = conversionService;
}
/**
* Return the associated ConversionService, if any.
*/
public ConversionService getConversionService() {
return this.conversionService;
}
//---------------------------------------------------------------------
// Management of default editors
//---------------------------------------------------------------------
/**
* Activate the default editors for this registry instance,
* allowing for lazily registering default editors when needed.
*/
protected void registerDefaultEditors() {
this.defaultEditorsActive = true;
}
/**
* Activate config value editors which are only intended for configuration purposes,
* such as {@link org.springframework.beans.propertyeditors.StringArrayPropertyEditor}.
* <p>Those editors are not registered by default simply because they are in
* general inappropriate for data binding purposes. Of course, you may register
* them individually in any case, through {@link #registerCustomEditor}.
*/
public void useConfigValueEditors() {
this.configValueEditorsActive = true;
}
/**
* Override the default editor for the specified type with the given property editor.
* <p>Note that this is different from registering a custom editor in that the editor
* semantically still is a default editor. A ConversionService will override such a
* default editor, whereas custom editors usually override the ConversionService.
* @param requiredType the type of the property
* @param propertyEditor the editor to register
* @see #registerCustomEditor(Class, PropertyEditor)
*/
public void overrideDefaultEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
if (this.overriddenDefaultEditors == null) {
this.overriddenDefaultEditors = new HashMap<Class<?>, PropertyEditor>();
}
this.overriddenDefaultEditors.put(requiredType, propertyEditor);
}
/**
* Retrieve the default editor for the given property type, if any.
* <p>Lazily registers the default editors, if they are active.
* @param requiredType type of the property
* @return the default editor, or <code>null</code> if none found
* @see #registerDefaultEditors
*/
public PropertyEditor getDefaultEditor(Class<?> requiredType) {
if (!this.defaultEditorsActive) {
return null;
}
if (this.overriddenDefaultEditors != null) {
PropertyEditor editor = this.overriddenDefaultEditors.get(requiredType);
if (editor != null) {
return editor;
}
}
if (this.defaultEditors == null) {
createDefaultEditors();
}
return this.defaultEditors.get(requiredType);
}
/**
* Actually register the default editors for this registry instance.
*/
private void createDefaultEditors() {
this.defaultEditors = new HashMap<Class<?>, PropertyEditor>(64);
// Simple editors, without parameterization capabilities.
// The JDK does not contain a default editor for any of these target types.
this.defaultEditors.put(Charset.class, new CharsetEditor());
this.defaultEditors.put(Class.class, new ClassEditor());
this.defaultEditors.put(Class[].class, new ClassArrayEditor());
this.defaultEditors.put(Currency.class, new CurrencyEditor());
this.defaultEditors.put(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
this.defaultEditors.put(InputSource.class, new InputSourceEditor());
this.defaultEditors.put(Locale.class, new LocaleEditor());
this.defaultEditors.put(Pattern.class, new PatternEditor());
this.defaultEditors.put(Properties.class, new PropertiesEditor());
this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
this.defaultEditors.put(URI.class, new URIEditor());
this.defaultEditors.put(URL.class, new URLEditor());
this.defaultEditors.put(UUID.class, new UUIDEditor());
// Default instances of collection editors.
// Can be overridden by registering custom instances of those as custom editors.
this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
// Default editors for primitive arrays.
this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
// The JDK does not contain a default editor for char!
this.defaultEditors.put(char.class, new CharacterEditor(false));
this.defaultEditors.put(Character.class, new CharacterEditor(true));
// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
// The JDK does not contain default editors for number wrapper types!
// Override JDK primitive number editors with our own CustomNumberEditor.
this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
// Only register config value editors if explicitly requested.
if (this.configValueEditorsActive) {
StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
this.defaultEditors.put(String[].class, sae);
this.defaultEditors.put(short[].class, sae);
this.defaultEditors.put(int[].class, sae);
this.defaultEditors.put(long[].class, sae);
}
}
/**
* Copy the default editors registered in this instance to the given target registry.
* @param target the target registry to copy to
*/
protected void copyDefaultEditorsTo(PropertyEditorRegistrySupport target) {
target.defaultEditorsActive = this.defaultEditorsActive;
target.configValueEditorsActive = this.configValueEditorsActive;
target.defaultEditors = this.defaultEditors;
target.overriddenDefaultEditors = this.overriddenDefaultEditors;
}
//---------------------------------------------------------------------
// Management of custom editors
//---------------------------------------------------------------------
public void registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
registerCustomEditor(requiredType, null, propertyEditor);
}
public void registerCustomEditor(Class<?> requiredType, String propertyPath, PropertyEditor propertyEditor) {
if (requiredType == null && propertyPath == null) {
throw new IllegalArgumentException("Either requiredType or propertyPath is required");
}
if (propertyPath != null) {
if (this.customEditorsForPath == null) {
this.customEditorsForPath = new LinkedHashMap<String, CustomEditorHolder>(16);
}
this.customEditorsForPath.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType));
}
else {
if (this.customEditors == null) {
this.customEditors = new LinkedHashMap<Class<?>, PropertyEditor>(16);
}
this.customEditors.put(requiredType, propertyEditor);
this.customEditorCache = null;
}
}
/**
* Register the given custom property editor for all properties
* of the given type, indicating that the given instance is a
* shared editor that might be used concurrently.
* @param requiredType the type of the property
* @param propertyEditor the shared editor to register
* @deprecated as of Spring 3.0, in favor of PropertyEditorRegistrars or ConversionService usage
*/
@Deprecated
public void registerSharedEditor(Class<?> requiredType, PropertyEditor propertyEditor) {
registerCustomEditor(requiredType, null, propertyEditor);
if (this.sharedEditors == null) {
this.sharedEditors = new HashSet<PropertyEditor>();
}
this.sharedEditors.add(propertyEditor);
}
/**
* Check whether the given editor instance is a shared editor, that is,
* whether the given editor instance might be used concurrently.
* @param propertyEditor the editor instance to check
* @return whether the editor is a shared instance
*/
public boolean isSharedEditor(PropertyEditor propertyEditor) {
return (this.sharedEditors != null && this.sharedEditors.contains(propertyEditor));
}
public PropertyEditor findCustomEditor(Class<?> requiredType, String propertyPath) {
Class<?> requiredTypeToUse = requiredType;
if (propertyPath != null) {
if (this.customEditorsForPath != null) {
// Check property-specific editor first.
PropertyEditor editor = getCustomEditor(propertyPath, requiredType);
if (editor == null) {
List<String> strippedPaths = new LinkedList<String>();
addStrippedPropertyPaths(strippedPaths, "", propertyPath);
for (Iterator<String> it = strippedPaths.iterator(); it.hasNext() && editor == null;) {
String strippedPath = it.next();
editor = getCustomEditor(strippedPath, requiredType);
}
}
if (editor != null) {
return editor;
}
}
if (requiredType == null) {
requiredTypeToUse = getPropertyType(propertyPath);
}
}
// No property-specific editor -> check type-specific editor.
return getCustomEditor(requiredTypeToUse);
}
/**
* Determine whether this registry contains a custom editor
* for the specified array/collection element.
* @param elementType the target type of the element
* (can be <code>null</code> if not known)
* @param propertyPath the property path (typically of the array/collection;
* can be <code>null</code> if not known)
* @return whether a matching custom editor has been found
*/
public boolean hasCustomEditorForElement(Class<?> elementType, String propertyPath) {
if (propertyPath != null && this.customEditorsForPath != null) {
for (Map.Entry<String, CustomEditorHolder> entry : this.customEditorsForPath.entrySet()) {
if (PropertyAccessorUtils.matchesProperty(entry.getKey(), propertyPath)) {
if (entry.getValue().getPropertyEditor(elementType) != null) {
return true;
}
}
}
}
// No property-specific editor -> check type-specific editor.
return (elementType != null && this.customEditors != null && this.customEditors.containsKey(elementType));
}
/**
* Determine the property type for the given property path.
* <p>Called by {@link #findCustomEditor} if no required type has been specified,
* to be able to find a type-specific editor even if just given a property path.
* <p>The default implementation always returns <code>null</code>.
* BeanWrapperImpl overrides this with the standard <code>getPropertyType</code>
* method as defined by the BeanWrapper interface.
* @param propertyPath the property path to determine the type for
* @return the type of the property, or <code>null</code> if not determinable
* @see BeanWrapper#getPropertyType(String)
*/
protected Class<?> getPropertyType(String propertyPath) {
return null;
}
/**
* Get custom editor that has been registered for the given property.
* @param propertyName the property path to look for
* @param requiredType the type to look for
* @return the custom editor, or <code>null</code> if none specific for this property
*/
private PropertyEditor getCustomEditor(String propertyName, Class<?> requiredType) {
CustomEditorHolder holder = this.customEditorsForPath.get(propertyName);
return (holder != null ? holder.getPropertyEditor(requiredType) : null);
}
/**
* Get custom editor for the given type. If no direct match found,
* try custom editor for superclass (which will in any case be able
* to render a value as String via <code>getAsText</code>).
* @param requiredType the type to look for
* @return the custom editor, or <code>null</code> if none found for this type
* @see java.beans.PropertyEditor#getAsText()
*/
private PropertyEditor getCustomEditor(Class<?> requiredType) {
if (requiredType == null || this.customEditors == null) {
return null;
}
// Check directly registered editor for type.
PropertyEditor editor = this.customEditors.get(requiredType);
if (editor == null) {
// Check cached editor for type, registered for superclass or interface.
if (this.customEditorCache != null) {
editor = this.customEditorCache.get(requiredType);
}
if (editor == null) {
// Find editor for superclass or interface.
for (Iterator<Class<?>> it = this.customEditors.keySet().iterator(); it.hasNext() && editor == null;) {
Class<?> key = it.next();
if (key.isAssignableFrom(requiredType)) {
editor = this.customEditors.get(key);
// Cache editor for search type, to avoid the overhead
// of repeated assignable-from checks.
if (this.customEditorCache == null) {
this.customEditorCache = new HashMap<Class<?>, PropertyEditor>();
}
this.customEditorCache.put(requiredType, editor);
}
}
}
}
return editor;
}
/**
* Guess the property type of the specified property from the registered
* custom editors (provided that they were registered for a specific type).
* @param propertyName the name of the property
* @return the property type, or <code>null</code> if not determinable
*/
protected Class<?> guessPropertyTypeFromEditors(String propertyName) {
if (this.customEditorsForPath != null) {
CustomEditorHolder editorHolder = this.customEditorsForPath.get(propertyName);
if (editorHolder == null) {
List<String> strippedPaths = new LinkedList<String>();
addStrippedPropertyPaths(strippedPaths, "", propertyName);
for (Iterator<String> it = strippedPaths.iterator(); it.hasNext() && editorHolder == null;) {
String strippedName = it.next();
editorHolder = this.customEditorsForPath.get(strippedName);
}
}
if (editorHolder != null) {
return editorHolder.getRegisteredType();
}
}
return null;
}
/**
* Copy the custom editors registered in this instance to the given target registry.
* @param target the target registry to copy to
* @param nestedProperty the nested property path of the target registry, if any.
* If this is non-null, only editors registered for a path below this nested property
* will be copied. If this is null, all editors will be copied.
*/
protected void copyCustomEditorsTo(PropertyEditorRegistry target, String nestedProperty) {
String actualPropertyName =
(nestedProperty != null ? PropertyAccessorUtils.getPropertyName(nestedProperty) : null);
if (this.customEditors != null) {
for (Map.Entry<Class<?>, PropertyEditor> entry : this.customEditors.entrySet()) {
target.registerCustomEditor(entry.getKey(), entry.getValue());
}
}
if (this.customEditorsForPath != null) {
for (Map.Entry<String, CustomEditorHolder> entry : this.customEditorsForPath.entrySet()) {
String editorPath = entry.getKey();
CustomEditorHolder editorHolder = entry.getValue();
if (nestedProperty != null) {
int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(editorPath);
if (pos != -1) {
String editorNestedProperty = editorPath.substring(0, pos);
String editorNestedPath = editorPath.substring(pos + 1);
if (editorNestedProperty.equals(nestedProperty) || editorNestedProperty.equals(actualPropertyName)) {
target.registerCustomEditor(
editorHolder.getRegisteredType(), editorNestedPath, editorHolder.getPropertyEditor());
}
}
}
else {
target.registerCustomEditor(
editorHolder.getRegisteredType(), editorPath, editorHolder.getPropertyEditor());
}
}
}
}
/**
* Add property paths with all variations of stripped keys and/or indexes.
* Invokes itself recursively with nested paths.
* @param strippedPaths the result list to add to
* @param nestedPath the current nested path
* @param propertyPath the property path to check for keys/indexes to strip
*/
private void addStrippedPropertyPaths(List<String> strippedPaths, String nestedPath, String propertyPath) {
int startIndex = propertyPath.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR);
if (startIndex != -1) {
int endIndex = propertyPath.indexOf(PropertyAccessor.PROPERTY_KEY_SUFFIX_CHAR);
if (endIndex != -1) {
String prefix = propertyPath.substring(0, startIndex);
String key = propertyPath.substring(startIndex, endIndex + 1);
String suffix = propertyPath.substring(endIndex + 1, propertyPath.length());
// Strip the first key.
strippedPaths.add(nestedPath + prefix + suffix);
// Search for further keys to strip, with the first key stripped.
addStrippedPropertyPaths(strippedPaths, nestedPath + prefix, suffix);
// Search for further keys to strip, with the first key not stripped.
addStrippedPropertyPaths(strippedPaths, nestedPath + prefix + key, suffix);
}
}
}
/**
* Holder for a registered custom editor with property name.
* Keeps the PropertyEditor itself plus the type it was registered for.
*/
private static class CustomEditorHolder {
private final PropertyEditor propertyEditor;
private final Class<?> registeredType;
private CustomEditorHolder(PropertyEditor propertyEditor, Class<?> registeredType) {
this.propertyEditor = propertyEditor;
this.registeredType = registeredType;
}
private PropertyEditor getPropertyEditor() {
return this.propertyEditor;
}
private Class<?> getRegisteredType() {
return this.registeredType;
}
private PropertyEditor getPropertyEditor(Class<?> requiredType) {
// Special case: If no required type specified, which usually only happens for
// Collection elements, or required type is not assignable to registered type,
// which usually only happens for generic properties of type Object -
// then return PropertyEditor if not registered for Collection or array type.
// (If not registered for Collection or array, it is assumed to be intended
// for elements.)
if (this.registeredType == null ||
(requiredType != null &&
(ClassUtils.isAssignable(this.registeredType, requiredType) ||
ClassUtils.isAssignable(requiredType, this.registeredType))) ||
(requiredType == null &&
(!Collection.class.isAssignableFrom(this.registeredType) && !this.registeredType.isArray()))) {
return this.propertyEditor;
}
else {
return null;
}
}
}
}

View File

@@ -0,0 +1,186 @@
/*
* 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;
import java.beans.PropertyDescriptor;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Helper class for calculating bean property matches, according to.
* Used by BeanWrapperImpl to suggest alternatives for an invalid property name.
*
* @author Alef Arendsen
* @author Arjen Poutsma
* @author Juergen Hoeller
* @since 2.0
* @see #forProperty(String, Class)
*/
final class PropertyMatches {
//---------------------------------------------------------------------
// Static section
//---------------------------------------------------------------------
/** Default maximum property distance: 2 */
public static final int DEFAULT_MAX_DISTANCE = 2;
/**
* Create PropertyMatches for the given bean property.
* @param propertyName the name of the property to find possible matches for
* @param beanClass the bean class to search for matches
*/
public static PropertyMatches forProperty(String propertyName, Class beanClass) {
return forProperty(propertyName, beanClass, DEFAULT_MAX_DISTANCE);
}
/**
* Create PropertyMatches for the given bean property.
* @param propertyName the name of the property to find possible matches for
* @param beanClass the bean class to search for matches
* @param maxDistance the maximum property distance allowed for matches
*/
public static PropertyMatches forProperty(String propertyName, Class beanClass, int maxDistance) {
return new PropertyMatches(propertyName, beanClass, maxDistance);
}
//---------------------------------------------------------------------
// Instance section
//---------------------------------------------------------------------
private final String propertyName;
private String[] possibleMatches;
/**
* Create a new PropertyMatches instance for the given property.
*/
private PropertyMatches(String propertyName, Class beanClass, int maxDistance) {
this.propertyName = propertyName;
this.possibleMatches = calculateMatches(BeanUtils.getPropertyDescriptors(beanClass), maxDistance);
}
/**
* Return the calculated possible matches.
*/
public String[] getPossibleMatches() {
return possibleMatches;
}
/**
* Build an error message for the given invalid property name,
* indicating the possible property matches.
*/
public String buildErrorMessage() {
StringBuilder msg = new StringBuilder();
msg.append("Bean property '");
msg.append(this.propertyName);
msg.append("' is not writable or has an invalid setter method. ");
if (ObjectUtils.isEmpty(this.possibleMatches)) {
msg.append("Does the parameter type of the setter match the return type of the getter?");
}
else {
msg.append("Did you mean ");
for (int i = 0; i < this.possibleMatches.length; i++) {
msg.append('\'');
msg.append(this.possibleMatches[i]);
if (i < this.possibleMatches.length - 2) {
msg.append("', ");
}
else if (i == this.possibleMatches.length - 2){
msg.append("', or ");
}
}
msg.append("'?");
}
return msg.toString();
}
/**
* Generate possible property alternatives for the given property and
* class. Internally uses the <code>getStringDistance</code> method, which
* in turn uses the Levenshtein algorithm to determine the distance between
* two Strings.
* @param propertyDescriptors the JavaBeans property descriptors to search
* @param maxDistance the maximum distance to accept
*/
private String[] calculateMatches(PropertyDescriptor[] propertyDescriptors, int maxDistance) {
List<String> candidates = new ArrayList<String>();
for (PropertyDescriptor pd : propertyDescriptors) {
if (pd.getWriteMethod() != null) {
String possibleAlternative = pd.getName();
if (calculateStringDistance(this.propertyName, possibleAlternative) <= maxDistance) {
candidates.add(possibleAlternative);
}
}
}
Collections.sort(candidates);
return StringUtils.toStringArray(candidates);
}
/**
* Calculate the distance between the given two Strings
* according to the Levenshtein algorithm.
* @param s1 the first String
* @param s2 the second String
* @return the distance value
*/
private int calculateStringDistance(String s1, String s2) {
if (s1.length() == 0) {
return s2.length();
}
if (s2.length() == 0) {
return s1.length();
}
int d[][] = new int[s1.length() + 1][s2.length() + 1];
for (int i = 0; i <= s1.length(); i++) {
d[i][0] = i;
}
for (int j = 0; j <= s2.length(); j++) {
d[0][j] = j;
}
for (int i = 1; i <= s1.length(); i++) {
char s_i = s1.charAt(i - 1);
for (int j = 1; j <= s2.length(); j++) {
int cost;
char t_j = s2.charAt(j - 1);
if (s_i == t_j) {
cost = 0;
} else {
cost = 1;
}
d[i][j] = Math.min(Math.min(d[i - 1][j] + 1, d[i][j - 1] + 1),
d[i - 1][j - 1] + cost);
}
}
return d[s1.length()][s2.length()];
}
}

View File

@@ -0,0 +1,201 @@
/*
* 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;
import java.beans.PropertyDescriptor;
import java.io.Serializable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
/**
* Object to hold information and value for an individual bean property.
* Using an object here, rather than just storing all properties in
* a map keyed by property name, allows for more flexibility, and the
* ability to handle indexed properties etc in an optimized way.
*
* <p>Note that the value doesn't need to be the final required type:
* A {@link BeanWrapper} implementation should handle any necessary conversion,
* as this object doesn't know anything about the objects it will be applied to.
*
* @author Rod Johnson
* @author Rob Harrop
* @author Juergen Hoeller
* @since 13 May 2001
* @see PropertyValues
* @see BeanWrapper
*/
public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable {
private final String name;
private final Object value;
private Object source;
private boolean optional = false;
private boolean converted = false;
private Object convertedValue;
/** Package-visible field that indicates whether conversion is necessary */
volatile Boolean conversionNecessary;
/** Package-visible field for caching the resolved property path tokens */
volatile Object resolvedTokens;
/** Package-visible field for caching the resolved PropertyDescriptor */
volatile PropertyDescriptor resolvedDescriptor;
/**
* Create a new PropertyValue instance.
* @param name the name of the property (never <code>null</code>)
* @param value the value of the property (possibly before type conversion)
*/
public PropertyValue(String name, Object value) {
this.name = name;
this.value = value;
}
/**
* Copy constructor.
* @param original the PropertyValue to copy (never <code>null</code>)
*/
public PropertyValue(PropertyValue original) {
Assert.notNull(original, "Original must not be null");
this.name = original.getName();
this.value = original.getValue();
this.source = original.getSource();
this.optional = original.isOptional();
this.converted = original.converted;
this.convertedValue = original.convertedValue;
this.conversionNecessary = original.conversionNecessary;
this.resolvedTokens = original.resolvedTokens;
this.resolvedDescriptor = original.resolvedDescriptor;
copyAttributesFrom(original);
}
/**
* Constructor that exposes a new value for an original value holder.
* The original holder will be exposed as source of the new holder.
* @param original the PropertyValue to link to (never <code>null</code>)
* @param newValue the new value to apply
*/
public PropertyValue(PropertyValue original, Object newValue) {
Assert.notNull(original, "Original must not be null");
this.name = original.getName();
this.value = newValue;
this.source = original;
this.optional = original.isOptional();
this.conversionNecessary = original.conversionNecessary;
this.resolvedTokens = original.resolvedTokens;
this.resolvedDescriptor = original.resolvedDescriptor;
copyAttributesFrom(original);
}
/**
* Return the name of the property.
*/
public String getName() {
return this.name;
}
/**
* Return the value of the property.
* <p>Note that type conversion will <i>not</i> have occurred here.
* It is the responsibility of the BeanWrapper implementation to
* perform type conversion.
*/
public Object getValue() {
return this.value;
}
/**
* Return the original PropertyValue instance for this value holder.
* @return the original PropertyValue (either a source of this
* value holder or this value holder itself).
*/
public PropertyValue getOriginalPropertyValue() {
PropertyValue original = this;
while (original.source instanceof PropertyValue && original.source != original) {
original = (PropertyValue) original.source;
}
return original;
}
public void setOptional(boolean optional) {
this.optional = optional;
}
public boolean isOptional() {
return this.optional;
}
/**
* Return whether this holder contains a converted value already (<code>true</code>),
* or whether the value still needs to be converted (<code>false</code>).
*/
public synchronized boolean isConverted() {
return this.converted;
}
/**
* Set the converted value of the constructor argument,
* after processed type conversion.
*/
public synchronized void setConvertedValue(Object value) {
this.converted = true;
this.convertedValue = value;
}
/**
* Return the converted value of the constructor argument,
* after processed type conversion.
*/
public synchronized Object getConvertedValue() {
return this.convertedValue;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PropertyValue)) {
return false;
}
PropertyValue otherPv = (PropertyValue) other;
return (this.name.equals(otherPv.name) &&
ObjectUtils.nullSafeEquals(this.value, otherPv.value) &&
ObjectUtils.nullSafeEquals(this.source, otherPv.source));
}
@Override
public int hashCode() {
return this.name.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.value);
}
@Override
public String toString() {
return "bean property '" + this.name + "'";
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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;
/**
* Holder containing one or more {@link PropertyValue} objects,
* typically comprising one update for a specific target bean.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 13 May 2001
* @see PropertyValue
*/
public interface PropertyValues {
/**
* Return an array of the PropertyValue objects held in this object.
*/
PropertyValue[] getPropertyValues();
/**
* Return the property value with the given name, if any.
* @param propertyName the name to search for
* @return the property value, or <code>null</code>
*/
PropertyValue getPropertyValue(String propertyName);
/**
* Return the changes since the previous PropertyValues.
* Subclasses should also override <code>equals</code>.
* @param old old property values
* @return PropertyValues updated or new properties.
* Return empty PropertyValues if there are no changes.
* @see java.lang.Object#equals
*/
PropertyValues changesSince(PropertyValues old);
/**
* Is there a property value (or other processing entry) for this property?
* @param propertyName the name of the property we're interested in
* @return whether there is a property value for this property
*/
boolean contains(String propertyName);
/**
* Does this holder not contain any PropertyValue objects at all?
*/
boolean isEmpty();
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright 2002-2007 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;
import java.beans.PropertyEditorSupport;
import java.util.Properties;
import org.springframework.beans.propertyeditors.PropertiesEditor;
/**
* {@link java.beans.PropertyEditor Editor} for a {@link PropertyValues} object.
*
* <p>The required format is defined in the {@link java.util.Properties}
* documentation. Each property must be on a new line.
*
* <p>The present implementation relies on a
* {@link org.springframework.beans.propertyeditors.PropertiesEditor}
* underneath.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public class PropertyValuesEditor extends PropertyEditorSupport {
private final PropertiesEditor propertiesEditor = new PropertiesEditor();
@Override
public void setAsText(String text) throws IllegalArgumentException {
this.propertiesEditor.setAsText(text);
Properties props = (Properties) this.propertiesEditor.getValue();
setValue(new MutablePropertyValues(props));
}
}

View File

@@ -0,0 +1,65 @@
/*
* Copyright 2002-2010 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;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionException;
import org.springframework.core.convert.ConverterNotFoundException;
/**
* Simple implementation of the TypeConverter interface that does not operate
* on any specific target object. This is an alternative to using a full-blown
* BeanWrapperImpl instance for arbitrary type conversion needs.
*
* @author Juergen Hoeller
* @since 2.0
* @see BeanWrapperImpl
*/
public class SimpleTypeConverter extends PropertyEditorRegistrySupport implements TypeConverter {
private final TypeConverterDelegate typeConverterDelegate = new TypeConverterDelegate(this);
public SimpleTypeConverter() {
registerDefaultEditors();
}
public <T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException {
return convertIfNecessary(value, requiredType, null);
}
public <T> T convertIfNecessary(
Object value, Class<T> requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
catch (ConverterNotFoundException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (ConversionException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
catch (IllegalStateException ex) {
throw new ConversionNotSupportedException(value, requiredType, ex);
}
catch (IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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;
import org.springframework.core.MethodParameter;
/**
* Interface that defines type conversion methods. Typically (but not necessarily)
* implemented in conjunction with the PropertyEditorRegistry interface.
*
* @author Juergen Hoeller
* @since 2.0
* @see PropertyEditorRegistry
* @see SimpleTypeConverter
* @see BeanWrapperImpl
*/
public interface TypeConverter {
/**
* Convert the value to the required type (if necessary from a String).
* <p>Conversions from String to any type will typically use the <code>setAsText</code>
* method of the PropertyEditor class. Note that a PropertyEditor must be registered
* for the given class for this to work; this is a standard JavaBeans API.
* A number of PropertyEditors are automatically registered.
* @param value the value to convert
* @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element)
* @return the new value, possibly the result of type conversion
* @throws TypeMismatchException if type conversion failed
* @see java.beans.PropertyEditor#setAsText(String)
* @see java.beans.PropertyEditor#getValue()
*/
<T> T convertIfNecessary(Object value, Class<T> requiredType) throws TypeMismatchException;
/**
* Convert the value to the required type (if necessary from a String).
* <p>Conversions from String to any type will typically use the <code>setAsText</code>
* method of the PropertyEditor class. Note that a PropertyEditor must be registered
* for the given class for this to work; this is a standard JavaBeans API.
* A number of PropertyEditors are automatically registered.
* @param value the value to convert
* @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element)
* @param methodParam the method parameter that is the target of the conversion
* (for analysis of generic types; may be <code>null</code>)
* @return the new value, possibly the result of type conversion
* @throws TypeMismatchException if type conversion failed
* @see java.beans.PropertyEditor#setAsText(String)
* @see java.beans.PropertyEditor#getValue()
*/
<T> T convertIfNecessary(Object value, Class<T> requiredType, MethodParameter methodParam)
throws TypeMismatchException;
}

View File

@@ -0,0 +1,661 @@
/*
* Copyright 2002-2011 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;
import java.beans.PropertyEditor;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.CollectionFactory;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.ConversionFailedException;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;
import org.springframework.util.ClassUtils;
import org.springframework.util.StringUtils;
/**
* Internal helper class for converting property values to target types.
*
* <p>Works on a given {@link PropertyEditorRegistrySupport} instance.
* Used as a delegate by {@link BeanWrapperImpl} and {@link SimpleTypeConverter}.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 2.0
* @see BeanWrapperImpl
* @see SimpleTypeConverter
*/
class TypeConverterDelegate {
private static final Log logger = LogFactory.getLog(TypeConverterDelegate.class);
private final PropertyEditorRegistrySupport propertyEditorRegistry;
private final Object targetObject;
/**
* Create a new TypeConverterDelegate for the given editor registry.
* @param propertyEditorRegistry the editor registry to use
*/
public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistry) {
this(propertyEditorRegistry, null);
}
/**
* Create a new TypeConverterDelegate for the given editor registry and bean instance.
* @param propertyEditorRegistry the editor registry to use
* @param targetObject the target object to work on (as context that can be passed to editors)
*/
public TypeConverterDelegate(PropertyEditorRegistrySupport propertyEditorRegistry, Object targetObject) {
this.propertyEditorRegistry = propertyEditorRegistry;
this.targetObject = targetObject;
}
/**
* Convert the value to the specified required type.
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element)
* @param methodParam the method parameter that is the target of the conversion
* (may be <code>null</code>)
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public <T> T convertIfNecessary(Object newValue, Class<T> requiredType, MethodParameter methodParam)
throws IllegalArgumentException {
return convertIfNecessary(null, null, newValue, requiredType,
(methodParam != null ? new TypeDescriptor(methodParam) : TypeDescriptor.valueOf(requiredType)));
}
/**
* Convert the value to the required type for the specified property.
* @param propertyName name of the property
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element)
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public <T> T convertIfNecessary(
String propertyName, Object oldValue, Object newValue, Class<T> requiredType)
throws IllegalArgumentException {
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, TypeDescriptor.valueOf(requiredType));
}
/**
* Convert the value to the required type (if necessary from a String),
* for the specified property.
* @param propertyName name of the property
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element)
* @param typeDescriptor the descriptor for the target property or field
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
@SuppressWarnings("unchecked")
public <T> T convertIfNecessary(String propertyName, Object oldValue, Object newValue,
Class<T> requiredType, TypeDescriptor typeDescriptor) throws IllegalArgumentException {
Object convertedValue = newValue;
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
ConversionFailedException firstAttemptEx = null;
// No custom editor but custom ConversionService specified?
ConversionService conversionService = this.propertyEditorRegistry.getConversionService();
if (editor == null && conversionService != null && convertedValue != null && typeDescriptor != null) {
TypeDescriptor sourceTypeDesc = TypeDescriptor.forObject(newValue);
TypeDescriptor targetTypeDesc = typeDescriptor;
if (conversionService.canConvert(sourceTypeDesc, targetTypeDesc)) {
try {
return (T) conversionService.convert(convertedValue, sourceTypeDesc, targetTypeDesc);
}
catch (ConversionFailedException ex) {
// fallback to default conversion logic below
firstAttemptEx = ex;
}
}
}
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (requiredType != null && Collection.class.isAssignableFrom(requiredType) && convertedValue instanceof String) {
TypeDescriptor elementType = typeDescriptor.getElementTypeDescriptor();
if (elementType != null && Enum.class.isAssignableFrom(elementType.getType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
}
if (editor == null) {
editor = findDefaultEditor(requiredType, typeDescriptor);
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
if (convertedValue instanceof String && Enum.class.isAssignableFrom(requiredType.getComponentType())) {
convertedValue = StringUtils.commaDelimitedListToStringArray((String) convertedValue);
}
return (T) convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
else if (convertedValue instanceof Collection) {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection(
(Collection) convertedValue, propertyName, requiredType, typeDescriptor);
}
else if (convertedValue instanceof Map) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap(
(Map) convertedValue, propertyName, requiredType, typeDescriptor);
}
if (convertedValue.getClass().isArray() && Array.getLength(convertedValue) == 1) {
convertedValue = Array.get(convertedValue, 0);
}
if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return (T) convertedValue.toString();
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
if (!requiredType.isInterface() && !requiredType.isEnum()) {
try {
Constructor strCtor = requiredType.getConstructor(String.class);
return (T) BeanUtils.instantiateClass(strCtor, convertedValue);
}
catch (NoSuchMethodException ex) {
// proceed with field lookup
if (logger.isTraceEnabled()) {
logger.trace("No String constructor found on type [" + requiredType.getName() + "]", ex);
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Construction via String failed for type [" + requiredType.getName() + "]", ex);
}
}
}
String trimmedValue = ((String) convertedValue).trim();
if (requiredType.isEnum() && "".equals(trimmedValue)) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
convertedValue = attemptToConvertStringToEnum(requiredType, trimmedValue, convertedValue);
}
}
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
if (firstAttemptEx != null) {
throw firstAttemptEx;
}
// Definitely doesn't match: throw IllegalArgumentException/IllegalStateException
StringBuilder msg = new StringBuilder();
msg.append("Cannot convert value of type [").append(ClassUtils.getDescriptiveType(newValue));
msg.append("] to required type [").append(ClassUtils.getQualifiedName(requiredType)).append("]");
if (propertyName != null) {
msg.append(" for property '").append(propertyName).append("'");
}
if (editor != null) {
msg.append(": PropertyEditor [").append(editor.getClass().getName()).append(
"] returned inappropriate value of type [").append(
ClassUtils.getDescriptiveType(convertedValue)).append("]");
throw new IllegalArgumentException(msg.toString());
}
else {
msg.append(": no matching editors or conversion strategy found");
throw new IllegalStateException(msg.toString());
}
}
}
if (firstAttemptEx != null) {
if (editor == null && convertedValue == newValue) {
throw firstAttemptEx;
}
logger.debug("Original ConversionService attempt failed - ignored since " +
"PropertyEditor based conversion eventually succeeded", firstAttemptEx);
}
return (T) convertedValue;
}
private Object attemptToConvertStringToEnum(Class<?> requiredType, String trimmedValue, Object currentConvertedValue) {
Object convertedValue = currentConvertedValue;
if (Enum.class.equals(requiredType)) {
// target type is declared as raw enum, treat the trimmed value as <enum.fqn>.FIELD_NAME
int index = trimmedValue.lastIndexOf(".");
if (index > - 1) {
String enumType = trimmedValue.substring(0, index);
String fieldName = trimmedValue.substring(index + 1);
ClassLoader loader = this.targetObject.getClass().getClassLoader();
try {
Class<?> enumValueType = loader.loadClass(enumType);
Field enumField = enumValueType.getField(fieldName);
convertedValue = enumField.get(null);
}
catch (ClassNotFoundException ex) {
if(logger.isTraceEnabled()) {
logger.trace("Enum class [" + enumType + "] cannot be loaded from [" + loader + "]", ex);
}
}
catch (Throwable ex) {
if(logger.isTraceEnabled()) {
logger.trace("Field [" + fieldName + "] isn't an enum value for type [" + enumType + "]", ex);
}
}
}
}
if (convertedValue == currentConvertedValue) {
// Try field lookup as fallback: for JDK 1.5 enum or custom enum
// with values defined as static fields. Resulting value still needs
// to be checked, hence we don't return it right away.
try {
Field enumField = requiredType.getField(trimmedValue);
convertedValue = enumField.get(null);
}
catch (Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Field [" + convertedValue + "] isn't an enum value", ex);
}
}
}
return convertedValue;
}
/**
* Find a default editor for the given type.
* @param requiredType the type to find an editor for
* @param descriptor the JavaBeans descriptor for the property
* @return the corresponding editor, or <code>null</code> if none
*/
protected PropertyEditor findDefaultEditor(Class requiredType, TypeDescriptor typeDescriptor) {
PropertyEditor editor = null;
//if (typeDescriptor instanceof PropertyTypeDescriptor) {
//PropertyDescriptor pd = ((PropertyTypeDescriptor) typeDescriptor).getPropertyDescriptor();
//editor = pd.createPropertyEditor(this.targetObject);
//}
if (editor == null && requiredType != null) {
// No custom editor -> check BeanWrapperImpl's default editors.
editor = this.propertyEditorRegistry.getDefaultEditor(requiredType);
if (editor == null && !String.class.equals(requiredType)) {
// No BeanWrapper default editor -> check standard JavaBean editor.
editor = BeanUtils.findEditorByConvention(requiredType);
}
}
return editor;
}
/**
* Convert the value to the required type (if necessary from a String),
* using the given property editor.
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newValue the proposed new value
* @param requiredType the type we must convert to
* (or <code>null</code> if not known, for example in case of a collection element)
* @param editor the PropertyEditor to use
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
protected Object doConvertValue(Object oldValue, Object newValue, Class<?> requiredType, PropertyEditor editor) {
Object convertedValue = newValue;
boolean sharedEditor = false;
if (editor != null) {
sharedEditor = this.propertyEditorRegistry.isSharedEditor(editor);
}
if (editor != null && !(convertedValue instanceof String)) {
// Not a String -> use PropertyEditor's setValue.
// With standard PropertyEditors, this will return the very same object;
// we just want to allow special PropertyEditors to override setValue
// for type conversion from non-String values to the required type.
try {
Object newConvertedValue;
if (sharedEditor) {
// Synchronized access to shared editor instance.
synchronized (editor) {
editor.setValue(convertedValue);
newConvertedValue = editor.getValue();
}
}
else {
// Unsynchronized access to non-shared editor instance.
editor.setValue(convertedValue);
newConvertedValue = editor.getValue();
}
if (newConvertedValue != convertedValue) {
convertedValue = newConvertedValue;
// Reset PropertyEditor: It already did a proper conversion.
// Don't use it again for a setAsText call.
editor = null;
}
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
}
// Swallow and proceed.
}
}
Object returnValue = convertedValue;
if (requiredType != null && !requiredType.isArray() && convertedValue instanceof String[]) {
// Convert String array to a comma-separated String.
// Only applies if no PropertyEditor converted the String array before.
// The CSV String will be passed into a PropertyEditor's setAsText method, if any.
if (logger.isTraceEnabled()) {
logger.trace("Converting String array to comma-delimited String [" + convertedValue + "]");
}
convertedValue = StringUtils.arrayToCommaDelimitedString((String[]) convertedValue);
}
if (convertedValue instanceof String) {
if (editor != null) {
// Use PropertyEditor's setAsText in case of a String value.
if (logger.isTraceEnabled()) {
logger.trace("Converting String to [" + requiredType + "] using property editor [" + editor + "]");
}
String newTextValue = (String) convertedValue;
if (sharedEditor) {
// Synchronized access to shared editor instance.
synchronized (editor) {
return doConvertTextValue(oldValue, newTextValue, editor);
}
}
else {
// Unsynchronized access to non-shared editor instance.
return doConvertTextValue(oldValue, newTextValue, editor);
}
}
else if (String.class.equals(requiredType)) {
returnValue = convertedValue;
}
}
return returnValue;
}
/**
* Convert the given text value using the given property editor.
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newTextValue the proposed text value
* @param editor the PropertyEditor to use
* @return the converted value
*/
protected Object doConvertTextValue(Object oldValue, String newTextValue, PropertyEditor editor) {
try {
editor.setValue(oldValue);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("PropertyEditor [" + editor.getClass().getName() + "] does not support setValue call", ex);
}
// Swallow and proceed.
}
editor.setAsText(newTextValue);
return editor.getValue();
}
protected Object convertToTypedArray(Object input, String propertyName, Class<?> componentType) {
if (input instanceof Collection) {
// Convert Collection elements to array elements.
Collection coll = (Collection) input;
Object result = Array.newInstance(componentType, coll.size());
int i = 0;
for (Iterator it = coll.iterator(); it.hasNext(); i++) {
Object value = convertIfNecessary(
buildIndexedPropertyName(propertyName, i), null, it.next(), componentType);
Array.set(result, i, value);
}
return result;
}
else if (input.getClass().isArray()) {
// Convert array elements, if necessary.
if (componentType.equals(input.getClass().getComponentType()) &&
!this.propertyEditorRegistry.hasCustomEditorForElement(componentType, propertyName)) {
return input;
}
int arrayLength = Array.getLength(input);
Object result = Array.newInstance(componentType, arrayLength);
for (int i = 0; i < arrayLength; i++) {
Object value = convertIfNecessary(
buildIndexedPropertyName(propertyName, i), null, Array.get(input, i), componentType);
Array.set(result, i, value);
}
return result;
}
else {
// A plain value: convert it to an array with a single component.
Object result = Array.newInstance(componentType, 1);
Object value = convertIfNecessary(
buildIndexedPropertyName(propertyName, 0), null, input, componentType);
Array.set(result, 0, value);
return result;
}
}
@SuppressWarnings("unchecked")
protected Collection convertToTypedCollection(
Collection original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
if (!Collection.class.isAssignableFrom(requiredType)) {
return original;
}
boolean approximable = CollectionFactory.isApproximableCollectionType(requiredType);
if (!approximable && !canCreateCopy(requiredType)) {
if (logger.isDebugEnabled()) {
logger.debug("Custom Collection type [" + original.getClass().getName() +
"] does not allow for creating a copy - injecting original Collection as-is");
}
return original;
}
boolean originalAllowed = requiredType.isInstance(original);
typeDescriptor = typeDescriptor.narrow(original);
TypeDescriptor elementType = typeDescriptor.getElementTypeDescriptor();
if (elementType == null && originalAllowed &&
!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
return original;
}
Iterator it;
try {
it = original.iterator();
if (it == null) {
if (logger.isDebugEnabled()) {
logger.debug("Collection of type [" + original.getClass().getName() +
"] returned null Iterator - injecting original Collection as-is");
}
return original;
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot access Collection of type [" + original.getClass().getName() +
"] - injecting original Collection as-is: " + ex);
}
return original;
}
Collection convertedCopy;
try {
if (approximable) {
convertedCopy = CollectionFactory.createApproximateCollection(original, original.size());
}
else {
convertedCopy = (Collection) requiredType.newInstance();
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot create copy of Collection type [" + original.getClass().getName() +
"] - injecting original Collection as-is: " + ex);
}
return original;
}
int i = 0;
for (; it.hasNext(); i++) {
Object element = it.next();
String indexedPropertyName = buildIndexedPropertyName(propertyName, i);
Object convertedElement = convertIfNecessary(indexedPropertyName, null, element,
(elementType != null ? elementType.getType() : null) , elementType);
try {
convertedCopy.add(convertedElement);
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Collection type [" + original.getClass().getName() +
"] seems to be read-only - injecting original Collection as-is: " + ex);
}
return original;
}
originalAllowed = originalAllowed && (element == convertedElement);
}
return (originalAllowed ? original : convertedCopy);
}
@SuppressWarnings("unchecked")
protected Map convertToTypedMap(
Map original, String propertyName, Class requiredType, TypeDescriptor typeDescriptor) {
if (!Map.class.isAssignableFrom(requiredType)) {
return original;
}
boolean approximable = CollectionFactory.isApproximableMapType(requiredType);
if (!approximable && !canCreateCopy(requiredType)) {
if (logger.isDebugEnabled()) {
logger.debug("Custom Map type [" + original.getClass().getName() +
"] does not allow for creating a copy - injecting original Map as-is");
}
return original;
}
boolean originalAllowed = requiredType.isInstance(original);
typeDescriptor = typeDescriptor.narrow(original);
TypeDescriptor keyType = typeDescriptor.getMapKeyTypeDescriptor();
TypeDescriptor valueType = typeDescriptor.getMapValueTypeDescriptor();
if (keyType == null && valueType == null && originalAllowed &&
!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
return original;
}
Iterator it;
try {
it = original.entrySet().iterator();
if (it == null) {
if (logger.isDebugEnabled()) {
logger.debug("Map of type [" + original.getClass().getName() +
"] returned null Iterator - injecting original Map as-is");
}
return original;
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot access Map of type [" + original.getClass().getName() +
"] - injecting original Map as-is: " + ex);
}
return original;
}
Map convertedCopy;
try {
if (approximable) {
convertedCopy = CollectionFactory.createApproximateMap(original, original.size());
}
else {
convertedCopy = (Map) requiredType.newInstance();
}
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot create copy of Map type [" + original.getClass().getName() +
"] - injecting original Map as-is: " + ex);
}
return original;
}
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
Object value = entry.getValue();
String keyedPropertyName = buildKeyedPropertyName(propertyName, key);
Object convertedKey = convertIfNecessary(keyedPropertyName, null, key,
(keyType != null ? keyType.getType() : null), keyType);
Object convertedValue = convertIfNecessary(keyedPropertyName, null, value,
(valueType!= null ? valueType.getType() : null), valueType);
try {
convertedCopy.put(convertedKey, convertedValue);
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Map type [" + original.getClass().getName() +
"] seems to be read-only - injecting original Map as-is: " + ex);
}
return original;
}
originalAllowed = originalAllowed && (key == convertedKey) && (value == convertedValue);
}
return (originalAllowed ? original : convertedCopy);
}
private String buildIndexedPropertyName(String propertyName, int index) {
return (propertyName != null ?
propertyName + PropertyAccessor.PROPERTY_KEY_PREFIX + index + PropertyAccessor.PROPERTY_KEY_SUFFIX :
null);
}
private String buildKeyedPropertyName(String propertyName, Object key) {
return (propertyName != null ?
propertyName + PropertyAccessor.PROPERTY_KEY_PREFIX + key + PropertyAccessor.PROPERTY_KEY_SUFFIX :
null);
}
private boolean canCreateCopy(Class requiredType) {
return (!requiredType.isInterface() && !Modifier.isAbstract(requiredType.getModifiers()) &&
Modifier.isPublic(requiredType.getModifiers()) && ClassUtils.hasConstructor(requiredType));
}
}

View File

@@ -0,0 +1,113 @@
/*
* 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;
import java.beans.PropertyChangeEvent;
import org.springframework.util.ClassUtils;
/**
* Exception thrown on a type mismatch when trying to set a bean property.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public class TypeMismatchException extends PropertyAccessException {
/**
* Error code that a type mismatch error will be registered with.
*/
public static final String ERROR_CODE = "typeMismatch";
private transient Object value;
private Class requiredType;
/**
* Create a new TypeMismatchException.
* @param propertyChangeEvent the PropertyChangeEvent that resulted in the problem
* @param requiredType the required target type
*/
public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class requiredType) {
this(propertyChangeEvent, requiredType, null);
}
/**
* Create a new TypeMismatchException.
* @param propertyChangeEvent the PropertyChangeEvent that resulted in the problem
* @param requiredType the required target type (or <code>null</code> if not known)
* @param cause the root cause (may be <code>null</code>)
*/
public TypeMismatchException(PropertyChangeEvent propertyChangeEvent, Class requiredType, Throwable cause) {
super(propertyChangeEvent,
"Failed to convert property value of type '" +
ClassUtils.getDescriptiveType(propertyChangeEvent.getNewValue()) + "'" +
(requiredType != null ?
" to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : "") +
(propertyChangeEvent.getPropertyName() != null ?
" for property '" + propertyChangeEvent.getPropertyName() + "'" : ""),
cause);
this.value = propertyChangeEvent.getNewValue();
this.requiredType = requiredType;
}
/**
* Create a new TypeMismatchException without PropertyChangeEvent.
* @param value the offending value that couldn't be converted (may be <code>null</code>)
* @param requiredType the required target type (or <code>null</code> if not known)
*/
public TypeMismatchException(Object value, Class requiredType) {
this(value, requiredType, null);
}
/**
* Create a new TypeMismatchException without PropertyChangeEvent.
* @param value the offending value that couldn't be converted (may be <code>null</code>)
* @param requiredType the required target type (or <code>null</code> if not known)
* @param cause the root cause (may be <code>null</code>)
*/
public TypeMismatchException(Object value, Class requiredType, Throwable cause) {
super("Failed to convert value of type '" + ClassUtils.getDescriptiveType(value) + "'" +
(requiredType != null ? " to required type '" + ClassUtils.getQualifiedName(requiredType) + "'" : ""),
cause);
this.value = value;
this.requiredType = requiredType;
}
/**
* Return the offending value (may be <code>null</code>)
*/
@Override
public Object getValue() {
return this.value;
}
/**
* Return the required target type, if any.
*/
public Class getRequiredType() {
return this.requiredType;
}
public String getErrorCode() {
return ERROR_CODE;
}
}

View File

@@ -0,0 +1,56 @@
/*
* 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.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.PropertyAccessorFactory;
import org.springframework.util.ReflectionUtils;
/**
* General utility methods for working with annotations in JavaBeans style.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
*/
public abstract class AnnotationBeanUtils {
/**
* Copy the properties of the supplied {@link Annotation} to the supplied target bean.
* Any properties defined in <code>excludedProperties</code> will not be copied.
* @see org.springframework.beans.BeanWrapper
*/
public static void copyPropertiesToBean(Annotation ann, Object bean, String... excludedProperties) {
Set<String> excluded = new HashSet<String>(Arrays.asList(excludedProperties));
Method[] annotationProperties = ann.annotationType().getDeclaredMethods();
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(bean);
for (Method annotationProperty : annotationProperties) {
String propertyName = annotationProperty.getName();
if ((!excluded.contains(propertyName)) && bw.isWritableProperty(propertyName)) {
Object value = ReflectionUtils.invokeMethod(annotationProperty, ann);
bw.setPropertyValue(propertyName, value);
}
}
}
}

View File

@@ -0,0 +1,8 @@
/**
*
* Support package for beans-style handling of Java 5 annotations.
*
*/
package org.springframework.beans.annotation;

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2002-2011 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;
/**
* Marker superinterface indicating that a bean is eligible to be
* notified by the Spring container of a particular framework object
* through a callback-style method. Actual method signature is
* determined by individual subinterfaces, but should typically
* consist of just one void-returning method that accepts a single
* argument.
*
* <p>Note that merely implementing {@link Aware} provides no default
* functionality. Rather, processing must be done explicitly, for example
* in a {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}.
* Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
* and {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory}
* for examples of processing {@code *Aware} interface callbacks.
*
* @author Chris Beams
* @since 3.1
*/
public interface Aware {
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2002-2011 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;
/**
* Callback that allows a bean to be aware of the bean
* {@link ClassLoader class loader}; that is, the class loader used by the
* present bean factory to load bean classes.
*
* <p>This is mainly intended to be implemented by framework classes which
* have to pick up application classes by name despite themselves potentially
* being loaded from a shared class loader.
*
* <p>For a list of all bean lifecycle methods, see the
* {@link BeanFactory BeanFactory javadocs}.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 2.0
* @see BeanNameAware
* @see BeanFactoryAware
* @see InitializingBean
*/
public interface BeanClassLoaderAware extends Aware {
/**
* Callback that supplies the bean {@link ClassLoader class loader} to
* a bean instance.
* <p>Invoked <i>after</i> the population of normal bean properties but
* <i>before</i> an initialization callback such as
* {@link org.springframework.beans.factory.InitializingBean InitializingBean's}
* {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* method or a custom init-method.
* @param classLoader the owning class loader; may be <code>null</code> in
* which case a default <code>ClassLoader</code> must be used, for example
* the <code>ClassLoader</code> obtained via
* {@link org.springframework.util.ClassUtils#getDefaultClassLoader()}
*/
void setBeanClassLoader(ClassLoader classLoader);
}

View File

@@ -0,0 +1,202 @@
/*
* 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;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.LinkedList;
import java.util.List;
import org.springframework.beans.FatalBeanException;
import org.springframework.core.NestedRuntimeException;
/**
* Exception thrown when a BeanFactory encounters an error when
* attempting to create a bean from a bean definition.
*
* @author Juergen Hoeller
*/
public class BeanCreationException extends FatalBeanException {
private String beanName;
private String resourceDescription;
private List<Throwable> relatedCauses;
/**
* Create a new BeanCreationException.
* @param msg the detail message
*/
public BeanCreationException(String msg) {
super(msg);
}
/**
* Create a new BeanCreationException.
* @param msg the detail message
* @param cause the root cause
*/
public BeanCreationException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Create a new BeanCreationException.
* @param beanName the name of the bean requested
* @param msg the detail message
*/
public BeanCreationException(String beanName, String msg) {
super("Error creating bean with name '" + beanName + "': " + msg);
this.beanName = beanName;
}
/**
* Create a new BeanCreationException.
* @param beanName the name of the bean requested
* @param msg the detail message
* @param cause the root cause
*/
public BeanCreationException(String beanName, String msg, Throwable cause) {
this(beanName, msg);
initCause(cause);
}
/**
* Create a new BeanCreationException.
* @param resourceDescription description of the resource
* that the bean definition came from
* @param beanName the name of the bean requested
* @param msg the detail message
*/
public BeanCreationException(String resourceDescription, String beanName, String msg) {
super("Error creating bean with name '" + beanName + "'" +
(resourceDescription != null ? " defined in " + resourceDescription : "") + ": " + msg);
this.resourceDescription = resourceDescription;
this.beanName = beanName;
}
/**
* Create a new BeanCreationException.
* @param resourceDescription description of the resource
* that the bean definition came from
* @param beanName the name of the bean requested
* @param msg the detail message
* @param cause the root cause
*/
public BeanCreationException(String resourceDescription, String beanName, String msg, Throwable cause) {
this(resourceDescription, beanName, msg);
initCause(cause);
}
/**
* Return the name of the bean requested, if any.
*/
public String getBeanName() {
return this.beanName;
}
/**
* Return the description of the resource that the bean
* definition came from, if any.
*/
public String getResourceDescription() {
return this.resourceDescription;
}
/**
* Add a related cause to this bean creation exception,
* not being a direct cause of the failure but having occured
* earlier in the creation of the same bean instance.
* @param ex the related cause to add
*/
public void addRelatedCause(Throwable ex) {
if (this.relatedCauses == null) {
this.relatedCauses = new LinkedList<Throwable>();
}
this.relatedCauses.add(ex);
}
/**
* Return the related causes, if any.
* @return the array of related causes, or <code>null</code> if none
*/
public Throwable[] getRelatedCauses() {
if (this.relatedCauses == null) {
return null;
}
return this.relatedCauses.toArray(new Throwable[this.relatedCauses.size()]);
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder(super.toString());
if (this.relatedCauses != null) {
for (Throwable relatedCause : this.relatedCauses) {
sb.append("\nRelated cause: ");
sb.append(relatedCause);
}
}
return sb.toString();
}
@Override
public void printStackTrace(PrintStream ps) {
synchronized (ps) {
super.printStackTrace(ps);
if (this.relatedCauses != null) {
for (Throwable relatedCause : this.relatedCauses) {
ps.println("Related cause:");
relatedCause.printStackTrace(ps);
}
}
}
}
@Override
public void printStackTrace(PrintWriter pw) {
synchronized (pw) {
super.printStackTrace(pw);
if (this.relatedCauses != null) {
for (Throwable relatedCause : this.relatedCauses) {
pw.println("Related cause:");
relatedCause.printStackTrace(pw);
}
}
}
}
@Override
public boolean contains(Class exClass) {
if (super.contains(exClass)) {
return true;
}
if (this.relatedCauses != null) {
for (Throwable relatedCause : this.relatedCauses) {
if (relatedCause instanceof NestedRuntimeException &&
((NestedRuntimeException) relatedCause).contains(exClass)) {
return true;
}
}
}
return false;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2002-2006 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;
/**
* Exception thrown in case of a bean being requested despite
* bean creation currently not being allowed (for example, during
* the shutdown phase of a bean factory).
*
* @author Juergen Hoeller
* @since 2.0
*/
public class BeanCreationNotAllowedException extends BeanCreationException {
/**
* Create a new BeanCreationNotAllowedException.
* @param beanName the name of the bean requested
* @param msg the detail message
*/
public BeanCreationNotAllowedException(String beanName, String msg) {
super(beanName, msg);
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2002-2006 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;
/**
* Exception thrown in case of a reference to a bean that's currently in creation.
* Typically happens when constructor autowiring matches the currently constructed bean.
*
* @author Juergen Hoeller
* @since 1.1
*/
public class BeanCurrentlyInCreationException extends BeanCreationException {
/**
* Create a new BeanCurrentlyInCreationException,
* with a default error message that indicates a circular reference.
* @param beanName the name of the bean requested
*/
public BeanCurrentlyInCreationException(String beanName) {
super(beanName,
"Requested bean is currently in creation: Is there an unresolvable circular reference?");
}
/**
* Create a new BeanCurrentlyInCreationException.
* @param beanName the name of the bean requested
* @param msg the detail message
*/
public BeanCurrentlyInCreationException(String beanName, String msg) {
super(beanName, msg);
}
}

View File

@@ -0,0 +1,115 @@
/*
* Copyright 2002-2007 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;
import org.springframework.beans.FatalBeanException;
/**
* Exception thrown when a BeanFactory encounters an invalid bean definition:
* e.g. in case of incomplete or contradictory bean metadata.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
*/
public class BeanDefinitionStoreException extends FatalBeanException {
private String resourceDescription;
private String beanName;
/**
* Create a new BeanDefinitionStoreException.
* @param msg the detail message (used as exception message as-is)
*/
public BeanDefinitionStoreException(String msg) {
super(msg);
}
/**
* Create a new BeanDefinitionStoreException.
* @param msg the detail message (used as exception message as-is)
* @param cause the root cause (may be <code>null</code>)
*/
public BeanDefinitionStoreException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Create a new BeanDefinitionStoreException.
* @param resourceDescription description of the resource that the bean definition came from
* @param msg the detail message (used as exception message as-is)
*/
public BeanDefinitionStoreException(String resourceDescription, String msg) {
super(msg);
this.resourceDescription = resourceDescription;
}
/**
* Create a new BeanDefinitionStoreException.
* @param resourceDescription description of the resource that the bean definition came from
* @param msg the detail message (used as exception message as-is)
* @param cause the root cause (may be <code>null</code>)
*/
public BeanDefinitionStoreException(String resourceDescription, String msg, Throwable cause) {
super(msg, cause);
this.resourceDescription = resourceDescription;
}
/**
* Create a new BeanDefinitionStoreException.
* @param resourceDescription description of the resource that the bean definition came from
* @param beanName the name of the bean requested
* @param msg the detail message (appended to an introductory message that indicates
* the resource and the name of the bean)
*/
public BeanDefinitionStoreException(String resourceDescription, String beanName, String msg) {
this(resourceDescription, beanName, msg, null);
}
/**
* Create a new BeanDefinitionStoreException.
* @param resourceDescription description of the resource that the bean definition came from
* @param beanName the name of the bean requested
* @param msg the detail message (appended to an introductory message that indicates
* the resource and the name of the bean)
* @param cause the root cause (may be <code>null</code>)
*/
public BeanDefinitionStoreException(String resourceDescription, String beanName, String msg, Throwable cause) {
super("Invalid bean definition with name '" + beanName + "' defined in " + resourceDescription + ": " + msg, cause);
this.resourceDescription = resourceDescription;
this.beanName = beanName;
}
/**
* Return the description of the resource that the bean
* definition came from, if any.
*/
public String getResourceDescription() {
return this.resourceDescription;
}
/**
* Return the name of the bean requested, if any.
*/
public String getBeanName() {
return this.beanName;
}
}

View File

@@ -0,0 +1,47 @@
/*
* 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;
import org.springframework.beans.FatalBeanException;
/**
* Exception that indicates an expression evaluation attempt having failed.
*
* @author Juergen Hoeller
* @since 3.0
*/
public class BeanExpressionException extends FatalBeanException {
/**
* Create a new BeanExpressionException with the specified message.
* @param msg the detail message
*/
public BeanExpressionException(String msg) {
super(msg);
}
/**
* Create a new BeanExpressionException with the specified message
* and root cause.
* @param msg the detail message
* @param cause the root cause
*/
public BeanExpressionException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright 2002-2011 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;
import org.springframework.beans.BeansException;
/**
* The root interface for accessing a Spring bean container.
* This is the basic client view of a bean container;
* further interfaces such as {@link ListableBeanFactory} and
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}
* are available for specific purposes.
*
* <p>This interface is implemented by objects that hold a number of bean definitions,
* each uniquely identified by a String name. Depending on the bean definition,
* the factory will return either an independent instance of a contained object
* (the Prototype design pattern), or a single shared instance (a superior
* alternative to the Singleton design pattern, in which the instance is a
* singleton in the scope of the factory). Which type of instance will be returned
* depends on the bean factory configuration: the API is the same. Since Spring
* 2.0, further scopes are available depending on the concrete application
* context (e.g. "request" and "session" scopes in a web environment).
*
* <p>The point of this approach is that the BeanFactory is a central registry
* of application components, and centralizes configuration of application
* components (no more do individual objects need to read properties files,
* for example). See chapters 4 and 11 of "Expert One-on-One J2EE Design and
* Development" for a discussion of the benefits of this approach.
*
* <p>Note that it is generally better to rely on Dependency Injection
* ("push" configuration) to configure application objects through setters
* or constructors, rather than use any form of "pull" configuration like a
* BeanFactory lookup. Spring's Dependency Injection functionality is
* implemented using this BeanFactory interface and its subinterfaces.
*
* <p>Normally a BeanFactory will load bean definitions stored in a configuration
* source (such as an XML document), and use the <code>org.springframework.beans</code>
* package to configure the beans. However, an implementation could simply return
* Java objects it creates as necessary directly in Java code. There are no
* constraints on how the definitions could be stored: LDAP, RDBMS, XML,
* properties file, etc. Implementations are encouraged to support references
* amongst beans (Dependency Injection).
*
* <p>In contrast to the methods in {@link ListableBeanFactory}, all of the
* operations in this interface will also check parent factories if this is a
* {@link HierarchicalBeanFactory}. If a bean is not found in this factory instance,
* the immediate parent factory will be asked. Beans in this factory instance
* are supposed to override beans of the same name in any parent factory.
*
* <p>Bean factory implementations should support the standard bean lifecycle interfaces
* as far as possible. The full set of initialization methods and their standard order is:<br>
* 1. BeanNameAware's <code>setBeanName</code><br>
* 2. BeanClassLoaderAware's <code>setBeanClassLoader</code><br>
* 3. BeanFactoryAware's <code>setBeanFactory</code><br>
* 4. ResourceLoaderAware's <code>setResourceLoader</code>
* (only applicable when running in an application context)<br>
* 5. ApplicationEventPublisherAware's <code>setApplicationEventPublisher</code>
* (only applicable when running in an application context)<br>
* 6. MessageSourceAware's <code>setMessageSource</code>
* (only applicable when running in an application context)<br>
* 7. ApplicationContextAware's <code>setApplicationContext</code>
* (only applicable when running in an application context)<br>
* 8. ServletContextAware's <code>setServletContext</code>
* (only applicable when running in a web application context)<br>
* 9. <code>postProcessBeforeInitialization</code> methods of BeanPostProcessors<br>
* 10. InitializingBean's <code>afterPropertiesSet</code><br>
* 11. a custom init-method definition<br>
* 12. <code>postProcessAfterInitialization</code> methods of BeanPostProcessors
*
* <p>On shutdown of a bean factory, the following lifecycle methods apply:<br>
* 1. DisposableBean's <code>destroy</code><br>
* 2. a custom destroy-method definition
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @since 13 April 2001
* @see BeanNameAware#setBeanName
* @see BeanClassLoaderAware#setBeanClassLoader
* @see BeanFactoryAware#setBeanFactory
* @see org.springframework.context.ResourceLoaderAware#setResourceLoader
* @see org.springframework.context.ApplicationEventPublisherAware#setApplicationEventPublisher
* @see org.springframework.context.MessageSourceAware#setMessageSource
* @see org.springframework.context.ApplicationContextAware#setApplicationContext
* @see org.springframework.web.context.ServletContextAware#setServletContext
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessBeforeInitialization
* @see InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
* @see org.springframework.beans.factory.config.BeanPostProcessor#postProcessAfterInitialization
* @see DisposableBean#destroy
* @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName
*/
public interface BeanFactory {
/**
* Used to dereference a {@link FactoryBean} instance and distinguish it from
* beans <i>created</i> by the FactoryBean. For example, if the bean named
* <code>myJndiObject</code> is a FactoryBean, getting <code>&myJndiObject</code>
* will return the factory, not the instance returned by the factory.
*/
String FACTORY_BEAN_PREFIX = "&";
/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>This method allows a Spring BeanFactory to be used as a replacement for the
* Singleton or Prototype design pattern. Callers may retain references to
* returned objects in the case of Singleton beans.
* <p>Translates aliases back to the corresponding canonical bean name.
* Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there is no bean definition
* with the specified name
* @throws BeansException if the bean could not be obtained
*/
Object getBean(String name) throws BeansException;
/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>Behaves the same as {@link #getBean(String)}, but provides a measure of type
* safety by throwing a BeanNotOfRequiredTypeException if the bean is not of the
* required type. This means that ClassCastException can't be thrown on casting
* the result correctly, as can happen with {@link #getBean(String)}.
* <p>Translates aliases back to the corresponding canonical bean name.
* Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to retrieve
* @param requiredType type the bean must match. Can be an interface or superclass
* of the actual class, or <code>null</code> for any match. For example, if the value
* is <code>Object.class</code>, this method will succeed whatever the class of the
* returned instance.
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there's no such bean definition
* @throws BeanNotOfRequiredTypeException if the bean is not of the required type
* @throws BeansException if the bean could not be created
*/
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
/**
* Return the bean instance that uniquely matches the given object type, if any.
* @param requiredType type the bean must match; can be an interface or superclass.
* {@code null} is disallowed.
* <p>This method goes into {@link ListableBeanFactory} by-type lookup territory
* but may also be translated into a conventional by-name lookup based on the name
* of the given type. For more extensive retrieval operations across sets of beans,
* use {@link ListableBeanFactory} and/or {@link BeanFactoryUtils}.
* @return an instance of the single bean matching the required type
* @throws NoSuchBeanDefinitionException if there is not exactly one matching bean found
* @since 3.0
* @see ListableBeanFactory
*/
<T> T getBean(Class<T> requiredType) throws BeansException;
/**
* Return an instance, which may be shared or independent, of the specified bean.
* <p>Allows for specifying explicit constructor arguments / factory method arguments,
* overriding the specified default arguments (if any) in the bean definition.
* @param name the name of the bean to retrieve
* @param args arguments to use if creating a prototype using explicit arguments to a
* static factory method. It is invalid to use a non-null args value in any other case.
* @return an instance of the bean
* @throws NoSuchBeanDefinitionException if there's no such bean definition
* @throws BeanDefinitionStoreException if arguments have been given but
* the affected bean isn't a prototype
* @throws BeansException if the bean could not be created
* @since 2.5
*/
Object getBean(String name, Object... args) throws BeansException;
/**
* Does this bean factory contain a bean definition or externally registered singleton
* instance with the given name?
* <p>If the given name is an alias, it will be translated back to the corresponding
* canonical bean name.
* <p>If this factory is hierarchical, will ask any parent factory if the bean cannot
* be found in this factory instance.
* <p>If a bean definition or singleton instance matching the given name is found,
* this method will return {@code true} whether the named bean definition is concrete
* or abstract, lazy or eager, in scope or not. Therefore, note that a {@code true}
* return value from this method does not necessarily indicate that {@link #getBean}
* will be able to obtain an instance for the same name.
* @param name the name of the bean to query
* @return whether a bean with the given name is present
*/
boolean containsBean(String name);
/**
* Is this bean a shared singleton? That is, will {@link #getBean} always
* return the same instance?
* <p>Note: This method returning <code>false</code> does not clearly indicate
* independent instances. It indicates non-singleton instances, which may correspond
* to a scoped bean as well. Use the {@link #isPrototype} operation to explicitly
* check for independent instances.
* <p>Translates aliases back to the corresponding canonical bean name.
* Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to query
* @return whether this bean corresponds to a singleton instance
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
* @see #getBean
* @see #isPrototype
*/
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
/**
* Is this bean a prototype? That is, will {@link #getBean} always return
* independent instances?
* <p>Note: This method returning <code>false</code> does not clearly indicate
* a singleton object. It indicates non-independent instances, which may correspond
* to a scoped bean as well. Use the {@link #isSingleton} operation to explicitly
* check for a shared singleton instance.
* <p>Translates aliases back to the corresponding canonical bean name.
* Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to query
* @return whether this bean will always deliver independent instances
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
* @since 2.0.3
* @see #getBean
* @see #isSingleton
*/
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
/**
* Check whether the bean with the given name matches the specified type.
* More specifically, check whether a {@link #getBean} call for the given name
* would return an object that is assignable to the specified target type.
* <p>Translates aliases back to the corresponding canonical bean name.
* Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to query
* @param targetType the type to match against
* @return <code>true</code> if the bean type matches,
* <code>false</code> if it doesn't match or cannot be determined yet
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
* @since 2.0.1
* @see #getBean
* @see #getType
*/
boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;
/**
* Determine the type of the bean with the given name. More specifically,
* determine the type of object that {@link #getBean} would return for the given name.
* <p>For a {@link FactoryBean}, return the type of object that the FactoryBean creates,
* as exposed by {@link FactoryBean#getObjectType()}.
* <p>Translates aliases back to the corresponding canonical bean name.
* Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the name of the bean to query
* @return the type of the bean, or <code>null</code> if not determinable
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
* @since 1.1.2
* @see #getBean
* @see #isTypeMatch
*/
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
/**
* Return the aliases for the given bean name, if any.
* All of those aliases point to the same bean when used in a {@link #getBean} call.
* <p>If the given name is an alias, the corresponding original bean name
* and other aliases (if any) will be returned, with the original bean name
* being the first element in the array.
* <p>Will ask the parent factory if the bean cannot be found in this factory instance.
* @param name the bean name to check for aliases
* @return the aliases, or an empty array if none
* @see #getBean
*/
String[] getAliases(String name);
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2002-2010 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;
import org.springframework.beans.BeansException;
/**
* Interface to be implemented by beans that wish to be aware of their
* owning {@link BeanFactory}.
*
* <p>For example, beans can look up collaborating beans via the factory
* (Dependency Lookup). Note that most beans will choose to receive references
* to collaborating beans via corresponding bean properties or constructor
* arguments (Dependency Injection).
*
* <p>For a list of all bean lifecycle methods, see the
* {@link BeanFactory BeanFactory javadocs}.
*
* @author Rod Johnson
* @author Chris Beams
* @since 11.03.2003
* @see BeanNameAware
* @see BeanClassLoaderAware
* @see InitializingBean
* @see org.springframework.context.ApplicationContextAware
*/
public interface BeanFactoryAware extends Aware {
/**
* Callback that supplies the owning factory to a bean instance.
* <p>Invoked after the population of normal bean properties
* but before an initialization callback such as
* {@link InitializingBean#afterPropertiesSet()} or a custom init-method.
* @param beanFactory owning BeanFactory (never <code>null</code>).
* The bean can immediately call methods on the factory.
* @throws BeansException in case of initialization errors
* @see BeanInitializationException
*/
void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

View File

@@ -0,0 +1,434 @@
/*
* Copyright 2002-2010 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;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import org.springframework.beans.BeansException;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Convenience methods operating on bean factories, in particular
* on the {@link ListableBeanFactory} interface.
*
* <p>Returns bean counts, bean names or bean instances,
* taking into account the nesting hierarchy of a bean factory
* (which the methods defined on the ListableBeanFactory interface don't,
* in contrast to the methods defined on the BeanFactory interface).
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 04.07.2003
*/
public abstract class BeanFactoryUtils {
/**
* Separator for generated bean names. If a class name or parent name is not
* unique, "#1", "#2" etc will be appended, until the name becomes unique.
*/
public static final String GENERATED_BEAN_NAME_SEPARATOR = "#";
/**
* Return whether the given name is a factory dereference
* (beginning with the factory dereference prefix).
* @param name the name of the bean
* @return whether the given name is a factory dereference
* @see BeanFactory#FACTORY_BEAN_PREFIX
*/
public static boolean isFactoryDereference(String name) {
return (name != null && name.startsWith(BeanFactory.FACTORY_BEAN_PREFIX));
}
/**
* Return the actual bean name, stripping out the factory dereference
* prefix (if any, also stripping repeated factory prefixes if found).
* @param name the name of the bean
* @return the transformed name
* @see BeanFactory#FACTORY_BEAN_PREFIX
*/
public static String transformedBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
String beanName = name;
while (beanName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
beanName = beanName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
}
return beanName;
}
/**
* Return whether the given name is a bean name which has been generated
* by the default naming strategy (containing a "#..." part).
* @param name the name of the bean
* @return whether the given name is a generated bean name
* @see #GENERATED_BEAN_NAME_SEPARATOR
* @see org.springframework.beans.factory.support.BeanDefinitionReaderUtils#generateBeanName
* @see org.springframework.beans.factory.support.DefaultBeanNameGenerator
*/
public static boolean isGeneratedBeanName(String name) {
return (name != null && name.contains(GENERATED_BEAN_NAME_SEPARATOR));
}
/**
* Extract the "raw" bean name from the given (potentially generated) bean name,
* excluding any "#..." suffixes which might have been added for uniqueness.
* @param name the potentially generated bean name
* @return the raw bean name
* @see #GENERATED_BEAN_NAME_SEPARATOR
*/
public static String originalBeanName(String name) {
Assert.notNull(name, "'name' must not be null");
int separatorIndex = name.indexOf(GENERATED_BEAN_NAME_SEPARATOR);
return (separatorIndex != -1 ? name.substring(0, separatorIndex) : name);
}
/**
* Count all beans in any hierarchy in which this factory participates.
* Includes counts of ancestor bean factories.
* <p>Beans that are "overridden" (specified in a descendant factory
* with the same name) are only counted once.
* @param lbf the bean factory
* @return count of beans including those defined in ancestor factories
*/
public static int countBeansIncludingAncestors(ListableBeanFactory lbf) {
return beanNamesIncludingAncestors(lbf).length;
}
/**
* Return all bean names in the factory, including ancestor factories.
* @param lbf the bean factory
* @return the array of matching bean names, or an empty array if none
* @see #beanNamesForTypeIncludingAncestors
*/
public static String[] beanNamesIncludingAncestors(ListableBeanFactory lbf) {
return beanNamesForTypeIncludingAncestors(lbf, Object.class);
}
/**
* Get all bean names for the given type, including those defined in ancestor
* factories. Will return unique names in case of overridden bean definitions.
* <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
* will get initialized. If the object created by the FactoryBean doesn't match,
* the raw FactoryBean itself will be matched against the type.
* <p>This version of <code>beanNamesForTypeIncludingAncestors</code> automatically
* includes prototypes and FactoryBeans.
* @param lbf the bean factory
* @param type the type that beans must match
* @return the array of matching bean names, or an empty array if none
*/
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class type) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
String[] result = lbf.getBeanNamesForType(type);
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
String[] parentResult = beanNamesForTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type);
List<String> resultList = new ArrayList<String>();
resultList.addAll(Arrays.asList(result));
for (String beanName : parentResult) {
if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
resultList.add(beanName);
}
}
result = StringUtils.toStringArray(resultList);
}
}
return result;
}
/**
* Get all bean names for the given type, including those defined in ancestor
* factories. Will return unique names in case of overridden bean definitions.
* <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
* flag is set, which means that FactoryBeans will get initialized. If the
* object created by the FactoryBean doesn't match, the raw FactoryBean itself
* will be matched against the type. If "allowEagerInit" is not set,
* only raw FactoryBeans will be checked (which doesn't require initialization
* of each FactoryBean).
* @param lbf the bean factory
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
* <i>objects created by FactoryBeans</i> (or by factory methods with a
* "factory-bean" reference) for the type check. Note that FactoryBeans need to be
* eagerly initialized to determine their type: So be aware that passing in "true"
* for this flag will initialize FactoryBeans and "factory-bean" references.
* @param type the type that beans must match
* @return the array of matching bean names, or an empty array if none
*/
public static String[] beanNamesForTypeIncludingAncestors(
ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit) {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
String[] parentResult = beanNamesForTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
List<String> resultList = new ArrayList<String>();
resultList.addAll(Arrays.asList(result));
for (String beanName : parentResult) {
if (!resultList.contains(beanName) && !hbf.containsLocalBean(beanName)) {
resultList.add(beanName);
}
}
result = StringUtils.toStringArray(resultList);
}
}
return result;
}
/**
* Return all beans of the given type or subtypes, also picking up beans defined in
* ancestor bean factories if the current bean factory is a HierarchicalBeanFactory.
* The returned Map will only contain beans of this type.
* <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
* will get initialized. If the object created by the FactoryBean doesn't match,
* the raw FactoryBean itself will be matched against the type.
* <p><b>Note: Beans of the same name will take precedence at the 'lowest' factory level,
* i.e. such beans will be returned from the lowest factory that they are being found in,
* hiding corresponding beans in ancestor factories.</b> This feature allows for
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
* @param lbf the bean factory
* @param type type of bean to match
* @return the Map of matching bean instances, or an empty Map if none
* @throws BeansException if a bean could not be created
*/
public static <T> Map<String, T> beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type)
throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map<String, T> result = new LinkedHashMap<String, T>(4);
result.putAll(lbf.getBeansOfType(type));
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
Map<String, T> parentResult = beansOfTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type);
for (Map.Entry<String, T> entry : parentResult.entrySet()) {
String beanName = entry.getKey();
if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
result.put(beanName, entry.getValue());
}
}
}
}
return result;
}
/**
* Return all beans of the given type or subtypes, also picking up beans defined in
* ancestor bean factories if the current bean factory is a HierarchicalBeanFactory.
* The returned Map will only contain beans of this type.
* <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
* which means that FactoryBeans will get initialized. If the object created by the
* FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
* type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked
* (which doesn't require initialization of each FactoryBean).
* <p><b>Note: Beans of the same name will take precedence at the 'lowest' factory level,
* i.e. such beans will be returned from the lowest factory that they are being found in,
* hiding corresponding beans in ancestor factories.</b> This feature allows for
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
* @param lbf the bean factory
* @param type type of bean to match
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
* <i>objects created by FactoryBeans</i> (or by factory methods with a
* "factory-bean" reference) for the type check. Note that FactoryBeans need to be
* eagerly initialized to determine their type: So be aware that passing in "true"
* for this flag will initialize FactoryBeans and "factory-bean" references.
* @return the Map of matching bean instances, or an empty Map if none
* @throws BeansException if a bean could not be created
*/
public static <T> Map<String, T> beansOfTypeIncludingAncestors(
ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map<String, T> result = new LinkedHashMap<String, T>(4);
result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
Map<String, T> parentResult = beansOfTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
for (Map.Entry<String, T> entry : parentResult.entrySet()) {
String beanName = entry.getKey();
if (!result.containsKey(beanName) && !hbf.containsLocalBean(beanName)) {
result.put(beanName, entry.getValue());
}
}
}
}
return result;
}
/**
* Return a single bean of the given type or subtypes, also picking up beans
* defined in ancestor bean factories if the current bean factory is a
* HierarchicalBeanFactory. Useful convenience method when we expect a
* single bean and don't care about the bean name.
* <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
* will get initialized. If the object created by the FactoryBean doesn't match,
* the raw FactoryBean itself will be matched against the type.
* <p>This version of <code>beanOfTypeIncludingAncestors</code> automatically includes
* prototypes and FactoryBeans.
* <p><b>Note: Beans of the same name will take precedence at the 'lowest' factory level,
* i.e. such beans will be returned from the lowest factory that they are being found in,
* hiding corresponding beans in ancestor factories.</b> This feature allows for
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
* @param lbf the bean factory
* @param type type of bean to match
* @return the matching bean instance
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
* if 0 or more than 1 beans of the given type were found
* @throws BeansException if the bean could not be created
*/
public static <T> T beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class<T> type)
throws BeansException {
Map<String, T> beansOfType = beansOfTypeIncludingAncestors(lbf, type);
if (beansOfType.size() == 1) {
return beansOfType.values().iterator().next();
}
else {
throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
}
}
/**
* Return a single bean of the given type or subtypes, also picking up beans
* defined in ancestor bean factories if the current bean factory is a
* HierarchicalBeanFactory. Useful convenience method when we expect a
* single bean and don't care about the bean name.
* <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
* which means that FactoryBeans will get initialized. If the object created by the
* FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
* type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked
* (which doesn't require initialization of each FactoryBean).
* <p><b>Note: Beans of the same name will take precedence at the 'lowest' factory level,
* i.e. such beans will be returned from the lowest factory that they are being found in,
* hiding corresponding beans in ancestor factories.</b> This feature allows for
* 'replacing' beans by explicitly choosing the same bean name in a child factory;
* the bean in the ancestor factory won't be visible then, not even for by-type lookups.
* @param lbf the bean factory
* @param type type of bean to match
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
* <i>objects created by FactoryBeans</i> (or by factory methods with a
* "factory-bean" reference) for the type check. Note that FactoryBeans need to be
* eagerly initialized to determine their type: So be aware that passing in "true"
* for this flag will initialize FactoryBeans and "factory-bean" references.
* @return the matching bean instance
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
* if 0 or more than 1 beans of the given type were found
* @throws BeansException if the bean could not be created
*/
public static <T> T beanOfTypeIncludingAncestors(
ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
Map<String, T> beansOfType = beansOfTypeIncludingAncestors(lbf, type, includeNonSingletons, allowEagerInit);
if (beansOfType.size() == 1) {
return beansOfType.values().iterator().next();
}
else {
throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
}
}
/**
* Return a single bean of the given type or subtypes, not looking in ancestor
* factories. Useful convenience method when we expect a single bean and
* don't care about the bean name.
* <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
* will get initialized. If the object created by the FactoryBean doesn't match,
* the raw FactoryBean itself will be matched against the type.
* <p>This version of <code>beanOfType</code> automatically includes
* prototypes and FactoryBeans.
* @param lbf the bean factory
* @param type type of bean to match
* @return the matching bean instance
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
* if 0 or more than 1 beans of the given type were found
* @throws BeansException if the bean could not be created
*/
public static <T> T beanOfType(ListableBeanFactory lbf, Class<T> type) throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map<String, T> beansOfType = lbf.getBeansOfType(type);
if (beansOfType.size() == 1) {
return beansOfType.values().iterator().next();
}
else {
throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
}
}
/**
* Return a single bean of the given type or subtypes, not looking in ancestor
* factories. Useful convenience method when we expect a single bean and
* don't care about the bean name.
* <p>Does consider objects created by FactoryBeans if the "allowEagerInit"
* flag is set, which means that FactoryBeans will get initialized. If the
* object created by the FactoryBean doesn't match, the raw FactoryBean itself
* will be matched against the type. If "allowEagerInit" is not set,
* only raw FactoryBeans will be checked (which doesn't require initialization
* of each FactoryBean).
* @param lbf the bean factory
* @param type type of bean to match
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
* <i>objects created by FactoryBeans</i> (or by factory methods with a
* "factory-bean" reference) for the type check. Note that FactoryBeans need to be
* eagerly initialized to determine their type: So be aware that passing in "true"
* for this flag will initialize FactoryBeans and "factory-bean" references.
* @return the matching bean instance
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
* if 0 or more than 1 beans of the given type were found
* @throws BeansException if the bean could not be created
*/
public static <T> T beanOfType(
ListableBeanFactory lbf, Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map<String, T> beansOfType = lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit);
if (beansOfType.size() == 1) {
return beansOfType.values().iterator().next();
}
else {
throw new NoSuchBeanDefinitionException(type, "expected single bean but found " + beansOfType.size());
}
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2002-2006 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;
import org.springframework.beans.FatalBeanException;
/**
* Exception that a bean implementation is suggested to throw if its own
* factory-aware initialization code fails. BeansExceptions thrown by
* bean factory methods themselves should simply be propagated as-is.
*
* <p>Note that non-factory-aware initialization methods like afterPropertiesSet()
* or a custom "init-method" can throw any exception.
*
* @author Juergen Hoeller
* @since 13.11.2003
* @see BeanFactoryAware#setBeanFactory
* @see InitializingBean#afterPropertiesSet
*/
public class BeanInitializationException extends FatalBeanException {
/**
* Create a new BeanInitializationException with the specified message.
* @param msg the detail message
*/
public BeanInitializationException(String msg) {
super(msg);
}
/**
* Create a new BeanInitializationException with the specified message
* and root cause.
* @param msg the detail message
* @param cause the root cause
*/
public BeanInitializationException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright 2002-2010 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;
/**
* Exception thrown when a bean instance has been requested for
* a bean definition which has been marked as abstract.
*
* @author Juergen Hoeller
* @since 1.1
* @see org.springframework.beans.factory.support.AbstractBeanDefinition#setAbstract
*/
public class BeanIsAbstractException extends BeanCreationException {
/**
* Create a new BeanIsAbstractException.
* @param beanName the name of the bean requested
*/
public BeanIsAbstractException(String beanName) {
super(beanName, "Bean definition is abstract");
}
}

View File

@@ -0,0 +1,40 @@
/*
* Copyright 2002-2005 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;
/**
* Exception thrown when a bean is not a factory, but a user tries to get
* at the factory for the given bean name. Whether a bean is a factory is
* determined by whether it implements the FactoryBean interface.
*
* @author Rod Johnson
* @since 10.03.2003
* @see org.springframework.beans.factory.FactoryBean
*/
public class BeanIsNotAFactoryException extends BeanNotOfRequiredTypeException {
/**
* Create a new BeanIsNotAFactoryException.
* @param name the name of the bean requested
* @param actualType the actual type returned, which did not match
* the expected type
*/
public BeanIsNotAFactoryException(String name, Class actualType) {
super(name, FactoryBean.class, actualType);
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright 2002-2011 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;
/**
* Interface to be implemented by beans that want to be aware of their
* bean name in a bean factory. Note that it is not usually recommended
* that an object depend on its bean name, as this represents a potentially
* brittle dependence on external configuration, as well as a possibly
* unnecessary dependence on a Spring API.
*
* <p>For a list of all bean lifecycle methods, see the
* {@link BeanFactory BeanFactory javadocs}.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 01.11.2003
* @see BeanClassLoaderAware
* @see BeanFactoryAware
* @see InitializingBean
*/
public interface BeanNameAware extends Aware {
/**
* Set the name of the bean in the bean factory that created this bean.
* <p>Invoked after population of normal bean properties but before an
* init callback such as {@link InitializingBean#afterPropertiesSet()}
* or a custom init-method.
* @param name the name of the bean in the factory.
* Note that this name is the actual bean name used in the factory, which may
* differ from the originally specified name: in particular for inner bean
* names, the actual bean name might have been made unique through appending
* "#..." suffixes. Use the {@link BeanFactoryUtils#originalBeanName(String)}
* method to extract the original bean name (without suffix), if desired.
*/
void setBeanName(String name);
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2002-2007 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;
import org.springframework.beans.BeansException;
/**
* Thrown when a bean doesn't match the expected type.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public class BeanNotOfRequiredTypeException extends BeansException {
/** The name of the instance that was of the wrong type */
private String beanName;
/** The required type */
private Class requiredType;
/** The offending type */
private Class actualType;
/**
* Create a new BeanNotOfRequiredTypeException.
* @param beanName the name of the bean requested
* @param requiredType the required type
* @param actualType the actual type returned, which did not match
* the expected type
*/
public BeanNotOfRequiredTypeException(String beanName, Class requiredType, Class actualType) {
super("Bean named '" + beanName + "' must be of type [" + requiredType.getName() +
"], but was actually of type [" + actualType.getName() + "]");
this.beanName = beanName;
this.requiredType = requiredType;
this.actualType = actualType;
}
/**
* Return the name of the instance that was of the wrong type.
*/
public String getBeanName() {
return this.beanName;
}
/**
* Return the expected type for the bean.
*/
public Class getRequiredType() {
return this.requiredType;
}
/**
* Return the actual type of the instance found.
*/
public Class getActualType() {
return this.actualType;
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 2002-2007 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;
import org.springframework.beans.FatalBeanException;
/**
* Exception thrown when the BeanFactory cannot load the specified class
* of a given bean.
*
* @author Juergen Hoeller
* @since 2.0
*/
public class CannotLoadBeanClassException extends FatalBeanException {
private String resourceDescription;
private String beanName;
private String beanClassName;
/**
* Create a new CannotLoadBeanClassException.
* @param resourceDescription description of the resource
* that the bean definition came from
* @param beanName the name of the bean requested
* @param beanClassName the name of the bean class
* @param cause the root cause
*/
public CannotLoadBeanClassException(
String resourceDescription, String beanName, String beanClassName, ClassNotFoundException cause) {
super("Cannot find class [" + beanClassName + "] for bean with name '" + beanName +
"' defined in " + resourceDescription, cause);
this.resourceDescription = resourceDescription;
this.beanName = beanName;
this.beanClassName = beanClassName;
}
/**
* Create a new CannotLoadBeanClassException.
* @param resourceDescription description of the resource
* that the bean definition came from
* @param beanName the name of the bean requested
* @param beanClassName the name of the bean class
* @param cause the root cause
*/
public CannotLoadBeanClassException(
String resourceDescription, String beanName, String beanClassName, LinkageError cause) {
super("Error loading class [" + beanClassName + "] for bean with name '" + beanName +
"' defined in " + resourceDescription + ": problem with class file or dependent class", cause);
this.resourceDescription = resourceDescription;
this.beanName = beanName;
this.beanClassName = beanClassName;
}
/**
* Return the description of the resource that the bean
* definition came from.
*/
public String getResourceDescription() {
return this.resourceDescription;
}
/**
* Return the name of the bean requested.
*/
public String getBeanName() {
return this.beanName;
}
/**
* Return the name of the class we were trying to load.
*/
public String getBeanClassName() {
return this.beanClassName;
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-2005 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;
/**
* Interface to be implemented by beans that want to release resources
* on destruction. A BeanFactory is supposed to invoke the destroy
* method if it disposes a cached singleton. An application context
* is supposed to dispose all of its singletons on close.
*
* <p>An alternative to implementing DisposableBean is specifying a custom
* destroy-method, for example in an XML bean definition.
* For a list of all bean lifecycle methods, see the BeanFactory javadocs.
*
* @author Juergen Hoeller
* @since 12.08.2003
* @see org.springframework.beans.factory.support.RootBeanDefinition#getDestroyMethodName
* @see org.springframework.context.ConfigurableApplicationContext#close
*/
public interface DisposableBean {
/**
* Invoked by a BeanFactory on destruction of a singleton.
* @throws Exception in case of shutdown errors.
* Exceptions will get logged but not rethrown to allow
* other beans to release their resources too.
*/
void destroy() throws Exception;
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright 2002-2010 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;
/**
* Interface to be implemented by objects used within a {@link BeanFactory}
* which are themselves factories. If a bean implements this interface,
* it is used as a factory for an object to expose, not directly as a bean
* instance that will be exposed itself.
*
* <p><b>NB: A bean that implements this interface cannot be used as a
* normal bean.</b> A FactoryBean is defined in a bean style, but the
* object exposed for bean references ({@link #getObject()} is always
* the object that it creates.
*
* <p>FactoryBeans can support singletons and prototypes, and can
* either create objects lazily on demand or eagerly on startup.
* The {@link SmartFactoryBean} interface allows for exposing
* more fine-grained behavioral metadata.
*
* <p>This interface is heavily used within the framework itself, for
* example for the AOP {@link org.springframework.aop.framework.ProxyFactoryBean}
* or the {@link org.springframework.jndi.JndiObjectFactoryBean}.
* It can be used for application components as well; however,
* this is not common outside of infrastructure code.
*
* <p><b>NOTE:</b> FactoryBean objects participate in the containing
* BeanFactory's synchronization of bean creation. There is usually no
* need for internal synchronization other than for purposes of lazy
* initialization within the FactoryBean itself (or the like).
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 08.03.2003
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.aop.framework.ProxyFactoryBean
* @see org.springframework.jndi.JndiObjectFactoryBean
*/
public interface FactoryBean<T> {
/**
* Return an instance (possibly shared or independent) of the object
* managed by this factory.
* <p>As with a {@link BeanFactory}, this allows support for both the
* Singleton and Prototype design pattern.
* <p>If this FactoryBean is not fully initialized yet at the time of
* the call (for example because it is involved in a circular reference),
* throw a corresponding {@link FactoryBeanNotInitializedException}.
* <p>As of Spring 2.0, FactoryBeans are allowed to return <code>null</code>
* objects. The factory will consider this as normal value to be used; it
* will not throw a FactoryBeanNotInitializedException in this case anymore.
* FactoryBean implementations are encouraged to throw
* FactoryBeanNotInitializedException themselves now, as appropriate.
* @return an instance of the bean (can be <code>null</code>)
* @throws Exception in case of creation errors
* @see FactoryBeanNotInitializedException
*/
T getObject() throws Exception;
/**
* Return the type of object that this FactoryBean creates,
* or <code>null</code> if not known in advance.
* <p>This allows one to check for specific types of beans without
* instantiating objects, for example on autowiring.
* <p>In the case of implementations that are creating a singleton object,
* this method should try to avoid singleton creation as far as possible;
* it should rather estimate the type in advance.
* For prototypes, returning a meaningful type here is advisable too.
* <p>This method can be called <i>before</i> this FactoryBean has
* been fully initialized. It must not rely on state created during
* initialization; of course, it can still use such state if available.
* <p><b>NOTE:</b> Autowiring will simply ignore FactoryBeans that return
* <code>null</code> here. Therefore it is highly recommended to implement
* this method properly, using the current state of the FactoryBean.
* @return the type of object that this FactoryBean creates,
* or <code>null</code> if not known at the time of the call
* @see ListableBeanFactory#getBeansOfType
*/
Class<?> getObjectType();
/**
* Is the object managed by this factory a singleton? That is,
* will {@link #getObject()} always return the same object
* (a reference that can be cached)?
* <p><b>NOTE:</b> If a FactoryBean indicates to hold a singleton object,
* the object returned from <code>getObject()</code> might get cached
* by the owning BeanFactory. Hence, do not return <code>true</code>
* unless the FactoryBean always exposes the same reference.
* <p>The singleton status of the FactoryBean itself will generally
* be provided by the owning BeanFactory; usually, it has to be
* defined as singleton there.
* <p><b>NOTE:</b> This method returning <code>false</code> does not
* necessarily indicate that returned objects are independent instances.
* An implementation of the extended {@link SmartFactoryBean} interface
* may explicitly indicate independent instances through its
* {@link SmartFactoryBean#isPrototype()} method. Plain {@link FactoryBean}
* implementations which do not implement this extended interface are
* simply assumed to always return independent instances if the
* <code>isSingleton()</code> implementation returns <code>false</code>.
* @return whether the exposed object is a singleton
* @see #getObject()
* @see SmartFactoryBean#isPrototype()
*/
boolean isSingleton();
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2002-2007 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;
import org.springframework.beans.FatalBeanException;
/**
* Exception to be thrown from a FactoryBean's <code>getObject()</code> method
* if the bean is not fully initialized yet, for example because it is involved
* in a circular reference.
*
* <p>Note: A circular reference with a FactoryBean cannot be solved by eagerly
* caching singleton instances like with normal beans. The reason is that
* <i>every</i> FactoryBean needs to be fully initialized before it can
* return the created bean, while only <i>specific</i> normal beans need
* to be initialized - that is, if a collaborating bean actually invokes
* them on initialization instead of just storing the reference.
*
* @author Juergen Hoeller
* @since 30.10.2003
* @see FactoryBean#getObject()
*/
public class FactoryBeanNotInitializedException extends FatalBeanException {
/**
* Create a new FactoryBeanNotInitializedException with the default message.
*/
public FactoryBeanNotInitializedException() {
super("FactoryBean is not fully initialized yet");
}
/**
* Create a new FactoryBeanNotInitializedException with the given message.
* @param msg the detail message
*/
public FactoryBeanNotInitializedException(String msg) {
super(msg);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright 2002-2006 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;
/**
* Sub-interface implemented by bean factories that can be part
* of a hierarchy.
*
* <p>The corresponding <code>setParentBeanFactory</code> method for bean
* factories that allow setting the parent in a configurable
* fashion can be found in the ConfigurableBeanFactory interface.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 07.07.2003
* @see org.springframework.beans.factory.config.ConfigurableBeanFactory#setParentBeanFactory
*/
public interface HierarchicalBeanFactory extends BeanFactory {
/**
* Return the parent bean factory, or <code>null</code> if there is none.
*/
BeanFactory getParentBeanFactory();
/**
* Return whether the local bean factory contains a bean of the given name,
* ignoring beans defined in ancestor contexts.
* <p>This is an alternative to <code>containsBean</code>, ignoring a bean
* of the given name from an ancestor bean factory.
* @param name the name of the bean to query
* @return whether a bean with the given name is defined in the local factory
* @see org.springframework.beans.factory.BeanFactory#containsBean
*/
boolean containsLocalBean(String name);
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2002-2005 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;
/**
* Interface to be implemented by beans that need to react once all their
* properties have been set by a BeanFactory: for example, to perform custom
* initialization, or merely to check that all mandatory properties have been set.
*
* <p>An alternative to implementing InitializingBean is specifying a custom
* init-method, for example in an XML bean definition.
* For a list of all bean lifecycle methods, see the BeanFactory javadocs.
*
* @author Rod Johnson
* @see BeanNameAware
* @see BeanFactoryAware
* @see BeanFactory
* @see org.springframework.beans.factory.support.RootBeanDefinition#getInitMethodName
* @see org.springframework.context.ApplicationContextAware
*/
public interface InitializingBean {
/**
* Invoked by a BeanFactory after it has set all bean properties supplied
* (and satisfied BeanFactoryAware and ApplicationContextAware).
* <p>This method allows the bean instance to perform initialization only
* possible when all bean properties have been set and to throw an
* exception in the event of misconfiguration.
* @throws Exception in the event of misconfiguration (such
* as failure to set an essential property) or if initialization fails.
*/
void afterPropertiesSet() throws Exception;
}

View File

@@ -0,0 +1,234 @@
/*
* 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;
import java.lang.annotation.Annotation;
import java.util.Map;
import org.springframework.beans.BeansException;
/**
* Extension of the {@link BeanFactory} interface to be implemented by bean factories
* that can enumerate all their bean instances, rather than attempting bean lookup
* by name one by one as requested by clients. BeanFactory implementations that
* preload all their bean definitions (such as XML-based factories) may implement
* this interface.
*
* <p>If this is a {@link HierarchicalBeanFactory}, the return values will <i>not</i>
* take any BeanFactory hierarchy into account, but will relate only to the beans
* defined in the current factory. Use the {@link BeanFactoryUtils} helper class
* to consider beans in ancestor factories too.
*
* <p>The methods in this interface will just respect bean definitions of this factory.
* They will ignore any singleton beans that have been registered by other means like
* {@link org.springframework.beans.factory.config.ConfigurableBeanFactory}'s
* <code>registerSingleton</code> method, with the exception of
* <code>getBeanNamesOfType</code> and <code>getBeansOfType</code> which will check
* such manually registered singletons too. Of course, BeanFactory's <code>getBean</code>
* does allow transparent access to such special beans as well. However, in typical
* scenarios, all beans will be defined by external bean definitions anyway, so most
* applications don't need to worry about this differentation.
*
* <p><b>NOTE:</b> With the exception of <code>getBeanDefinitionCount</code>
* and <code>containsBeanDefinition</code>, the methods in this interface
* are not designed for frequent invocation. Implementations may be slow.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 16 April 2001
* @see HierarchicalBeanFactory
* @see BeanFactoryUtils
*/
public interface ListableBeanFactory extends BeanFactory {
/**
* Check if this bean factory contains a bean definition with the given name.
* <p>Does not consider any hierarchy this factory may participate in,
* and ignores any singleton beans that have been registered by
* other means than bean definitions.
* @param beanName the name of the bean to look for
* @return if this bean factory contains a bean definition with the given name
* @see #containsBean
*/
boolean containsBeanDefinition(String beanName);
/**
* Return the number of beans defined in the factory.
* <p>Does not consider any hierarchy this factory may participate in,
* and ignores any singleton beans that have been registered by
* other means than bean definitions.
* @return the number of beans defined in the factory
*/
int getBeanDefinitionCount();
/**
* Return the names of all beans defined in this factory.
* <p>Does not consider any hierarchy this factory may participate in,
* and ignores any singleton beans that have been registered by
* other means than bean definitions.
* @return the names of all beans defined in this factory,
* or an empty array if none defined
*/
String[] getBeanDefinitionNames();
/**
* Return the names of beans matching the given type (including subclasses),
* judging from either bean definitions or the value of <code>getObjectType</code>
* in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
* will get initialized. If the object created by the FactoryBean doesn't match,
* the raw FactoryBean itself will be matched against the type.
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' <code>beanNamesForTypeIncludingAncestors</code>
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>This version of <code>getBeanNamesForType</code> matches all kinds of beans,
* be it singletons, prototypes, or FactoryBeans. In most implementations, the
* result will be the same as for <code>getBeanNamesOfType(type, true, true)</code>.
* <p>Bean names returned by this method should always return bean names <i>in the
* order of definition</i> in the backend configuration, as far as possible.
* @param type the class or interface to match, or <code>null</code> for all bean names
* @return the names of beans (or objects created by FactoryBeans) matching
* the given object type (including subclasses), or an empty array if none
* @see FactoryBean#getObjectType
* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class)
*/
String[] getBeanNamesForType(Class<?> type);
/**
* Return the names of beans matching the given type (including subclasses),
* judging from either bean definitions or the value of <code>getObjectType</code>
* in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
* which means that FactoryBeans will get initialized. If the object created by the
* FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
* type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked
* (which doesn't require initialization of each FactoryBean).
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' <code>beanNamesForTypeIncludingAncestors</code>
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>Bean names returned by this method should always return bean names <i>in the
* order of definition</i> in the backend configuration, as far as possible.
* @param type the class or interface to match, or <code>null</code> for all bean names
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
* <i>objects created by FactoryBeans</i> (or by factory methods with a
* "factory-bean" reference) for the type check. Note that FactoryBeans need to be
* eagerly initialized to determine their type: So be aware that passing in "true"
* for this flag will initialize FactoryBeans and "factory-bean" references.
* @return the names of beans (or objects created by FactoryBeans) matching
* the given object type (including subclasses), or an empty array if none
* @see FactoryBean#getObjectType
* @see BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
*/
String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit);
/**
* Return the bean instances that match the given object type (including
* subclasses), judging from either bean definitions or the value of
* <code>getObjectType</code> in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans, which means that FactoryBeans
* will get initialized. If the object created by the FactoryBean doesn't match,
* the raw FactoryBean itself will be matched against the type.
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' <code>beansOfTypeIncludingAncestors</code>
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>This version of getBeansOfType matches all kinds of beans, be it
* singletons, prototypes, or FactoryBeans. In most implementations, the
* result will be the same as for <code>getBeansOfType(type, true, true)</code>.
* <p>The Map returned by this method should always return bean names and
* corresponding bean instances <i>in the order of definition</i> in the
* backend configuration, as far as possible.
* @param type the class or interface to match, or <code>null</code> for all concrete beans
* @return a Map with the matching beans, containing the bean names as
* keys and the corresponding bean instances as values
* @throws BeansException if a bean could not be created
* @since 1.1.2
* @see FactoryBean#getObjectType
* @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class)
*/
<T> Map<String, T> getBeansOfType(Class<T> type) throws BeansException;
/**
* Return the bean instances that match the given object type (including
* subclasses), judging from either bean definitions or the value of
* <code>getObjectType</code> in the case of FactoryBeans.
* <p><b>NOTE: This method introspects top-level beans only.</b> It does <i>not</i>
* check nested beans which might match the specified type as well.
* <p>Does consider objects created by FactoryBeans if the "allowEagerInit" flag is set,
* which means that FactoryBeans will get initialized. If the object created by the
* FactoryBean doesn't match, the raw FactoryBean itself will be matched against the
* type. If "allowEagerInit" is not set, only raw FactoryBeans will be checked
* (which doesn't require initialization of each FactoryBean).
* <p>Does not consider any hierarchy this factory may participate in.
* Use BeanFactoryUtils' <code>beansOfTypeIncludingAncestors</code>
* to include beans in ancestor factories too.
* <p>Note: Does <i>not</i> ignore singleton beans that have been registered
* by other means than bean definitions.
* <p>The Map returned by this method should always return bean names and
* corresponding bean instances <i>in the order of definition</i> in the
* backend configuration, as far as possible.
* @param type the class or interface to match, or <code>null</code> for all concrete beans
* @param includeNonSingletons whether to include prototype or scoped beans too
* or just singletons (also applies to FactoryBeans)
* @param allowEagerInit whether to initialize <i>lazy-init singletons</i> and
* <i>objects created by FactoryBeans</i> (or by factory methods with a
* "factory-bean" reference) for the type check. Note that FactoryBeans need to be
* eagerly initialized to determine their type: So be aware that passing in "true"
* for this flag will initialize FactoryBeans and "factory-bean" references.
* @return a Map with the matching beans, containing the bean names as
* keys and the corresponding bean instances as values
* @throws BeansException if a bean could not be created
* @see FactoryBean#getObjectType
* @see BeanFactoryUtils#beansOfTypeIncludingAncestors(ListableBeanFactory, Class, boolean, boolean)
*/
<T> Map<String, T> getBeansOfType(Class<T> type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException;
/**
* Find all beans whose <code>Class</code> has the supplied {@link java.lang.annotation.Annotation} type.
* @param annotationType the type of annotation to look for
* @return a Map with the matching beans, containing the bean names as
* keys and the corresponding bean instances as values
* @throws BeansException if a bean could not be created
*/
Map<String, Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType)
throws BeansException;
/**
* Find a {@link Annotation} of <code>annotationType</code> on the specified
* bean, traversing its interfaces and super classes if no annotation can be
* found on the given class itself.
* @param beanName the name of the bean to look for annotations on
* @param annotationType the annotation class to look for
* @return the annotation of the given type found, or <code>null</code>
*/
<A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType);
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2002-2006 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;
/**
* Counterpart of BeanNameAware. Returns the bean name of an object.
*
* <p>This interface can be introduced to avoid a brittle dependence
* on bean name in objects used with Spring IoC and Spring AOP.
*
* @author Rod Johnson
* @since 2.0
* @see BeanNameAware
*/
public interface NamedBean {
/**
* Return the name of this bean in a Spring bean factory.
*/
String getBeanName();
}

View File

@@ -0,0 +1,106 @@
/*
* 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;
import org.springframework.beans.BeansException;
import org.springframework.util.StringUtils;
/**
* Exception thrown when a BeanFactory is asked for a bean
* instance name for which it cannot find a definition.
*
* @author Rod Johnson
* @author Juergen Hoeller
*/
public class NoSuchBeanDefinitionException extends BeansException {
/** Name of the missing bean */
private String beanName;
/** Required bean type */
private Class beanType;
/**
* Create a new NoSuchBeanDefinitionException.
* @param name the name of the missing bean
*/
public NoSuchBeanDefinitionException(String name) {
super("No bean named '" + name + "' is defined");
this.beanName = name;
}
/**
* Create a new NoSuchBeanDefinitionException.
* @param name the name of the missing bean
* @param message detailed message describing the problem
*/
public NoSuchBeanDefinitionException(String name, String message) {
super("No bean named '" + name + "' is defined: " + message);
this.beanName = name;
}
/**
* Create a new NoSuchBeanDefinitionException.
* @param type required type of bean
*/
public NoSuchBeanDefinitionException(Class type) {
super("No unique bean of type [" + type.getName() + "] is defined");
this.beanType = type;
}
/**
* Create a new NoSuchBeanDefinitionException.
* @param type required type of bean
* @param message detailed message describing the problem
*/
public NoSuchBeanDefinitionException(Class type, String message) {
super("No unique bean of type [" + type.getName() + "] is defined: " + message);
this.beanType = type;
}
/**
* Create a new NoSuchBeanDefinitionException.
* @param type required type of bean
* @param dependencyDescription a description of the originating dependency
* @param message detailed message describing the problem
*/
public NoSuchBeanDefinitionException(Class type, String dependencyDescription, String message) {
super("No matching bean of type [" + type.getName() + "] found for dependency" +
(StringUtils.hasLength(dependencyDescription) ? " [" + dependencyDescription + "]" : "") +
": " + message);
this.beanType = type;
}
/**
* Return the name of the missing bean,
* if it was a lookup by name that failed.
*/
public String getBeanName() {
return this.beanName;
}
/**
* Return the required type of bean,
* if it was a lookup by type that failed.
*/
public Class getBeanType() {
return this.beanType;
}
}

View File

@@ -0,0 +1,48 @@
/*
* 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;
import org.springframework.beans.BeansException;
/**
* Defines a factory which can return an Object instance
* (possibly shared or independent) when invoked.
*
* <p>This interface is typically used to encapsulate a generic factory which
* returns a new instance (prototype) of some target object on each invocation.
*
* <p>This interface is similar to {@link FactoryBean}, but implementations
* of the latter are normally meant to be defined as SPI instances in a
* {@link BeanFactory}, while implementations of this class are normally meant
* to be fed as an API to other beans (through injection). As such, the
* <code>getObject()</code> method has different exception handling behavior.
*
* @author Colin Sampaleanu
* @since 1.0.2
* @see FactoryBean
*/
public interface ObjectFactory<T> {
/**
* Return an instance (possibly shared or independent)
* of the object managed by this factory.
* @return an instance of the bean (should never be <code>null</code>)
* @throws BeansException in case of creation errors
*/
T getObject() throws BeansException;
}

View File

@@ -0,0 +1,75 @@
/*
* 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;
/**
* Extension of the {@link FactoryBean} interface. Implementations may
* indicate whether they always return independent instances, for the
* case where their {@link #isSingleton()} implementation returning
* <code>false</code> does not clearly indicate independent instances.
*
* <p>Plain {@link FactoryBean} implementations which do not implement
* this extended interface are simply assumed to always return independent
* instances if their {@link #isSingleton()} implementation returns
* <code>false</code>; the exposed object is only accessed on demand.
*
* <p><b>NOTE:</b> This interface is a special purpose interface, mainly for
* internal use within the framework and within collaborating frameworks.
* In general, application-provided FactoryBeans should simply implement
* the plain {@link FactoryBean} interface. New methods might be added
* to this extended interface even in point releases.
*
* @author Juergen Hoeller
* @since 2.0.3
* @see #isPrototype()
* @see #isSingleton()
*/
public interface SmartFactoryBean<T> extends FactoryBean<T> {
/**
* Is the object managed by this factory a prototype? That is,
* will {@link #getObject()} always return an independent instance?
* <p>The prototype status of the FactoryBean itself will generally
* be provided by the owning {@link BeanFactory}; usually, it has to be
* defined as singleton there.
* <p>This method is supposed to strictly check for independent instances;
* it should not return <code>true</code> for scoped objects or other
* kinds of non-singleton, non-independent objects. For this reason,
* this is not simply the inverted form of {@link #isSingleton()}.
* @return whether the exposed object is a prototype
* @see #getObject()
* @see #isSingleton()
*/
boolean isPrototype();
/**
* Does this FactoryBean expect eager initialization, that is,
* eagerly initialize itself as well as expect eager initialization
* of its singleton object (if any)?
* <p>A standard FactoryBean is not expected to initialize eagerly:
* Its {@link #getObject()} will only be called for actual access, even
* in case of a singleton object. Returning <code>true</code> from this
* method suggests that {@link #getObject()} should be called eagerly,
* also applying post-processors eagerly. This may make sense in case
* of a {@link #isSingleton() singleton} object, in particular if
* post-processors expect to be applied on startup.
* @return whether eager initialization applies
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory#preInstantiateSingletons()
*/
boolean isEagerInit();
}

View File

@@ -0,0 +1,94 @@
/*
* 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;
import org.springframework.beans.BeansException;
import org.springframework.util.ClassUtils;
/**
* Exception thrown when a bean depends on other beans or simple properties
* that were not specified in the bean factory definition, although
* dependency checking was enabled.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 03.09.2003
*/
public class UnsatisfiedDependencyException extends BeanCreationException {
/**
* Create a new UnsatisfiedDependencyException.
* @param resourceDescription description of the resource that the bean definition came from
* @param beanName the name of the bean requested
* @param propertyName the name of the bean property that couldn't be satisfied
* @param msg the detail message
*/
public UnsatisfiedDependencyException(
String resourceDescription, String beanName, String propertyName, String msg) {
super(resourceDescription, beanName,
"Unsatisfied dependency expressed through bean property '" + propertyName + "'" +
(msg != null ? ": " + msg : ""));
}
/**
* Create a new UnsatisfiedDependencyException.
* @param resourceDescription description of the resource that the bean definition came from
* @param beanName the name of the bean requested
* @param propertyName the name of the bean property that couldn't be satisfied
* @param ex the bean creation exception that indicated the unsatisfied dependency
*/
public UnsatisfiedDependencyException(
String resourceDescription, String beanName, String propertyName, BeansException ex) {
this(resourceDescription, beanName, propertyName, (ex != null ? ": " + ex.getMessage() : ""));
initCause(ex);
}
/**
* Create a new UnsatisfiedDependencyException.
* @param resourceDescription description of the resource that the bean definition came from
* @param beanName the name of the bean requested
* @param ctorArgIndex the index of the constructor argument that couldn't be satisfied
* @param ctorArgType the type of the constructor argument that couldn't be satisfied
* @param msg the detail message
*/
public UnsatisfiedDependencyException(
String resourceDescription, String beanName, int ctorArgIndex, Class ctorArgType, String msg) {
super(resourceDescription, beanName,
"Unsatisfied dependency expressed through constructor argument with index " +
ctorArgIndex + " of type [" + ClassUtils.getQualifiedName(ctorArgType) + "]" +
(msg != null ? ": " + msg : ""));
}
/**
* Create a new UnsatisfiedDependencyException.
* @param resourceDescription description of the resource that the bean definition came from
* @param beanName the name of the bean requested
* @param ctorArgIndex the index of the constructor argument that couldn't be satisfied
* @param ctorArgType the type of the constructor argument that couldn't be satisfied
* @param ex the bean creation exception that indicated the unsatisfied dependency
*/
public UnsatisfiedDependencyException(
String resourceDescription, String beanName, int ctorArgIndex, Class ctorArgType, BeansException ex) {
this(resourceDescription, beanName, ctorArgIndex, ctorArgType, (ex != null ? ": " + ex.getMessage() : ""));
initCause(ex);
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2002-2007 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.access;
import org.springframework.beans.BeansException;
/**
* Defines a contract for the lookup, use, and release of a
* {@link org.springframework.beans.factory.BeanFactory},
* or a <code>BeanFactory</code> subclass such as an
* {@link org.springframework.context.ApplicationContext}.
*
* <p>Where this interface is implemented as a singleton class such as
* {@link SingletonBeanFactoryLocator}, the Spring team <strong>strongly</strong>
* suggests that it be used sparingly and with caution. By far the vast majority
* of the code inside an application is best written in a Dependency Injection
* style, where that code is served out of a
* <code>BeanFactory</code>/<code>ApplicationContext</code> container, and has
* its own dependencies supplied by the container when it is created. However,
* even such a singleton implementation sometimes has its use in the small glue
* layers of code that is sometimes needed to tie other code together. For
* example, third party code may try to construct new objects directly, without
* the ability to force it to get these objects out of a <code>BeanFactory</code>.
* If the object constructed by the third party code is just a small stub or
* proxy, which then uses an implementation of this class to get a
* <code>BeanFactory</code> from which it gets the real object, to which it
* delegates, then proper Dependency Injection has been achieved.
*
* <p>As another example, in a complex J2EE app with multiple layers, with each
* layer having its own <code>ApplicationContext</code> definition (in a
* hierarchy), a class like <code>SingletonBeanFactoryLocator</code> may be used
* to demand load these contexts.
*
* @author Colin Sampaleanu
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.context.access.DefaultLocatorFactory
* @see org.springframework.context.ApplicationContext
*/
public interface BeanFactoryLocator {
/**
* Use the {@link org.springframework.beans.factory.BeanFactory} (or derived
* interface such as {@link org.springframework.context.ApplicationContext})
* specified by the <code>factoryKey</code> parameter.
* <p>The definition is possibly loaded/created as needed.
* @param factoryKey a resource name specifying which <code>BeanFactory</code> the
* <code>BeanFactoryLocator</code> must return for usage. The actual meaning of the
* resource name is specific to the implementation of <code>BeanFactoryLocator</code>.
* @return the <code>BeanFactory</code> instance, wrapped as a {@link BeanFactoryReference} object
* @throws BeansException if there is an error loading or accessing the <code>BeanFactory</code>
*/
BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException;
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2002-2007 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.access;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanFactory;
/**
* Used to track a reference to a {@link BeanFactory} obtained through
* a {@link BeanFactoryLocator}.
*
* <p>It is safe to call {@link #release()} multiple times, but
* {@link #getFactory()} must not be called after calling release.
*
* @author Colin Sampaleanu
* @see BeanFactoryLocator
* @see org.springframework.context.access.ContextBeanFactoryReference
*/
public interface BeanFactoryReference {
/**
* Return the {@link BeanFactory} instance held by this reference.
* @throws IllegalStateException if invoked after <code>release()</code> has been called
*/
BeanFactory getFactory();
/**
* Indicate that the {@link BeanFactory} instance referred to by this object is not
* needed any longer by the client code which obtained the {@link BeanFactoryReference}.
* <p>Depending on the actual implementation of {@link BeanFactoryLocator}, and
* the actual type of <code>BeanFactory</code>, this may possibly not actually
* do anything; alternately in the case of a 'closeable' <code>BeanFactory</code>
* or derived class (such as {@link org.springframework.context.ApplicationContext})
* may 'close' it, or may 'close' it once no more references remain.
* <p>In an EJB usage scenario this would normally be called from
* <code>ejbRemove()</code> and <code>ejbPassivate()</code>.
* <p>This is safe to call multiple times.
* @throws FatalBeanException if the <code>BeanFactory</code> cannot be released
* @see BeanFactoryLocator
* @see org.springframework.context.access.ContextBeanFactoryReference
* @see org.springframework.context.ConfigurableApplicationContext#close()
*/
void release() throws FatalBeanException;
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2002-2005 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.access;
import org.springframework.beans.FatalBeanException;
/**
* Exception thrown if a bean factory could not be loaded by a bootstrap class.
*
* @author Rod Johnson
* @since 02.12.2002
*/
public class BootstrapException extends FatalBeanException {
/**
* Create a new BootstrapException with the specified message.
* @param msg the detail message
*/
public BootstrapException(String msg) {
super(msg);
}
/**
* Create a new BootstrapException with the specified message
* and root cause.
* @param msg the detail message
* @param cause the root cause
*/
public BootstrapException(String msg, Throwable cause) {
super(msg, cause);
}
}

View File

@@ -0,0 +1,532 @@
/*
* 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.access;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.FatalBeanException;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
/**
* <p>Keyed-singleton implementation of {@link BeanFactoryLocator},
* which accesses shared Spring {@link BeanFactory} instances.</p>
*
* <p>Please see the warning in BeanFactoryLocator's javadoc about appropriate usage
* of singleton style BeanFactoryLocator implementations. It is the opinion of the
* Spring team that the use of this class and similar classes is unnecessary except
* (sometimes) for a small amount of glue code. Excessive usage will lead to code
* that is more tightly coupled, and harder to modify or test.</p>
*
* <p>In this implementation, a BeanFactory is built up from one or more XML
* definition file fragments, accessed as resources. The default resource name
* searched for is 'classpath*:beanRefFactory.xml', with the Spring-standard
* 'classpath*:' prefix ensuring that if the classpath contains multiple copies
* of this file (perhaps one in each component jar) they will be combined. To
* override the default resource name, instead of using the no-arg
* {@link #getInstance()} method, use the {@link #getInstance(String selector)}
* variant, which will treat the 'selector' argument as the resource name to
* search for.</p>
*
* <p>The purpose of this 'outer' BeanFactory is to create and hold a copy of one
* or more 'inner' BeanFactory or ApplicationContext instances, and allow those
* to be obtained either directly or via an alias. As such, this class provides
* both singleton style access to one or more BeanFactories/ApplicationContexts,
* and also a level of indirection, allowing multiple pieces of code, which are
* not able to work in a Dependency Injection fashion, to refer to and use the
* same target BeanFactory/ApplicationContext instance(s), by different names.<p>
*
* <p>Consider an example application scenario:
*
* <ul>
* <li><code>com.mycompany.myapp.util.applicationContext.xml</code> -
* ApplicationContext definition file which defines beans for 'util' layer.
* <li><code>com.mycompany.myapp.dataaccess-applicationContext.xml</code> -
* ApplicationContext definition file which defines beans for 'data access' layer.
* Depends on the above.
* <li><code>com.mycompany.myapp.services.applicationContext.xml</code> -
* ApplicationContext definition file which defines beans for 'services' layer.
* Depends on the above.
* </ul>
*
* <p>In an ideal scenario, these would be combined to create one ApplicationContext,
* or created as three hierarchical ApplicationContexts, by one piece of code
* somewhere at application startup (perhaps a Servlet filter), from which all other
* code in the application would flow, obtained as beans from the context(s). However
* when third party code enters into the picture, things can get problematic. If the
* third party code needs to create user classes, which should normally be obtained
* from a Spring BeanFactory/ApplicationContext, but can handle only newInstance()
* style object creation, then some extra work is required to actually access and
* use object from a BeanFactory/ApplicationContext. One solutions is to make the
* class created by the third party code be just a stub or proxy, which gets the
* real object from a BeanFactory/ApplicationContext, and delegates to it. However,
* it is is not normally workable for the stub to create the BeanFactory on each
* use, as depending on what is inside it, that can be an expensive operation.
* Additionally, there is a fairly tight coupling between the stub and the name of
* the definition resource for the BeanFactory/ApplicationContext. This is where
* SingletonBeanFactoryLocator comes in. The stub can obtain a
* SingletonBeanFactoryLocator instance, which is effectively a singleton, and
* ask it for an appropriate BeanFactory. A subsequent invocation (assuming the
* same class loader is involved) by the stub or another piece of code, will obtain
* the same instance. The simple aliasing mechanism allows the context to be asked
* for by a name which is appropriate for (or describes) the user. The deployer can
* match alias names to actual context names.
*
* <p>Another use of SingletonBeanFactoryLocator, is to demand-load/use one or more
* BeanFactories/ApplicationContexts. Because the definition can contain one of more
* BeanFactories/ApplicationContexts, which can be independent or in a hierarchy, if
* they are set to lazy-initialize, they will only be created when actually requested
* for use.
*
* <p>Given the above-mentioned three ApplicationContexts, consider the simplest
* SingletonBeanFactoryLocator usage scenario, where there is only one single
* <code>beanRefFactory.xml</code> definition file:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
*
* &lt;bean id="com.mycompany.myapp"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>
* &lt;value>com/mycompany/myapp/util/applicationContext.xml&lt;/value>
* &lt;value>com/mycompany/myapp/dataaccess/applicationContext.xml&lt;/value>
* &lt;value>com/mycompany/myapp/dataaccess/services.xml&lt;/value>
* &lt;/list>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;/beans>
* </pre>
*
* The client code is as simple as:
*
* <pre class="code">
* BeanFactoryLocator bfl = SingletonBeanFactoryLocator.getInstance();
* BeanFactoryReference bf = bfl.useBeanFactory("com.mycompany.myapp");
* // now use some bean from factory
* MyClass zed = bf.getFactory().getBean("mybean");
* </pre>
*
* Another relatively simple variation of the <code>beanRefFactory.xml</code> definition file could be:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
*
* &lt;bean id="com.mycompany.myapp.util" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;value>com/mycompany/myapp/util/applicationContext.xml&lt;/value>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;!-- child of above -->
* &lt;bean id="com.mycompany.myapp.dataaccess" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>&lt;value>com/mycompany/myapp/dataaccess/applicationContext.xml&lt;/value>&lt;/list>
* &lt;/constructor-arg>
* &lt;constructor-arg>
* &lt;ref bean="com.mycompany.myapp.util"/>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;!-- child of above -->
* &lt;bean id="com.mycompany.myapp.services" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>&lt;value>com/mycompany/myapp/dataaccess.services.xml&lt;/value>&lt;/value>
* &lt;/constructor-arg>
* &lt;constructor-arg>
* &lt;ref bean="com.mycompany.myapp.dataaccess"/>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;!-- define an alias -->
* &lt;bean id="com.mycompany.myapp.mypackage"
* class="java.lang.String">
* &lt;constructor-arg>
* &lt;value>com.mycompany.myapp.services&lt;/value>
* &lt;/constructor-arg>
* &lt;/bean>
*
* &lt;/beans>
* </pre>
*
* <p>In this example, there is a hierarchy of three contexts created. The (potential)
* advantage is that if the lazy flag is set to true, a context will only be created
* if it's actually used. If there is some code that is only needed some of the time,
* this mechanism can save some resources. Additionally, an alias to the last context
* has been created. Aliases allow usage of the idiom where client code asks for a
* context with an id which represents the package or module the code is in, and the
* actual definition file(s) for the SingletonBeanFactoryLocator maps that id to
* a real context id.
*
* <p>A final example is more complex, with a <code>beanRefFactory.xml</code> for every module.
* All the files are automatically combined to create the final definition.
*
* <p><code>beanRefFactory.xml</code> file inside jar for util module:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
* &lt;bean id="com.mycompany.myapp.util" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;value>com/mycompany/myapp/util/applicationContext.xml&lt;/value>
* &lt;/constructor-arg>
* &lt;/bean>
* &lt;/beans>
* </pre>
*
* <code>beanRefFactory.xml</code> file inside jar for data-access module:<br>
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
* &lt;!-- child of util -->
* &lt;bean id="com.mycompany.myapp.dataaccess" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>&lt;value>com/mycompany/myapp/dataaccess/applicationContext.xml&lt;/value>&lt;/list>
* &lt;/constructor-arg>
* &lt;constructor-arg>
* &lt;ref bean="com.mycompany.myapp.util"/>
* &lt;/constructor-arg>
* &lt;/bean>
* &lt;/beans>
* </pre>
*
* <code>beanRefFactory.xml</code> file inside jar for services module:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
* &lt;!-- child of data-access -->
* &lt;bean id="com.mycompany.myapp.services" lazy-init="true"
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
* &lt;constructor-arg>
* &lt;list>&lt;value>com/mycompany/myapp/dataaccess/services.xml&lt;/value>&lt;/list>
* &lt;/constructor-arg>
* &lt;constructor-arg>
* &lt;ref bean="com.mycompany.myapp.dataaccess"/>
* &lt;/constructor-arg>
* &lt;/bean>
* &lt;/beans>
* </pre>
*
* <code>beanRefFactory.xml</code> file inside jar for mypackage module. This doesn't
* create any of its own contexts, but allows the other ones to be referred to be
* a name known to this module:
*
* <pre class="code">&lt;?xml version="1.0" encoding="UTF-8"?>
* &lt;!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
*
* &lt;beans>
* &lt;!-- define an alias for "com.mycompany.myapp.services" -->
* &lt;alias name="com.mycompany.myapp.services" alias="com.mycompany.myapp.mypackage"/&gt;
* &lt;/beans>
* </pre>
*
* @author Colin Sampaleanu
* @author Juergen Hoeller
* @see org.springframework.context.access.ContextSingletonBeanFactoryLocator
* @see org.springframework.context.access.DefaultLocatorFactory
*/
public class SingletonBeanFactoryLocator implements BeanFactoryLocator {
private static final String DEFAULT_RESOURCE_LOCATION = "classpath*:beanRefFactory.xml";
protected static final Log logger = LogFactory.getLog(SingletonBeanFactoryLocator.class);
/** The keyed BeanFactory instances */
private static final Map<String, BeanFactoryLocator> instances = new HashMap<String, BeanFactoryLocator>();
/**
* Returns an instance which uses the default "classpath*:beanRefFactory.xml",
* as the name of the definition file(s). All resources returned by calling the
* current thread context ClassLoader's <code>getResources</code> method with
* this name will be combined to create a BeanFactory definition set.
* @return the corresponding BeanFactoryLocator instance
* @throws BeansException in case of factory loading failure
*/
public static BeanFactoryLocator getInstance() throws BeansException {
return getInstance(null);
}
/**
* Returns an instance which uses the the specified selector, as the name of the
* definition file(s). In the case of a name with a Spring 'classpath*:' prefix,
* or with no prefix, which is treated the same, the current thread context
* ClassLoader's <code>getResources</code> method will be called with this value
* to get all resources having that name. These resources will then be combined to
* form a definition. In the case where the name uses a Spring 'classpath:' prefix,
* or a standard URL prefix, then only one resource file will be loaded as the
* definition.
* @param selector the name of the resource(s) which will be read and
* combined to form the definition for the BeanFactoryLocator instance.
* Any such files must form a valid BeanFactory definition.
* @return the corresponding BeanFactoryLocator instance
* @throws BeansException in case of factory loading failure
*/
public static BeanFactoryLocator getInstance(String selector) throws BeansException {
String resourceLocation = selector;
if (resourceLocation == null) {
resourceLocation = DEFAULT_RESOURCE_LOCATION;
}
// For backwards compatibility, we prepend 'classpath*:' to the selector name if there
// is no other prefix (i.e. classpath*:, classpath:, or some URL prefix.
if (!ResourcePatternUtils.isUrl(resourceLocation)) {
resourceLocation = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resourceLocation;
}
synchronized (instances) {
if (logger.isTraceEnabled()) {
logger.trace("SingletonBeanFactoryLocator.getInstance(): instances.hashCode=" +
instances.hashCode() + ", instances=" + instances);
}
BeanFactoryLocator bfl = instances.get(resourceLocation);
if (bfl == null) {
bfl = new SingletonBeanFactoryLocator(resourceLocation);
instances.put(resourceLocation, bfl);
}
return bfl;
}
}
// We map BeanFactoryGroup objects by String keys, and by the definition object.
private final Map<String, BeanFactoryGroup> bfgInstancesByKey = new HashMap<String, BeanFactoryGroup>();
private final Map<BeanFactory, BeanFactoryGroup> bfgInstancesByObj = new HashMap<BeanFactory, BeanFactoryGroup>();
private final String resourceLocation;
/**
* Constructor which uses the the specified name as the resource name
* of the definition file(s).
* @param resourceLocation the Spring resource location to use
* (either a URL or a "classpath:" / "classpath*:" pseudo URL)
*/
protected SingletonBeanFactoryLocator(String resourceLocation) {
this.resourceLocation = resourceLocation;
}
public BeanFactoryReference useBeanFactory(String factoryKey) throws BeansException {
synchronized (this.bfgInstancesByKey) {
BeanFactoryGroup bfg = this.bfgInstancesByKey.get(this.resourceLocation);
if (bfg != null) {
bfg.refCount++;
}
else {
// This group definition doesn't exist, we need to try to load it.
if (logger.isTraceEnabled()) {
logger.trace("Factory group with resource name [" + this.resourceLocation +
"] requested. Creating new instance.");
}
// Create the BeanFactory but don't initialize it.
BeanFactory groupContext = createDefinition(this.resourceLocation, factoryKey);
// Record its existence now, before instantiating any singletons.
bfg = new BeanFactoryGroup();
bfg.definition = groupContext;
bfg.refCount = 1;
this.bfgInstancesByKey.put(this.resourceLocation, bfg);
this.bfgInstancesByObj.put(groupContext, bfg);
// Now initialize the BeanFactory. This may cause a re-entrant invocation
// of this method, but since we've already added the BeanFactory to our
// mappings, the next time it will be found and simply have its
// reference count incremented.
try {
initializeDefinition(groupContext);
}
catch (BeansException ex) {
this.bfgInstancesByKey.remove(this.resourceLocation);
this.bfgInstancesByObj.remove(groupContext);
throw new BootstrapException("Unable to initialize group definition. " +
"Group resource name [" + this.resourceLocation + "], factory key [" + factoryKey + "]", ex);
}
}
try {
BeanFactory beanFactory;
if (factoryKey != null) {
beanFactory = bfg.definition.getBean(factoryKey, BeanFactory.class);
}
else {
beanFactory = bfg.definition.getBean(BeanFactory.class);
}
return new CountingBeanFactoryReference(beanFactory, bfg.definition);
}
catch (BeansException ex) {
throw new BootstrapException("Unable to return specified BeanFactory instance: factory key [" +
factoryKey + "], from group with resource name [" + this.resourceLocation + "]", ex);
}
}
}
/**
* Actually creates definition in the form of a BeanFactory, given a resource name
* which supports standard Spring resource prefixes ('classpath:', 'classpath*:', etc.)
* This is split out as a separate method so that subclasses can override the actual
* type used (to be an ApplicationContext, for example).
* <p>The default implementation simply builds a
* {@link org.springframework.beans.factory.support.DefaultListableBeanFactory}
* and populates it using an
* {@link org.springframework.beans.factory.xml.XmlBeanDefinitionReader}.
* <p>This method should not instantiate any singletons. That function is performed
* by {@link #initializeDefinition initializeDefinition()}, which should also be
* overridden if this method is.
* @param resourceLocation the resource location for this factory group
* @param factoryKey the bean name of the factory to obtain
* @return the corresponding BeanFactory reference
*/
protected BeanFactory createDefinition(String resourceLocation, String factoryKey) {
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
try {
Resource[] configResources = resourcePatternResolver.getResources(resourceLocation);
if (configResources.length == 0) {
throw new FatalBeanException("Unable to find resource for specified definition. " +
"Group resource name [" + this.resourceLocation + "], factory key [" + factoryKey + "]");
}
reader.loadBeanDefinitions(configResources);
}
catch (IOException ex) {
throw new BeanDefinitionStoreException(
"Error accessing bean definition resource [" + this.resourceLocation + "]", ex);
}
catch (BeanDefinitionStoreException ex) {
throw new FatalBeanException("Unable to load group definition: " +
"group resource name [" + this.resourceLocation + "], factory key [" + factoryKey + "]", ex);
}
return factory;
}
/**
* Instantiate singletons and do any other normal initialization of the factory.
* Subclasses that override {@link #createDefinition createDefinition()} should
* also override this method.
* @param groupDef the factory returned by {@link #createDefinition createDefinition()}
*/
protected void initializeDefinition(BeanFactory groupDef) {
if (groupDef instanceof ConfigurableListableBeanFactory) {
((ConfigurableListableBeanFactory) groupDef).preInstantiateSingletons();
}
}
/**
* Destroy definition in separate method so subclass may work with other definition types.
* @param groupDef the factory returned by {@link #createDefinition createDefinition()}
* @param selector the resource location for this factory group
*/
protected void destroyDefinition(BeanFactory groupDef, String selector) {
if (groupDef instanceof ConfigurableBeanFactory) {
if (logger.isTraceEnabled()) {
logger.trace("Factory group with selector '" + selector +
"' being released, as there are no more references to it");
}
((ConfigurableBeanFactory) groupDef).destroySingletons();
}
}
/**
* We track BeanFactory instances with this class.
*/
private static class BeanFactoryGroup {
private BeanFactory definition;
private int refCount = 0;
}
/**
* BeanFactoryReference implementation for this locator.
*/
private class CountingBeanFactoryReference implements BeanFactoryReference {
private BeanFactory beanFactory;
private BeanFactory groupContextRef;
public CountingBeanFactoryReference(BeanFactory beanFactory, BeanFactory groupContext) {
this.beanFactory = beanFactory;
this.groupContextRef = groupContext;
}
public BeanFactory getFactory() {
return this.beanFactory;
}
// Note that it's legal to call release more than once!
public void release() throws FatalBeanException {
synchronized (bfgInstancesByKey) {
BeanFactory savedRef = this.groupContextRef;
if (savedRef != null) {
this.groupContextRef = null;
BeanFactoryGroup bfg = bfgInstancesByObj.get(savedRef);
if (bfg != null) {
bfg.refCount--;
if (bfg.refCount == 0) {
destroyDefinition(savedRef, resourceLocation);
bfgInstancesByKey.remove(resourceLocation);
bfgInstancesByObj.remove(savedRef);
}
}
else {
// This should be impossible.
logger.warn("Tried to release a SingletonBeanFactoryLocator group definition " +
"more times than it has actually been used. Resource name [" + resourceLocation + "]");
}
}
}
}
}
}

View File

@@ -0,0 +1,50 @@
/*
* 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.access.el;
import javax.el.ELContext;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.util.Assert;
/**
* Simple concrete variant of {@link SpringBeanELResolver}, delegating
* to a given {@link BeanFactory} that the resolver was constructed with.
*
* @author Juergen Hoeller
* @since 2.5.2
*/
public class SimpleSpringBeanELResolver extends SpringBeanELResolver {
private final BeanFactory beanFactory;
/**
* Create a new SimpleSpringBeanELResolver for the given BeanFactory.
* @param beanFactory the Spring BeanFactory to delegate to
*/
public SimpleSpringBeanELResolver(BeanFactory beanFactory) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.beanFactory = beanFactory;
}
@Override
protected BeanFactory getBeanFactory(ELContext elContext) {
return this.beanFactory;
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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.access.el;
import java.beans.FeatureDescriptor;
import java.util.Iterator;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.ELResolver;
import javax.el.PropertyNotWritableException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanFactory;
/**
* Unified EL <code>ELResolver</code> that delegates to a Spring BeanFactory,
* resolving name references to Spring-defined beans.
*
* @author Juergen Hoeller
* @since 2.5.2
* @see org.springframework.web.jsf.el.SpringBeanFacesELResolver
*/
public abstract class SpringBeanELResolver extends ELResolver {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
@Override
public Object getValue(ELContext elContext, Object base, Object property) throws ELException {
if (base == null) {
String beanName = property.toString();
BeanFactory bf = getBeanFactory(elContext);
if (bf.containsBean(beanName)) {
if (logger.isTraceEnabled()) {
logger.trace("Successfully resolved variable '" + beanName + "' in Spring BeanFactory");
}
elContext.setPropertyResolved(true);
return bf.getBean(beanName);
}
}
return null;
}
@Override
public Class<?> getType(ELContext elContext, Object base, Object property) throws ELException {
if (base == null) {
String beanName = property.toString();
BeanFactory bf = getBeanFactory(elContext);
if (bf.containsBean(beanName)) {
elContext.setPropertyResolved(true);
return bf.getType(beanName);
}
}
return null;
}
@Override
public void setValue(ELContext elContext, Object base, Object property, Object value) throws ELException {
if (base == null) {
String beanName = property.toString();
BeanFactory bf = getBeanFactory(elContext);
if (bf.containsBean(beanName)) {
throw new PropertyNotWritableException(
"Variable '" + beanName + "' refers to a Spring bean which by definition is not writable");
}
}
}
@Override
public boolean isReadOnly(ELContext elContext, Object base, Object property) throws ELException {
if (base == null) {
String beanName = property.toString();
BeanFactory bf = getBeanFactory(elContext);
if (bf.containsBean(beanName)) {
return true;
}
}
return false;
}
@Override
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, Object base) {
return null;
}
@Override
public Class<?> getCommonPropertyType(ELContext elContext, Object base) {
return Object.class;
}
/**
* Retrieve the Spring BeanFactory to delegate bean name resolution to.
* @param elContext the current ELContext
* @return the Spring BeanFactory (never <code>null</code>)
*/
protected abstract BeanFactory getBeanFactory(ELContext elContext);
}

View File

@@ -0,0 +1,8 @@
/**
*
* Support classes for accessing a Spring BeanFactory from Unified EL.
*
*/
package org.springframework.beans.factory.access.el;

View File

@@ -0,0 +1,12 @@
/**
*
* Helper infrastructure to locate and access bean factories.
*
* <p><b>Note: This package is only relevant for special sharing of bean
* factories, for example behind EJB facades. It is <i>not</i> used in a
* typical web application or standalone application.</b>
*
*/
package org.springframework.beans.factory.access;

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2002-2007 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 org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.core.type.AnnotationMetadata;
/**
* Extended {@link org.springframework.beans.factory.config.BeanDefinition}
* interface that exposes {@link org.springframework.core.type.AnnotationMetadata}
* about its bean class - without requiring the class to be loaded yet.
*
* @author Juergen Hoeller
* @since 2.5
* @see AnnotatedGenericBeanDefinition
* @see org.springframework.core.type.AnnotationMetadata
*/
public interface AnnotatedBeanDefinition extends BeanDefinition {
/**
* Obtain the annotation metadata (as well as basic class metadata)
* for this bean definition's bean class.
* @return the annotation metadata object (never <code>null</code>)
*/
AnnotationMetadata getMetadata();
}

View File

@@ -0,0 +1,58 @@
/*
* 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 org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.StandardAnnotationMetadata;
/**
* Extension of the {@link org.springframework.beans.factory.support.GenericBeanDefinition}
* class, adding support for annotation metadata exposed through the
* {@link AnnotatedBeanDefinition} interface.
*
* <p>This GenericBeanDefinition variant is mainly useful for testing code that expects
* to operate on an AnnotatedBeanDefinition, for example strategy implementations
* in Spring's component scanning support (where the default definition class is
* {@link org.springframework.context.annotation.ScannedGenericBeanDefinition},
* which also implements the AnnotatedBeanDefinition interface).
*
* @author Juergen Hoeller
* @since 2.5
* @see AnnotatedBeanDefinition#getMetadata()
* @see org.springframework.core.type.StandardAnnotationMetadata
*/
public class AnnotatedGenericBeanDefinition extends GenericBeanDefinition implements AnnotatedBeanDefinition {
private final AnnotationMetadata annotationMetadata;
/**
* Create a new AnnotatedGenericBeanDefinition for the given bean class.
* @param beanClass the loaded bean class
*/
public AnnotatedGenericBeanDefinition(Class beanClass) {
setBeanClass(beanClass);
this.annotationMetadata = new StandardAnnotationMetadata(beanClass);
}
public final AnnotationMetadata getMetadata() {
return this.annotationMetadata;
}
}

View File

@@ -0,0 +1,79 @@
/*
* Copyright 2002-2007 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 org.springframework.beans.factory.wiring.BeanWiringInfo;
import org.springframework.beans.factory.wiring.BeanWiringInfoResolver;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* {@link org.springframework.beans.factory.wiring.BeanWiringInfoResolver} that
* uses the Configurable annotation to identify which classes need autowiring.
* The bean name to look up will be taken from the {@link Configurable} annotation
* if specified; otherwise the default will be the fully-qualified name of the
* class being configured.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see Configurable
* @see org.springframework.beans.factory.wiring.ClassNameBeanWiringInfoResolver
*/
public class AnnotationBeanWiringInfoResolver implements BeanWiringInfoResolver {
public BeanWiringInfo resolveWiringInfo(Object beanInstance) {
Assert.notNull(beanInstance, "Bean instance must not be null");
Configurable annotation = beanInstance.getClass().getAnnotation(Configurable.class);
return (annotation != null ? buildWiringInfo(beanInstance, annotation) : null);
}
/**
* Build the BeanWiringInfo for the given Configurable annotation.
* @param beanInstance the bean instance
* @param annotation the Configurable annotation found on the bean class
* @return the resolved BeanWiringInfo
*/
protected BeanWiringInfo buildWiringInfo(Object beanInstance, Configurable annotation) {
if (!Autowire.NO.equals(annotation.autowire())) {
return new BeanWiringInfo(annotation.autowire().value(), annotation.dependencyCheck());
}
else {
if (!"".equals(annotation.value())) {
// explicitly specified bean name
return new BeanWiringInfo(annotation.value(), false);
}
else {
// default bean name
return new BeanWiringInfo(getDefaultBeanName(beanInstance), true);
}
}
}
/**
* Determine the default bean name for the specified bean instance.
* <p>The default implementation returns the superclass name for a CGLIB
* proxy and the name of the plain bean class else.
* @param beanInstance the bean instance to build a default name for
* @return the default bean name to use
* @see org.springframework.util.ClassUtils#getUserClass(Class)
*/
protected String getDefaultBeanName(Object beanInstance) {
return ClassUtils.getUserClass(beanInstance).getName();
}
}

View File

@@ -0,0 +1,73 @@
/*
* 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.annotation;
import org.springframework.beans.factory.config.AutowireCapableBeanFactory;
/**
* Enumeration determining autowiring status: that is, whether a bean should
* have its dependencies automatically injected by the Spring container using
* setter injection. This is a core concept in Spring DI.
*
* <p>Available for use in annotation-based configurations, such as for the
* AspectJ AnnotationBeanConfigurer aspect.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @since 2.0
* @see org.springframework.beans.factory.annotation.Configurable
* @see org.springframework.beans.factory.config.AutowireCapableBeanFactory
*/
public enum Autowire {
/**
* Constant that indicates no autowiring at all.
*/
NO(AutowireCapableBeanFactory.AUTOWIRE_NO),
/**
* Constant that indicates autowiring bean properties by name.
*/
BY_NAME(AutowireCapableBeanFactory.AUTOWIRE_BY_NAME),
/**
* Constant that indicates autowiring bean properties by type.
*/
BY_TYPE(AutowireCapableBeanFactory.AUTOWIRE_BY_TYPE);
private final int value;
Autowire(int value) {
this.value = value;
}
public int value() {
return this.value;
}
/**
* Return whether this represents an actual autowiring value.
* @return whether actual autowiring was specified
* (either BY_NAME or BY_TYPE)
*/
public boolean isAutowire() {
return (this == BY_NAME || this == BY_TYPE);
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2002-2011 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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a constructor, field, setter method or config method as to be
* autowired by Spring's dependency injection facilities.
*
* <p>Only one constructor (at max) of any given bean class may carry this
* annotation, indicating the constructor to autowire when used as a Spring
* bean. Such a 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>In the case of multiple argument methods, the 'required' parameter is
* applicable for all arguments.
*
* <p>In case of a {@link java.util.Collection} or {@link java.util.Map}
* dependency type, the container will autowire all beans matching the
* declared value type. In case of a Map, the keys must be declared as
* type String and will be resolved to the corresponding bean names.
*
* <p>Note that actual injection is performed through a
* {@link org.springframework.beans.factory.config.BeanPostProcessor
* BeanPostProcessor} which in turn means that you <em>cannot</em>
* use {@code @Autowired} to inject references into
* {@link org.springframework.beans.factory.config.BeanPostProcessor
* BeanPostProcessor} or {@link BeanFactoryPostProcessor} types. Please
* consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor}
* class (which, by default, checks for the presence of this annotation).
*
* @author Juergen Hoeller
* @author Mark Fisher
* @since 2.5
* @see AutowiredAnnotationBeanPostProcessor
* @see Qualifier
* @see Value
*/
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
/**
* Declares whether the annotated dependency is required.
* <p>Defaults to <code>true</code>.
*/
boolean required() default true;
}

View File

@@ -0,0 +1,609 @@
/*
* Copyright 2002-2011 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.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
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.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.BridgeMethodResolver;
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 @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</code>.
*
* <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. 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.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @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<Class<? extends Annotation>>();
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>();
/**
* 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);
ClassLoader cl = AutowiredAnnotationBeanPostProcessor.class.getClassLoader();
try {
this.autowiredAnnotationTypes.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Inject"));
logger.info("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.
* <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.
* <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 #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);
}
}
@Override
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 (Constructor<?> candidate : rawCandidates) {
Annotation annotation = findAutowiredAnnotation(candidate);
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 = candidates.toArray(new Constructor[candidates.size()]);
}
else {
candidateConstructors = new Constructor[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
try {
metadata.inject(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies 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
* @throws BeansException if autowiring failed
*/
public void processInjection(Object bean) throws BeansException {
Class<?> clazz = bean.getClass();
InjectionMetadata metadata = findAutowiringMetadata(clazz);
try {
metadata.inject(bean, null, null);
}
catch (Throwable ex) {
throw new BeanCreationException("Injection of autowired dependencies failed for class [" + clazz + "]", ex);
}
}
private InjectionMetadata findAutowiringMetadata(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) {
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(clazz, metadata);
}
}
}
return metadata;
}
private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {
LinkedList<InjectionMetadata.InjectedElement> elements = new LinkedList<InjectionMetadata.InjectedElement>();
Class<?> targetClass = clazz;
do {
LinkedList<InjectionMetadata.InjectedElement> currElements = new LinkedList<InjectionMetadata.InjectedElement>();
for (Field field : targetClass.getDeclaredFields()) {
Annotation annotation = findAutowiredAnnotation(field);
if (annotation != null) {
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields: " + field);
}
continue;
}
boolean required = determineRequiredStatus(annotation);
currElements.add(new AutowiredFieldElement(field, required));
}
}
for (Method method : targetClass.getDeclaredMethods()) {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
Annotation annotation = BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod) ?
findAutowiredAnnotation(bridgedMethod) : findAutowiredAnnotation(method);
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods: " + method);
}
continue;
}
if (method.getParameterTypes().length == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should be used on methods with actual parameters: " + method);
}
}
boolean required = determineRequiredStatus(annotation);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
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);
}
private Annotation findAutowiredAnnotation(AccessibleObject ao) {
for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {
Annotation annotation = ao.getAnnotation(type);
if (annotation != null) {
return annotation;
}
}
return null;
}
/**
* 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);
}
/**
* 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 (String autowiredBeanName : autowiredBeanNames) {
beanFactory.registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug(
"Autowiring by type from bean name '" + beanName + "' to bean named '" + autowiredBeanName +
"'");
}
}
}
}
/**
* Resolve the specified cached method argument or field value.
*/
private Object resolvedCachedArgument(String beanName, Object cachedArgument) {
if (cachedArgument instanceof DependencyDescriptor) {
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArgument;
TypeConverter typeConverter = beanFactory.getTypeConverter();
return beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
}
else if (cachedArgument instanceof RuntimeBeanReference) {
return beanFactory.getBean(((RuntimeBeanReference) cachedArgument).getBeanName());
}
else {
return cachedArgument;
}
}
/**
* 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;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required);
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
synchronized (this) {
if (!this.cached) {
if (value != null || this.required) {
this.cachedFieldValue = descriptor;
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 (checkPropertySkipping(pvs)) {
return;
}
Method method = (Method) this.member;
try {
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> autowiredBeanNames = new LinkedHashSet<String>(paramTypes.length);
TypeConverter typeConverter = beanFactory.getTypeConverter();
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
GenericTypeResolver.resolveParameterType(methodParam, bean.getClass());
descriptors[i] = new DependencyDescriptor(methodParam, this.required);
arguments[i] = beanFactory.resolveDependency(
descriptors[i], beanName, autowiredBeanNames, typeConverter);
if (arguments[i] == null && !this.required) {
arguments = null;
break;
}
}
synchronized (this) {
if (!this.cached) {
if (arguments != null) {
this.cachedMethodArguments = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
this.cachedMethodArguments[i] = descriptors[i];
}
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 = null;
}
this.cached = true;
}
}
}
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);
}
}
private Object[] resolveCachedArguments(String beanName) {
if (this.cachedMethodArguments == null) {
return null;
}
Object[] arguments = new Object[this.cachedMethodArguments.length];
for (int i = 0; i < arguments.length; i++) {
arguments[i] = resolvedCachedArgument(beanName, this.cachedMethodArguments[i]);
}
return arguments;
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* Copyright 2002-2011 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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a class as being eligible for Spring-driven configuration.
*
* <p>Typically used with the AspectJ <code>AnnotationBeanConfigurerAspect</code>.
*
* @author Rod Johnson
* @author Rob Harrop
* @author Adrian Colyer
* @author Ramnivas Laddad
* @since 2.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Configurable {
/**
* The name of the bean definition that serves as the configuration template.
*/
String value() default "";
/**
* Are dependencies to be injected via autowiring?
*/
Autowire autowire() default Autowire.NO;
/**
* Is dependency checking to be performed for configured objects?
*/
boolean dependencyCheck() default false;
/**
* Are dependencies to be injected prior to the construction of an object?
*/
boolean preConstruction() default false;
}

View File

@@ -0,0 +1,120 @@
/*
* 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.lang.annotation.Annotation;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.Ordered;
import org.springframework.util.ClassUtils;
/**
* A {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor}
* implementation that allows for convenient registration of custom autowire
* qualifier types.
*
* <pre class="code">
* &lt;bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer"&gt;
* &lt;property name="customQualifierTypes"&gt;
* &lt;set&gt;
* &lt;value&gt;mypackage.MyQualifier&lt;/value&gt;
* &lt;/set&gt;
* &lt;/property&gt;
* &lt;/bean&gt;</pre>
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
* @see org.springframework.beans.factory.annotation.Qualifier
*/
public class CustomAutowireConfigurer implements BeanFactoryPostProcessor, BeanClassLoaderAware, Ordered {
private int order = Ordered.LOWEST_PRECEDENCE; // default: same as non-Ordered
private Set customQualifierTypes;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return this.order;
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
/**
* Register custom qualifier annotation types to be considered
* when autowiring beans. Each element of the provided set may
* be either a Class instance or a String representation of the
* fully-qualified class name of the custom annotation.
* <p>Note that any annotation that is itself annotated with Spring's
* {@link org.springframework.beans.factory.annotation.Qualifier}
* does not require explicit registration.
* @param customQualifierTypes the custom types to register
*/
public void setCustomQualifierTypes(Set customQualifierTypes) {
this.customQualifierTypes = customQualifierTypes;
}
@SuppressWarnings("unchecked")
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.customQualifierTypes != null) {
if (!(beanFactory instanceof DefaultListableBeanFactory)) {
throw new IllegalStateException(
"CustomAutowireConfigurer needs to operate on a DefaultListableBeanFactory");
}
DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;
if (!(dlbf.getAutowireCandidateResolver() instanceof QualifierAnnotationAutowireCandidateResolver)) {
dlbf.setAutowireCandidateResolver(new QualifierAnnotationAutowireCandidateResolver());
}
QualifierAnnotationAutowireCandidateResolver resolver =
(QualifierAnnotationAutowireCandidateResolver) dlbf.getAutowireCandidateResolver();
for (Object value : this.customQualifierTypes) {
Class customType = null;
if (value instanceof Class) {
customType = (Class) value;
}
else if (value instanceof String) {
String className = (String) value;
customType = ClassUtils.resolveClassName(className, this.beanClassLoader);
}
else {
throw new IllegalArgumentException(
"Invalid value [" + value + "] for custom qualifier type: needs to be Class or String.");
}
if (!Annotation.class.isAssignableFrom(customType)) {
throw new IllegalArgumentException(
"Qualifier type [" + customType.getName() + "] needs to be annotation type");
}
resolver.addQualifierType(customType);
}
}
}
}

View File

@@ -0,0 +1,367 @@
/*
* Copyright 2002-2011 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.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
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.BeansException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.config.DestructionAwareBeanPostProcessor;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.util.ReflectionUtils;
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that invokes annotated init and destroy methods. Allows for an annotation
* alternative to Spring's {@link org.springframework.beans.factory.InitializingBean}
* and {@link org.springframework.beans.factory.DisposableBean} callback interfaces.
*
* <p>The actual annotation types that this post-processor checks for can be
* configured through the {@link #setInitAnnotationType "initAnnotationType"}
* and {@link #setDestroyAnnotationType "destroyAnnotationType"} properties.
* Any custom annotation can be used, since there are no required annotation
* attributes.
*
* <p>Init and destroy annotations may be applied to methods of any visibility:
* public, package-protected, protected, or private. Multiple such methods
* may be annotated, but it is recommended to only annotate one single
* init method and destroy method, respectively.
*
* <p>Spring's {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor}
* supports the JSR-250 {@link javax.annotation.PostConstruct} and {@link javax.annotation.PreDestroy}
* annotations out of the box, as init annotation and destroy annotation, respectively.
* Furthermore, it also supports the {@link javax.annotation.Resource} annotation
* for annotation-driven injection of named beans.
*
* @author Juergen Hoeller
* @since 2.5
* @see #setInitAnnotationType
* @see #setDestroyAnnotationType
*/
@SuppressWarnings("serial")
public class InitDestroyAnnotationBeanPostProcessor
implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {
protected transient Log logger = LogFactory.getLog(getClass());
private Class<? extends Annotation> initAnnotationType;
private Class<? extends Annotation> destroyAnnotationType;
private int order = Ordered.LOWEST_PRECEDENCE;
private transient final Map<Class<?>, LifecycleMetadata> lifecycleMetadataCache =
new ConcurrentHashMap<Class<?>, LifecycleMetadata>();
/**
* Specify the init annotation to check for, indicating initialization
* methods to call after configuration of a bean.
* <p>Any custom annotation can be used, since there are no required
* annotation attributes. There is no default, although a typical choice
* is the JSR-250 {@link javax.annotation.PostConstruct} annotation.
*/
public void setInitAnnotationType(Class<? extends Annotation> initAnnotationType) {
this.initAnnotationType = initAnnotationType;
}
/**
* Specify the destroy annotation to check for, indicating destruction
* methods to call when the context is shutting down.
* <p>Any custom annotation can be used, since there are no required
* annotation attributes. There is no default, although a typical choice
* is the JSR-250 {@link javax.annotation.PreDestroy} annotation.
*/
public void setDestroyAnnotationType(Class<? extends Annotation> destroyAnnotationType) {
this.destroyAnnotationType = destroyAnnotationType;
}
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return this.order;
}
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
if (beanType != null) {
LifecycleMetadata metadata = findLifecycleMetadata(beanType);
metadata.checkConfigMembers(beanDefinition);
}
}
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeInitMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
throw new BeanCreationException(beanName, "Invocation of init method failed", ex.getTargetException());
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Couldn't invoke init method", ex);
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
LifecycleMetadata metadata = findLifecycleMetadata(bean.getClass());
try {
metadata.invokeDestroyMethods(bean, beanName);
}
catch (InvocationTargetException ex) {
String msg = "Invocation of destroy method failed on bean with name '" + beanName + "'";
if (logger.isDebugEnabled()) {
logger.warn(msg, ex.getTargetException());
}
else {
logger.warn(msg + ": " + ex.getTargetException());
}
}
catch (Throwable ex) {
logger.error("Couldn't invoke destroy method on bean with name '" + beanName + "'", ex);
}
}
private LifecycleMetadata findLifecycleMetadata(Class<?> clazz) {
if (this.lifecycleMetadataCache == null) {
// Happens after deserialization, during destruction...
return buildLifecycleMetadata(clazz);
}
// Quick check on the concurrent map first, with minimal locking.
LifecycleMetadata metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
synchronized (this.lifecycleMetadataCache) {
metadata = this.lifecycleMetadataCache.get(clazz);
if (metadata == null) {
metadata = buildLifecycleMetadata(clazz);
this.lifecycleMetadataCache.put(clazz, metadata);
}
return metadata;
}
}
return metadata;
}
private LifecycleMetadata buildLifecycleMetadata(Class<?> clazz) {
final boolean debug = logger.isDebugEnabled();
LinkedList<LifecycleElement> initMethods = new LinkedList<LifecycleElement>();
LinkedList<LifecycleElement> destroyMethods = new LinkedList<LifecycleElement>();
Class<?> targetClass = clazz;
do {
LinkedList<LifecycleElement> currInitMethods = new LinkedList<LifecycleElement>();
LinkedList<LifecycleElement> currDestroyMethods = new LinkedList<LifecycleElement>();
for (Method method : targetClass.getDeclaredMethods()) {
if (this.initAnnotationType != null) {
if (method.getAnnotation(this.initAnnotationType) != null) {
LifecycleElement element = new LifecycleElement(method);
currInitMethods.add(element);
if (debug) {
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
}
if (this.destroyAnnotationType != null) {
if (method.getAnnotation(this.destroyAnnotationType) != null) {
currDestroyMethods.add(new LifecycleElement(method));
if (debug) {
logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
}
}
initMethods.addAll(0, currInitMethods);
destroyMethods.addAll(currDestroyMethods);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new LifecycleMetadata(clazz, initMethods, destroyMethods);
}
//---------------------------------------------------------------------
// Serialization support
//---------------------------------------------------------------------
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
// Rely on default serialization; just initialize state after deserialization.
ois.defaultReadObject();
// Initialize transient fields.
this.logger = LogFactory.getLog(getClass());
}
/**
* Class representing information about annotated init and destroy methods.
*/
private class LifecycleMetadata {
private final Set<LifecycleElement> initMethods;
private final Set<LifecycleElement> destroyMethods;
public LifecycleMetadata(Class<?> targetClass, Collection<LifecycleElement> initMethods,
Collection<LifecycleElement> destroyMethods) {
this.initMethods = Collections.synchronizedSet(new LinkedHashSet<LifecycleElement>());
for (LifecycleElement element : initMethods) {
if (logger.isDebugEnabled()) {
logger.debug("Found init method on class [" + targetClass.getName() + "]: " + element);
}
this.initMethods.add(element);
}
this.destroyMethods = Collections.synchronizedSet(new LinkedHashSet<LifecycleElement>());
for (LifecycleElement element : destroyMethods) {
if (logger.isDebugEnabled()) {
logger.debug("Found destroy method on class [" + targetClass.getName() + "]: " + element);
}
this.destroyMethods.add(element);
}
}
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
synchronized(this.initMethods) {
for (Iterator<LifecycleElement> it = this.initMethods.iterator(); it.hasNext();) {
String methodIdentifier = it.next().getIdentifier();
if (!beanDefinition.isExternallyManagedInitMethod(methodIdentifier)) {
beanDefinition.registerExternallyManagedInitMethod(methodIdentifier);
}
else {
it.remove();
}
}
}
synchronized(this.destroyMethods) {
for (Iterator<LifecycleElement> it = this.destroyMethods.iterator(); it.hasNext();) {
String methodIdentifier = it.next().getIdentifier();
if (!beanDefinition.isExternallyManagedDestroyMethod(methodIdentifier)) {
beanDefinition.registerExternallyManagedDestroyMethod(methodIdentifier);
}
else {
it.remove();
}
}
}
}
public void invokeInitMethods(Object target, String beanName) throws Throwable {
if (!this.initMethods.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (LifecycleElement element : this.initMethods) {
if (debug) {
logger.debug("Invoking init method on bean '" + beanName + "': " + element.getMethod());
}
element.invoke(target);
}
}
}
public void invokeDestroyMethods(Object target, String beanName) throws Throwable {
if (!this.destroyMethods.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (LifecycleElement element : this.destroyMethods) {
if (debug) {
logger.debug("Invoking destroy method on bean '" + beanName + "': " + element.getMethod());
}
element.invoke(target);
}
}
}
}
/**
* Class representing injection information about an annotated method.
*/
private static class LifecycleElement {
private final Method method;
private final String identifier;
public LifecycleElement(Method method) {
if (method.getParameterTypes().length != 0) {
throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method);
}
this.method = method;
this.identifier = (Modifier.isPrivate(method.getModifiers()) ?
method.getDeclaringClass() + "." + method.getName() : method.getName());
}
public Method getMethod() {
return this.method;
}
public String getIdentifier() {
return this.identifier;
}
public void invoke(Object target) throws Throwable {
ReflectionUtils.makeAccessible(this.method);
this.method.invoke(target, (Object[]) null);
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof LifecycleElement)) {
return false;
}
LifecycleElement otherElement = (LifecycleElement) other;
return (this.identifier.equals(otherElement.identifier));
}
@Override
public int hashCode() {
return this.identifier.hashCode();
}
}
}

View File

@@ -0,0 +1,225 @@
/*
* Copyright 2002-2011 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.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.util.ReflectionUtils;
/**
* Internal class for managing injection metadata.
* Not intended for direct use in applications.
*
* <p>Used by {@link AutowiredAnnotationBeanPostProcessor},
* {@link org.springframework.context.annotation.CommonAnnotationBeanPostProcessor} and
* {@link org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor}.
*
* @author Juergen Hoeller
* @since 2.5
*/
public class InjectionMetadata {
private final Log logger = LogFactory.getLog(InjectionMetadata.class);
private final Set<InjectedElement> injectedElements;
public InjectionMetadata(Class targetClass, Collection<InjectedElement> elements) {
this.injectedElements = Collections.synchronizedSet(new LinkedHashSet<InjectedElement>());
for (InjectedElement element : elements) {
if (logger.isDebugEnabled()) {
logger.debug("Found injected element on class [" + targetClass.getName() + "]: " + element);
}
this.injectedElements.add(element);
}
}
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
synchronized(this.injectedElements) {
for (Iterator<InjectedElement> it = this.injectedElements.iterator(); it.hasNext();) {
Member member = it.next().getMember();
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
beanDefinition.registerExternallyManagedConfigMember(member);
}
else {
it.remove();
}
}
}
}
public void inject(Object target, String beanName, PropertyValues pvs) throws Throwable {
if (!this.injectedElements.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (InjectedElement element : this.injectedElements) {
if (debug) {
logger.debug("Processing injected method of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, pvs);
}
}
}
public static abstract class InjectedElement {
protected final Member member;
protected final boolean isField;
protected final PropertyDescriptor pd;
protected volatile Boolean skip;
protected InjectedElement(Member member, PropertyDescriptor pd) {
this.member = member;
this.isField = (member instanceof Field);
this.pd = pd;
}
public final Member getMember() {
return this.member;
}
protected final Class getResourceType() {
if (this.isField) {
return ((Field) this.member).getType();
}
else if (this.pd != null) {
return this.pd.getPropertyType();
}
else {
return ((Method) this.member).getParameterTypes()[0];
}
}
protected final void checkResourceType(Class resourceType) {
if (this.isField) {
Class fieldType = ((Field) this.member).getType();
if (!(resourceType.isAssignableFrom(fieldType) || fieldType.isAssignableFrom(resourceType))) {
throw new IllegalStateException("Specified field type [" + fieldType +
"] is incompatible with resource type [" + resourceType.getName() + "]");
}
}
else {
Class paramType =
(this.pd != null ? this.pd.getPropertyType() : ((Method) this.member).getParameterTypes()[0]);
if (!(resourceType.isAssignableFrom(paramType) || paramType.isAssignableFrom(resourceType))) {
throw new IllegalStateException("Specified parameter type [" + paramType +
"] is incompatible with resource type [" + resourceType.getName() + "]");
}
}
}
/**
* Either this or {@link #getResourceToInject} needs to be overridden.
*/
protected void inject(Object target, String requestingBeanName, PropertyValues pvs) throws Throwable {
if (this.isField) {
Field field = (Field) this.member;
ReflectionUtils.makeAccessible(field);
field.set(target, getResourceToInject(target, requestingBeanName));
}
else {
if (checkPropertySkipping(pvs)) {
return;
}
try {
Method method = (Method) this.member;
ReflectionUtils.makeAccessible(method);
method.invoke(target, getResourceToInject(target, requestingBeanName));
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
/**
* Checks whether this injector's property needs to be skipped due to
* an explicit property value having been specified. Also marks the
* affected property as processed for other processors to ignore it.
*/
protected boolean checkPropertySkipping(PropertyValues pvs) {
if (this.skip == null) {
if (pvs != null) {
synchronized (pvs) {
if (this.skip == null) {
if (this.pd != null) {
if (pvs.contains(this.pd.getName())) {
// Explicit value provided as part of the bean definition.
this.skip = true;
return true;
}
else if (pvs instanceof MutablePropertyValues) {
((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
}
}
}
}
}
this.skip = false;
}
return this.skip;
}
/**
* Either this or {@link #inject} needs to be overridden.
*/
protected Object getResourceToInject(Object target, String requestingBeanName) {
return null;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof InjectedElement)) {
return false;
}
InjectedElement otherElement = (InjectedElement) other;
return this.member.equals(otherElement.member);
}
@Override
public int hashCode() {
return this.member.getClass().hashCode() * 29 + this.member.getName().hashCode();
}
@Override
public String toString() {
return getClass().getSimpleName() + " for " + this.member;
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-2011 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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* This annotation may be used on a field or parameter as a qualifier for
* candidate beans when autowiring. It may also be used to annotate other
* custom annotations that can then in turn be used as qualifiers.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
* @see Autowired
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}

View File

@@ -0,0 +1,299 @@
/*
* 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.annotation;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.MethodParameter;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* {@link AutowireCandidateResolver} implementation that matches bean definition qualifiers
* against {@link Qualifier qualifier annotations} on the field or parameter to be autowired.
* Also supports suggested expression values through a {@link Value value} annotation.
*
* <p>Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
* @see AutowireCandidateQualifier
* @see Qualifier
* @see Value
*/
public class QualifierAnnotationAutowireCandidateResolver implements AutowireCandidateResolver, BeanFactoryAware {
private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<Class<? extends Annotation>>();
private Class<? extends Annotation> valueAnnotationType = Value.class;
private BeanFactory beanFactory;
/**
* Create a new QualifierAnnotationAutowireCandidateResolver
* for Spring's standard {@link Qualifier} annotation.
* <p>Also supports JSR-330's {@link javax.inject.Qualifier} annotation, if available.
*/
@SuppressWarnings("unchecked")
public QualifierAnnotationAutowireCandidateResolver() {
this.qualifierTypes.add(Qualifier.class);
ClassLoader cl = QualifierAnnotationAutowireCandidateResolver.class.getClassLoader();
try {
this.qualifierTypes.add((Class<? extends Annotation>) cl.loadClass("javax.inject.Qualifier"));
}
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<? extends Annotation> qualifierType) {
Assert.notNull(qualifierType, "'qualifierType' must not be null");
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<Class<? extends Annotation>> qualifierTypes) {
Assert.notNull(qualifierTypes, "'qualifierTypes' must not be null");
this.qualifierTypes.addAll(qualifierTypes);
}
/**
* Register the given type to be used as a qualifier when autowiring.
* <p>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.
* <p>This implementation only supports annotations as qualifier types.
* The default is Spring'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<? extends Annotation> qualifierType) {
this.qualifierTypes.add(qualifierType);
}
/**
* Set the 'value' annotation type, to be used on fields, method parameters
* and constructor parameters.
* <p>The default value annotation type is the Spring-provided
* {@link Value} annotation.
* <p>This setter property exists so that developers can provide their own
* (non-Spring-specific) annotation type to indicate a default value
* expression for a specific argument.
*/
public void setValueAnnotationType(Class<? extends Annotation> valueAnnotationType) {
this.valueAnnotationType = valueAnnotationType;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* Determine whether the provided bean definition is an autowire candidate.
* <p>To be considered a candidate the bean's <em>autowire-candidate</em>
* 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 <em>qualifier</em>, the bean must 'match' against the annotation as
* well as any attributes it may contain. The bean definition 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
*/
public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
if (!bdHolder.getBeanDefinition().isAutowireCandidate()) {
// if explicitly false, do not proceed with qualifier check
return false;
}
if (descriptor == null) {
// no qualification necessary
return true;
}
boolean match = checkQualifiers(bdHolder, descriptor.getAnnotations());
if (match) {
MethodParameter methodParam = descriptor.getMethodParameter();
if (methodParam != null) {
Method method = methodParam.getMethod();
if (method == null || void.class.equals(method.getReturnType())) {
match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
}
}
}
return match;
}
/**
* Match the given qualifier annotations against the candidate bean definition.
*/
protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
if (ObjectUtils.isEmpty(annotationsToSearch)) {
return true;
}
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
for (Annotation annotation : annotationsToSearch) {
Class<? extends Annotation> type = annotation.annotationType();
if (isQualifier(type)) {
if (!checkQualifier(bdHolder, annotation, typeConverter)) {
return false;
}
}
}
return true;
}
/**
* Checks whether the given annotation type is a recognized qualifier type.
*/
protected boolean isQualifier(Class<? extends Annotation> annotationType) {
for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
return true;
}
}
return false;
}
/**
* Match the given qualifier annotation against the candidate bean definition.
*/
protected boolean checkQualifier(
BeanDefinitionHolder bdHolder, Annotation annotation, TypeConverter typeConverter) {
Class<? extends Annotation> 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) {
Annotation targetAnnotation = null;
if (bd.getResolvedFactoryMethod() != null) {
targetAnnotation = bd.getResolvedFactoryMethod().getAnnotation(type);
}
if (targetAnnotation == null) {
// look for matching annotation on the target class
if (this.beanFactory != null) {
Class<?> beanType = this.beanFactory.getType(bdHolder.getBeanName());
if (beanType != null) {
targetAnnotation = ClassUtils.getUserClass(beanType).getAnnotation(type);
}
}
if (targetAnnotation == null && bd.hasBeanClass()) {
targetAnnotation = ClassUtils.getUserClass(bd.getBeanClass()).getAnnotation(type);
}
}
if (targetAnnotation != null && targetAnnotation.equals(annotation)) {
return true;
}
}
Map<String, Object> attributes = AnnotationUtils.getAnnotationAttributes(annotation);
if (attributes.isEmpty() && qualifier == null) {
// if no attributes, the qualifier must be present
return false;
}
for (Map.Entry<String, Object> 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 (!expectedValue.equals(actualValue)) {
return false;
}
}
return true;
}
/**
* Determine whether the given dependency carries a value annotation.
* @see Value
*/
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.
*/
protected Object findValue(Annotation[] annotationsToSearch) {
for (Annotation annotation : annotationsToSearch) {
if (this.valueAnnotationType.isInstance(annotation)) {
Object value = AnnotationUtils.getValue(annotation);
if (value == null) {
throw new IllegalStateException("Value annotation must have a value attribute");
}
return value;
}
}
return null;
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright 2002-2007 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.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Marks a method (typically a JavaBean setter method) as being 'required': that is,
* the setter method must be configured to be dependency-injected with a value.
*
* <p>Please do consult the javadoc for the {@link RequiredAnnotationBeanPostProcessor}
* class (which, by default, checks for the presence of this annotation).
*
* @author Rob Harrop
* @since 2.0
* @see RequiredAnnotationBeanPostProcessor
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Required {
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright 2002-2011 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.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanInitializationException;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.Conventions;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.Assert;
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that enforces required JavaBean properties to have been configured.
* Required bean properties are detected through a Java 5 annotation:
* by default, Spring's {@link Required} annotation.
*
* <p>The motivation for the existence of this BeanPostProcessor is to allow
* developers to annotate the setter properties of their own classes with an
* arbitrary JDK 1.5 annotation to indicate that the container must check
* for the configuration of a dependency injected value. This neatly pushes
* responsibility for such checking onto the container (where it arguably belongs),
* and obviates the need (<b>in part</b>) for a developer to code a method that
* simply checks that all required properties have actually been set.
*
* <p>Please note that an 'init' method may still need to implemented (and may
* still be desirable), because all that this class does is enforce that a
* 'required' property has actually been configured with a value. It does
* <b>not</b> check anything else... In particular, it does not check that a
* configured value is not <code>null</code>.
*
* <p>Note: A default RequiredAnnotationBeanPostProcessor 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 RequiredAnnotationBeanPostProcessor bean definition.
*
* @author Rob Harrop
* @author Juergen Hoeller
* @since 2.0
* @see #setRequiredAnnotationType
* @see Required
*/
public class RequiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
/**
* Bean definition attribute that may indicate whether a given bean is supposed
* to be skipped when performing this post-processor's required property check.
* @see #shouldSkip
*/
public static final String SKIP_REQUIRED_CHECK_ATTRIBUTE =
Conventions.getQualifiedAttributeName(RequiredAnnotationBeanPostProcessor.class, "skipRequiredCheck");
private Class<? extends Annotation> requiredAnnotationType = Required.class;
private int order = Ordered.LOWEST_PRECEDENCE - 1;
private ConfigurableListableBeanFactory beanFactory;
/** Cache for validated bean names, skipping re-validation for the same bean */
private final Set<String> validatedBeanNames = Collections.synchronizedSet(new HashSet<String>());
/**
* Set the 'required' annotation type, to be used on bean property
* setter methods.
* <p>The default required annotation type is the Spring-provided
* {@link Required} annotation.
* <p>This setter property exists so that developers can provide their own
* (non-Spring-specific) annotation type to indicate that a property value
* is required.
*/
public void setRequiredAnnotationType(Class<? extends Annotation> requiredAnnotationType) {
Assert.notNull(requiredAnnotationType, "'requiredAnnotationType' must not be null");
this.requiredAnnotationType = requiredAnnotationType;
}
/**
* Return the 'required' annotation type.
*/
protected Class<? extends Annotation> getRequiredAnnotationType() {
return this.requiredAnnotationType;
}
public void setBeanFactory(BeanFactory beanFactory) {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return this.order;
}
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
}
@Override
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException {
if (!this.validatedBeanNames.contains(beanName)) {
if (!shouldSkip(this.beanFactory, beanName)) {
List<String> invalidProperties = new ArrayList<String>();
for (PropertyDescriptor pd : pds) {
if (isRequiredProperty(pd) && !pvs.contains(pd.getName())) {
invalidProperties.add(pd.getName());
}
}
if (!invalidProperties.isEmpty()) {
throw new BeanInitializationException(buildExceptionMessage(invalidProperties, beanName));
}
}
this.validatedBeanNames.add(beanName);
}
return pvs;
}
/**
* Check whether the given bean definition is not subject to the annotation-based
* required property check as performed by this post-processor.
* <p>The default implementations check for the presence of the
* {@link #SKIP_REQUIRED_CHECK_ATTRIBUTE} attribute in the bean definition, if any.
* @param beanFactory the BeanFactory to check against
* @param beanName the name of the bean to check against
* @return <code>true</code> to skip the bean; <code>false</code> to process it
*/
protected boolean shouldSkip(ConfigurableListableBeanFactory beanFactory, String beanName) {
if (beanFactory == null || !beanFactory.containsBeanDefinition(beanName)) {
return false;
}
Object value = beanFactory.getBeanDefinition(beanName).getAttribute(SKIP_REQUIRED_CHECK_ATTRIBUTE);
return (value != null && (Boolean.TRUE.equals(value) || Boolean.valueOf(value.toString())));
}
/**
* Is the supplied property required to have a value (that is, to be dependency-injected)?
* <p>This implementation looks for the existence of a
* {@link #setRequiredAnnotationType "required" annotation}
* on the supplied {@link PropertyDescriptor property}.
* @param propertyDescriptor the target PropertyDescriptor (never <code>null</code>)
* @return <code>true</code> if the supplied property has been marked as being required;
* <code>false</code> if not, or if the supplied property does not have a setter method
*/
protected boolean isRequiredProperty(PropertyDescriptor propertyDescriptor) {
Method setter = propertyDescriptor.getWriteMethod();
return (setter != null && AnnotationUtils.getAnnotation(setter, getRequiredAnnotationType()) != null);
}
/**
* Build an exception message for the given list of invalid properties.
* @param invalidProperties the list of names of invalid properties
* @param beanName the name of the bean
* @return the exception message
*/
private String buildExceptionMessage(List<String> invalidProperties, String beanName) {
int size = invalidProperties.size();
StringBuilder sb = new StringBuilder();
sb.append(size == 1 ? "Property" : "Properties");
for (int i = 0; i < size; i++) {
String propertyName = invalidProperties.get(i);
if (i > 0) {
if (i == (size - 1)) {
sb.append(" and");
}
else {
sb.append(",");
}
}
sb.append(" '").append(propertyName).append("'");
}
sb.append(size == 1 ? " is" : " are");
sb.append(" required for bean '").append(beanName).append("'");
return sb.toString();
}
}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2002-2011 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.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation at the field or method/constructor parameter level
* that indicates a default value expression for the affected argument.
*
* <p>Typically used for expression-driven dependency injection. Also supported
* for dynamic resolution of handler method parameters, e.g. in Spring MVC.
*
* <p>A common use case is to assign default field values using
* "#{systemProperties.myProp}" style expressions.
*
* <p>Note that actual processing of the {@code @Value} annotation is performed
* by a {@link org.springframework.beans.factory.config.BeanPostProcessor
* BeanPostProcessor} which in turn means that you <em>cannot</em> use
* {@code @Value} within
* {@link org.springframework.beans.factory.config.BeanPostProcessor
* BeanPostProcessor} or {@link BeanFactoryPostProcessor} types. Please
* consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor}
* class (which, by default, checks for the presence of this annotation).
*
* @author Juergen Hoeller
* @since 3.0
* @see AutowiredAnnotationBeanPostProcessor
* @see Autowired
* @see org.springframework.beans.factory.config.BeanExpressionResolver
* @see org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
/**
* The actual value expression: e.g. "#{systemProperties.myProp}".
*/
String value();
}

View File

@@ -0,0 +1,8 @@
/**
*
* Support package for annotation-driven bean configuration.
*
*/
package org.springframework.beans.factory.annotation;

View File

@@ -0,0 +1,266 @@
/*
* Copyright 2002-2010 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.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.ReflectionUtils;
/**
* Simple template superclass for {@link FactoryBean} implementations that
* creates a singleton or a prototype object, depending on a flag.
*
* <p>If the "singleton" flag is <code>true</code> (the default),
* this class will create the object that it creates exactly once
* on initialization and subsequently return said singleton instance
* on all calls to the {@link #getObject()} method.
*
* <p>Else, this class will create a new instance every time the
* {@link #getObject()} method is invoked. Subclasses are responsible
* for implementing the abstract {@link #createInstance()} template
* method to actually create the object(s) to expose.
*
* @author Juergen Hoeller
* @author Keith Donald
* @since 1.0.2
* @see #setSingleton
* @see #createInstance()
*/
public abstract class AbstractFactoryBean<T>
implements FactoryBean<T>, BeanClassLoaderAware, BeanFactoryAware, InitializingBean, DisposableBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private boolean singleton = true;
private ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
private BeanFactory beanFactory;
private boolean initialized = false;
private T singletonInstance;
private T earlySingletonInstance;
/**
* Set if a singleton should be created, or a new object on each request
* otherwise. Default is <code>true</code> (a singleton).
*/
public void setSingleton(boolean singleton) {
this.singleton = singleton;
}
public boolean isSingleton() {
return this.singleton;
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* Return the BeanFactory that this bean runs in.
*/
protected BeanFactory getBeanFactory() {
return this.beanFactory;
}
/**
* Obtain a bean type converter from the BeanFactory that this bean
* runs in. This is typically a fresh instance for each call,
* since TypeConverters are usually <i>not</i> thread-safe.
* <p>Falls back to a SimpleTypeConverter when not running in a BeanFactory.
* @see ConfigurableBeanFactory#getTypeConverter()
* @see org.springframework.beans.SimpleTypeConverter
*/
protected TypeConverter getBeanTypeConverter() {
BeanFactory beanFactory = getBeanFactory();
if (beanFactory instanceof ConfigurableBeanFactory) {
return ((ConfigurableBeanFactory) beanFactory).getTypeConverter();
}
else {
return new SimpleTypeConverter();
}
}
/**
* Eagerly create the singleton instance, if necessary.
*/
public void afterPropertiesSet() throws Exception {
if (isSingleton()) {
this.initialized = true;
this.singletonInstance = createInstance();
this.earlySingletonInstance = null;
}
}
/**
* Expose the singleton instance or create a new prototype instance.
* @see #createInstance()
* @see #getEarlySingletonInterfaces()
*/
public final T getObject() throws Exception {
if (isSingleton()) {
return (this.initialized ? this.singletonInstance : getEarlySingletonInstance());
}
else {
return createInstance();
}
}
/**
* Determine an 'eager singleton' instance, exposed in case of a
* circular reference. Not called in a non-circular scenario.
*/
@SuppressWarnings("unchecked")
private T getEarlySingletonInstance() throws Exception {
Class[] ifcs = getEarlySingletonInterfaces();
if (ifcs == null) {
throw new FactoryBeanNotInitializedException(
getClass().getName() + " does not support circular references");
}
if (this.earlySingletonInstance == null) {
this.earlySingletonInstance = (T) Proxy.newProxyInstance(
this.beanClassLoader, ifcs, new EarlySingletonInvocationHandler());
}
return this.earlySingletonInstance;
}
/**
* Expose the singleton instance (for access through the 'early singleton' proxy).
* @return the singleton instance that this FactoryBean holds
* @throws IllegalStateException if the singleton instance is not initialized
*/
private T getSingletonInstance() throws IllegalStateException {
if (!this.initialized) {
throw new IllegalStateException("Singleton instance not initialized yet");
}
return this.singletonInstance;
}
/**
* Destroy the singleton instance, if any.
* @see #destroyInstance(Object)
*/
public void destroy() throws Exception {
if (isSingleton()) {
destroyInstance(this.singletonInstance);
}
}
/**
* This abstract method declaration mirrors the method in the FactoryBean
* interface, for a consistent offering of abstract template methods.
* @see org.springframework.beans.factory.FactoryBean#getObjectType()
*/
public abstract Class<?> getObjectType();
/**
* Template method that subclasses must override to construct
* the object returned by this factory.
* <p>Invoked on initialization of this FactoryBean in case of
* a singleton; else, on each {@link #getObject()} call.
* @return the object returned by this factory
* @throws Exception if an exception occured during object creation
* @see #getObject()
*/
protected abstract T createInstance() throws Exception;
/**
* Return an array of interfaces that a singleton object exposed by this
* FactoryBean is supposed to implement, for use with an 'early singleton
* proxy' that will be exposed in case of a circular reference.
* <p>The default implementation returns this FactoryBean's object type,
* provided that it is an interface, or <code>null</code> else. The latter
* indicates that early singleton access is not supported by this FactoryBean.
* This will lead to a FactoryBeanNotInitializedException getting thrown.
* @return the interfaces to use for 'early singletons',
* or <code>null</code> to indicate a FactoryBeanNotInitializedException
* @see org.springframework.beans.factory.FactoryBeanNotInitializedException
*/
protected Class[] getEarlySingletonInterfaces() {
Class type = getObjectType();
return (type != null && type.isInterface() ? new Class[] {type} : null);
}
/**
* Callback for destroying a singleton instance. Subclasses may
* override this to destroy the previously created instance.
* <p>The default implementation is empty.
* @param instance the singleton instance, as returned by
* {@link #createInstance()}
* @throws Exception in case of shutdown errors
* @see #createInstance()
*/
protected void destroyInstance(T instance) throws Exception {
}
/**
* Reflective InvocationHandler for lazy access to the actual singleton object.
*/
private class EarlySingletonInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (ReflectionUtils.isEqualsMethod(method)) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (ReflectionUtils.isHashCodeMethod(method)) {
// Use hashCode of reference proxy.
return System.identityHashCode(proxy);
}
else if (!initialized && ReflectionUtils.isToStringMethod(method)) {
return "Early singleton proxy for interfaces " +
ObjectUtils.nullSafeToString(getEarlySingletonInterfaces());
}
try {
return method.invoke(getSingletonInstance(), args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}

View File

@@ -0,0 +1,319 @@
/*
* 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.util.Set;
import org.springframework.beans.BeansException;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanFactory;
/**
* Extension of the {@link org.springframework.beans.factory.BeanFactory}
* interface to be implemented by bean factories that are capable of
* autowiring, provided that they want to expose this functionality for
* existing bean instances.
*
* <p>This subinterface of BeanFactory is not meant to be used in normal
* application code: stick to {@link org.springframework.beans.factory.BeanFactory}
* or {@link org.springframework.beans.factory.ListableBeanFactory} for
* typical use cases.
*
* <p>Integration code for other frameworks can leverage this interface to
* wire and populate existing bean instances that Spring does not control
* the lifecycle of. This is particularly useful for WebWork Actions and
* Tapestry Page objects, for example.
*
* <p>Note that this interface is not implemented by
* {@link org.springframework.context.ApplicationContext} facades,
* as it is hardly ever used by application code. That said, it is available
* from an application context too, accessible through ApplicationContext's
* {@link org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()}
* method.
*
* <p>You may also implement the {@link org.springframework.beans.factory.BeanFactoryAware}
* interface, which exposes the internal BeanFactory even when running in an
* ApplicationContext, to get access to an AutowireCapableBeanFactory:
* simply cast the passed-in BeanFactory to AutowireCapableBeanFactory.
*
* @author Juergen Hoeller
* @since 04.12.2003
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.beans.factory.config.ConfigurableListableBeanFactory
* @see org.springframework.context.ApplicationContext#getAutowireCapableBeanFactory()
*/
public interface AutowireCapableBeanFactory extends BeanFactory {
/**
* Constant that indicates no externally defined autowiring. Note that
* BeanFactoryAware etc and annotation-driven injection will still be applied.
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
int AUTOWIRE_NO = 0;
/**
* Constant that indicates autowiring bean properties by name
* (applying to all bean property setters).
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
int AUTOWIRE_BY_NAME = 1;
/**
* Constant that indicates autowiring bean properties by type
* (applying to all bean property setters).
* @see #createBean
* @see #autowire
* @see #autowireBeanProperties
*/
int AUTOWIRE_BY_TYPE = 2;
/**
* Constant that indicates autowiring the greediest constructor that
* can be satisfied (involves resolving the appropriate constructor).
* @see #createBean
* @see #autowire
*/
int AUTOWIRE_CONSTRUCTOR = 3;
/**
* Constant that indicates determining an appropriate autowire strategy
* through introspection of the bean class.
* @see #createBean
* @see #autowire
* @deprecated as of Spring 3.0: If you are using mixed autowiring strategies,
* prefer annotation-based autowiring for clearer demarcation of autowiring needs.
*/
@Deprecated
int AUTOWIRE_AUTODETECT = 4;
//-------------------------------------------------------------------------
// Typical methods for creating and populating external bean instances
//-------------------------------------------------------------------------
/**
* Fully create a new bean instance of the given class.
* <p>Performs full initialization of the bean, including all applicable
* {@link BeanPostProcessor BeanPostProcessors}.
* <p>Note: This is intended for creating a fresh instance, populating annotated
* fields and methods as well as applying all standard bean initialiation callbacks.
* It does <i>not</> imply traditional by-name or by-type autowiring of properties;
* use {@link #createBean(Class, int, boolean)} for that purposes.
* @param beanClass the class of the bean to create
* @return the new bean instance
* @throws BeansException if instantiation or wiring failed
*/
<T> T createBean(Class<T> beanClass) throws BeansException;
/**
* Populate the given bean instance through applying after-instantiation callbacks
* and bean property post-processing (e.g. for annotation-driven injection).
* <p>Note: This is essentially intended for (re-)populating annotated fields and
* methods, either for new instances or for deserialized instances. It does
* <i>not</i> imply traditional by-name or by-type autowiring of properties;
* use {@link #autowireBeanProperties} for that purposes.
* @param existingBean the existing bean instance
* @throws BeansException if wiring failed
*/
void autowireBean(Object existingBean) throws BeansException;
/**
* Configure the given raw bean: autowiring bean properties, applying
* bean property values, applying factory callbacks such as <code>setBeanName</code>
* and <code>setBeanFactory</code>, and also applying all bean post processors
* (including ones which might wrap the given raw bean).
* <p>This is effectively a superset of what {@link #initializeBean} provides,
* fully applying the configuration specified by the corresponding bean definition.
* <b>Note: This method requires a bean definition for the given name!</b>
* @param existingBean the existing bean instance
* @param beanName the name of the bean, to be passed to it if necessary
* (a bean definition of that name has to be available)
* @return the bean instance to use, either the original or a wrapped one
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
* if there is no bean definition with the given name
* @throws BeansException if the initialization failed
* @see #initializeBean
*/
Object configureBean(Object existingBean, String beanName) throws BeansException;
/**
* Resolve the specified dependency against the beans defined in this factory.
* @param descriptor the descriptor for the dependency
* @param beanName the name of the bean which declares the present dependency
* @return the resolved object, or <code>null</code> if none found
* @throws BeansException in dependency resolution failed
*/
Object resolveDependency(DependencyDescriptor descriptor, String beanName) throws BeansException;
//-------------------------------------------------------------------------
// Specialized methods for fine-grained control over the bean lifecycle
//-------------------------------------------------------------------------
/**
* Fully create a new bean instance of the given class with the specified
* autowire strategy. All constants defined in this interface are supported here.
* <p>Performs full initialization of the bean, including all applicable
* {@link BeanPostProcessor BeanPostProcessors}. This is effectively a superset
* of what {@link #autowire} provides, adding {@link #initializeBean} behavior.
* @param beanClass the class of the bean to create
* @param autowireMode by name or type, using the constants in this interface
* @param dependencyCheck whether to perform a dependency check for objects
* (not applicable to autowiring a constructor, thus ignored there)
* @return the new bean instance
* @throws BeansException if instantiation or wiring failed
* @see #AUTOWIRE_NO
* @see #AUTOWIRE_BY_NAME
* @see #AUTOWIRE_BY_TYPE
* @see #AUTOWIRE_CONSTRUCTOR
*/
Object createBean(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
/**
* Instantiate a new bean instance of the given class with the specified autowire
* strategy. All constants defined in this interface are supported here.
* Can also be invoked with <code>AUTOWIRE_NO</code> in order to just apply
* before-instantiation callbacks (e.g. for annotation-driven injection).
* <p>Does <i>not</i> apply standard {@link BeanPostProcessor BeanPostProcessors}
* callbacks or perform any further initialization of the bean. This interface
* offers distinct, fine-grained operations for those purposes, for example
* {@link #initializeBean}. However, {@link InstantiationAwareBeanPostProcessor}
* callbacks are applied, if applicable to the construction of the instance.
* @param beanClass the class of the bean to instantiate
* @param autowireMode by name or type, using the constants in this interface
* @param dependencyCheck whether to perform a dependency check for object
* references in the bean instance (not applicable to autowiring a constructor,
* thus ignored there)
* @return the new bean instance
* @throws BeansException if instantiation or wiring failed
* @see #AUTOWIRE_NO
* @see #AUTOWIRE_BY_NAME
* @see #AUTOWIRE_BY_TYPE
* @see #AUTOWIRE_CONSTRUCTOR
* @see #AUTOWIRE_AUTODETECT
* @see #initializeBean
* @see #applyBeanPostProcessorsBeforeInitialization
* @see #applyBeanPostProcessorsAfterInitialization
*/
Object autowire(Class beanClass, int autowireMode, boolean dependencyCheck) throws BeansException;
/**
* Autowire the bean properties of the given bean instance by name or type.
* Can also be invoked with <code>AUTOWIRE_NO</code> in order to just apply
* after-instantiation callbacks (e.g. for annotation-driven injection).
* <p>Does <i>not</i> apply standard {@link BeanPostProcessor BeanPostProcessors}
* callbacks or perform any further initialization of the bean. This interface
* offers distinct, fine-grained operations for those purposes, for example
* {@link #initializeBean}. However, {@link InstantiationAwareBeanPostProcessor}
* callbacks are applied, if applicable to the configuration of the instance.
* @param existingBean the existing bean instance
* @param autowireMode by name or type, using the constants in this interface
* @param dependencyCheck whether to perform a dependency check for object
* references in the bean instance
* @throws BeansException if wiring failed
* @see #AUTOWIRE_BY_NAME
* @see #AUTOWIRE_BY_TYPE
* @see #AUTOWIRE_NO
*/
void autowireBeanProperties(Object existingBean, int autowireMode, boolean dependencyCheck)
throws BeansException;
/**
* Apply the property values of the bean definition with the given name to
* the given bean instance. The bean definition can either define a fully
* self-contained bean, reusing its property values, or just property values
* meant to be used for existing bean instances.
* <p>This method does <i>not</i> autowire bean properties; it just applies
* explicitly defined property values. Use the {@link #autowireBeanProperties}
* method to autowire an existing bean instance.
* <b>Note: This method requires a bean definition for the given name!</b>
* <p>Does <i>not</i> apply standard {@link BeanPostProcessor BeanPostProcessors}
* callbacks or perform any further initialization of the bean. This interface
* offers distinct, fine-grained operations for those purposes, for example
* {@link #initializeBean}. However, {@link InstantiationAwareBeanPostProcessor}
* callbacks are applied, if applicable to the configuration of the instance.
* @param existingBean the existing bean instance
* @param beanName the name of the bean definition in the bean factory
* (a bean definition of that name has to be available)
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException
* if there is no bean definition with the given name
* @throws BeansException if applying the property values failed
* @see #autowireBeanProperties
*/
void applyBeanPropertyValues(Object existingBean, String beanName) throws BeansException;
/**
* Initialize the given raw bean, applying factory callbacks
* such as <code>setBeanName</code> and <code>setBeanFactory</code>,
* also applying all bean post processors (including ones which
* might wrap the given raw bean).
* <p>Note that no bean definition of the given name has to exist
* in the bean factory. The passed-in bean name will simply be used
* for callbacks but not checked against the registered bean definitions.
* @param existingBean the existing bean instance
* @param beanName the name of the bean, to be passed to it if necessary
* (only passed to {@link BeanPostProcessor BeanPostProcessors})
* @return the bean instance to use, either the original or a wrapped one
* @throws BeansException if the initialization failed
*/
Object initializeBean(Object existingBean, String beanName) throws BeansException;
/**
* Apply {@link BeanPostProcessor BeanPostProcessors} to the given existing bean
* instance, invoking their <code>postProcessBeforeInitialization</code> methods.
* The returned bean instance may be a wrapper around the original.
* @param existingBean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one
* @throws BeansException if any post-processing failed
* @see BeanPostProcessor#postProcessBeforeInitialization
*/
Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
throws BeansException;
/**
* Apply {@link BeanPostProcessor BeanPostProcessors} to the given existing bean
* instance, invoking their <code>postProcessAfterInitialization</code> methods.
* The returned bean instance may be a wrapper around the original.
* @param existingBean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one
* @throws BeansException if any post-processing failed
* @see BeanPostProcessor#postProcessAfterInitialization
*/
Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
throws BeansException;
/**
* Resolve the specified dependency against the beans defined in this factory.
* @param descriptor the descriptor for the dependency
* @param beanName the name of the bean which declares the present dependency
* @param autowiredBeanNames a Set that all names of autowired beans (used for
* resolving the present dependency) are supposed to be added to
* @param typeConverter the TypeConverter to use for populating arrays and
* collections
* @return the resolved object, or <code>null</code> if none found
* @throws BeansException in dependency resolution failed
*/
Object resolveDependency(DependencyDescriptor descriptor, String beanName,
Set<String> autowiredBeanNames, TypeConverter typeConverter) throws BeansException;
}

View File

@@ -0,0 +1,259 @@
/*
* Copyright 2002-2011 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 org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.core.AttributeAccessor;
/**
* A BeanDefinition describes a bean instance, which has property values,
* constructor argument values, and further information supplied by
* concrete implementations.
*
* <p>This is just a minimal interface: The main intention is to allow a
* {@link BeanFactoryPostProcessor} such as {@link PropertyPlaceholderConfigurer}
* to introspect and modify property values and other bean metadata.
*
* @author Juergen Hoeller
* @author Rob Harrop
* @since 19.03.2004
* @see ConfigurableListableBeanFactory#getBeanDefinition
* @see org.springframework.beans.factory.support.RootBeanDefinition
* @see org.springframework.beans.factory.support.ChildBeanDefinition
*/
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
/**
* Scope identifier for the standard singleton scope: "singleton".
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
*/
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
/**
* Scope identifier for the standard prototype scope: "prototype".
* <p>Note that extended bean factories might support further scopes.
* @see #setScope
*/
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
/**
* Role hint indicating that a <code>BeanDefinition</code> is a major part
* of the application. Typically corresponds to a user-defined bean.
*/
int ROLE_APPLICATION = 0;
/**
* Role hint indicating that a <code>BeanDefinition</code> is a supporting
* part of some larger configuration, typically an outer
* {@link org.springframework.beans.factory.parsing.ComponentDefinition}.
* <code>SUPPORT</code> beans are considered important enough to be aware
* of when looking more closely at a particular
* {@link org.springframework.beans.factory.parsing.ComponentDefinition},
* but not when looking at the overall configuration of an application.
*/
int ROLE_SUPPORT = 1;
/**
* Role hint indicating that a <code>BeanDefinition</code> is providing an
* entirely background role and has no relevance to the end-user. This hint is
* used when registering beans that are completely part of the internal workings
* of a {@link org.springframework.beans.factory.parsing.ComponentDefinition}.
*/
int ROLE_INFRASTRUCTURE = 2;
/**
* Return the name of the parent definition of this bean definition, if any.
*/
String getParentName();
/**
* Set the name of the parent definition of this bean definition, if any.
*/
void setParentName(String parentName);
/**
* Return the current bean class name of this bean definition.
* <p>Note that this does not have to be the actual class name used at runtime, in
* case of a child definition overriding/inheriting the class name from its parent.
* Hence, do <i>not</i> consider this to be the definitive bean type at runtime but
* rather only use it for parsing purposes at the individual bean definition level.
*/
String getBeanClassName();
/**
* Override the bean class name of this bean definition.
* <p>The class name can be modified during bean factory post-processing,
* typically replacing the original class name with a parsed variant of it.
*/
void setBeanClassName(String beanClassName);
/**
* Return the factory bean name, if any.
*/
String getFactoryBeanName();
/**
* Specify the factory bean to use, if any.
*/
void setFactoryBeanName(String factoryBeanName);
/**
* Return a factory method, if any.
*/
String getFactoryMethodName();
/**
* Specify a factory method, if any. This method will be invoked with
* constructor arguments, or with no arguments if none are specified.
* The method will be invoked on the specified factory bean, if any,
* or otherwise as a static method on the local bean class.
* @param factoryMethodName static factory method name,
* or <code>null</code> if normal constructor creation should be used
* @see #getBeanClassName()
*/
void setFactoryMethodName(String factoryMethodName);
/**
* Return the name of the current target scope for this bean,
* or <code>null</code> if not known yet.
*/
String getScope();
/**
* Override the target scope of this bean, specifying a new scope name.
* @see #SCOPE_SINGLETON
* @see #SCOPE_PROTOTYPE
*/
void setScope(String scope);
/**
* Return whether this bean should be lazily initialized, i.e. not
* eagerly instantiated on startup. Only applicable to a singleton bean.
*/
boolean isLazyInit();
/**
* Set whether this bean should be lazily initialized.
* <p>If <code>false</code>, the bean will get instantiated on startup by bean
* factories that perform eager initialization of singletons.
*/
void setLazyInit(boolean lazyInit);
/**
* Return the bean names that this bean depends on.
*/
String[] getDependsOn();
/**
* Set the names of the beans that this bean depends on being initialized.
* The bean factory will guarantee that these beans get initialized first.
*/
void setDependsOn(String[] dependsOn);
/**
* Return whether this bean is a candidate for getting autowired into some other bean.
*/
boolean isAutowireCandidate();
/**
* Set whether this bean is a candidate for getting autowired into some other bean.
*/
void setAutowireCandidate(boolean autowireCandidate);
/**
* Return whether this bean is a primary autowire candidate.
* If this value is true for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
boolean isPrimary();
/**
* Set whether this bean is a primary autowire candidate.
* <p>If this value is true for exactly one bean among multiple
* matching candidates, it will serve as a tie-breaker.
*/
void setPrimary(boolean primary);
/**
* Return the constructor argument values for this bean.
* <p>The returned instance can be modified during bean factory post-processing.
* @return the ConstructorArgumentValues object (never <code>null</code>)
*/
ConstructorArgumentValues getConstructorArgumentValues();
/**
* Return the property values to be applied to a new instance of the bean.
* <p>The returned instance can be modified during bean factory post-processing.
* @return the MutablePropertyValues object (never <code>null</code>)
*/
MutablePropertyValues getPropertyValues();
/**
* Return whether this a <b>Singleton</b>, with a single, shared instance
* returned on all calls.
* @see #SCOPE_SINGLETON
*/
boolean isSingleton();
/**
* Return whether this a <b>Prototype</b>, with an independent instance
* returned for each call.
* @see #SCOPE_PROTOTYPE
*/
boolean isPrototype();
/**
* Return whether this bean is "abstract", that is, not meant to be instantiated.
*/
boolean isAbstract();
/**
* Get the role hint for this <code>BeanDefinition</code>. The role hint
* provides tools with an indication of the importance of a particular
* <code>BeanDefinition</code>.
* @see #ROLE_APPLICATION
* @see #ROLE_INFRASTRUCTURE
* @see #ROLE_SUPPORT
*/
int getRole();
/**
* Return a human-readable description of this bean definition.
*/
String getDescription();
/**
* Return a description of the resource that this bean definition
* came from (for the purpose of showing context in case of errors).
*/
String getResourceDescription();
/**
* Return the originating BeanDefinition, or <code>null</code> if none.
* Allows for retrieving the decorated bean definition, if any.
* <p>Note that this method returns the immediate originator. Iterate through the
* originator chain to find the original BeanDefinition as defined by the user.
*/
BeanDefinition getOriginatingBeanDefinition();
}

View File

@@ -0,0 +1,185 @@
/*
* 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 org.springframework.beans.BeanMetadataElement;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Holder for a BeanDefinition with name and aliases.
* Can be registered as a placeholder for an inner bean.
*
* <p>Can also be used for programmatic registration of inner bean
* definitions. If you don't care about BeanNameAware and the like,
* registering RootBeanDefinition or ChildBeanDefinition is good enough.
*
* @author Juergen Hoeller
* @since 1.0.2
* @see org.springframework.beans.factory.BeanNameAware
* @see org.springframework.beans.factory.support.RootBeanDefinition
* @see org.springframework.beans.factory.support.ChildBeanDefinition
*/
public class BeanDefinitionHolder implements BeanMetadataElement {
private final BeanDefinition beanDefinition;
private final String beanName;
private final String[] aliases;
/**
* Create a new BeanDefinitionHolder.
* @param beanDefinition the BeanDefinition to wrap
* @param beanName the name of the bean, as specified for the bean definition
*/
public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName) {
this(beanDefinition, beanName, null);
}
/**
* Create a new BeanDefinitionHolder.
* @param beanDefinition the BeanDefinition to wrap
* @param beanName the name of the bean, as specified for the bean definition
* @param aliases alias names for the bean, or <code>null</code> if none
*/
public BeanDefinitionHolder(BeanDefinition beanDefinition, String beanName, String[] aliases) {
Assert.notNull(beanDefinition, "BeanDefinition must not be null");
Assert.notNull(beanName, "Bean name must not be null");
this.beanDefinition = beanDefinition;
this.beanName = beanName;
this.aliases = aliases;
}
/**
* Copy constructor: Create a new BeanDefinitionHolder with the
* same contents as the given BeanDefinitionHolder instance.
* <p>Note: The wrapped BeanDefinition reference is taken as-is;
* it is <code>not</code> deeply copied.
* @param beanDefinitionHolder the BeanDefinitionHolder to copy
*/
public BeanDefinitionHolder(BeanDefinitionHolder beanDefinitionHolder) {
Assert.notNull(beanDefinitionHolder, "BeanDefinitionHolder must not be null");
this.beanDefinition = beanDefinitionHolder.getBeanDefinition();
this.beanName = beanDefinitionHolder.getBeanName();
this.aliases = beanDefinitionHolder.getAliases();
}
/**
* Return the wrapped BeanDefinition.
*/
public BeanDefinition getBeanDefinition() {
return this.beanDefinition;
}
/**
* Return the primary name of the bean, as specified for the bean definition.
*/
public String getBeanName() {
return this.beanName;
}
/**
* Return the alias names for the bean, as specified directly for the bean definition.
* @return the array of alias names, or <code>null</code> if none
*/
public String[] getAliases() {
return this.aliases;
}
/**
* Expose the bean definition's source object.
* @see BeanDefinition#getSource()
*/
public Object getSource() {
return this.beanDefinition.getSource();
}
/**
* Determine whether the given candidate name matches the bean name
* or the aliases stored in this bean definition.
*/
public boolean matchesName(String candidateName) {
return (candidateName != null &&
(candidateName.equals(this.beanName) || ObjectUtils.containsElement(this.aliases, candidateName)));
}
/**
* Return a friendly, short description for the bean, stating name and aliases.
* @see #getBeanName()
* @see #getAliases()
*/
public String getShortDescription() {
StringBuilder sb = new StringBuilder();
sb.append("Bean definition with name '").append(this.beanName).append("'");
if (this.aliases != null) {
sb.append(" and aliases [").append(StringUtils.arrayToCommaDelimitedString(this.aliases)).append("]");
}
return sb.toString();
}
/**
* Return a long description for the bean, including name and aliases
* as well as a description of the contained {@link BeanDefinition}.
* @see #getShortDescription()
* @see #getBeanDefinition()
*/
public String getLongDescription() {
StringBuilder sb = new StringBuilder(getShortDescription());
sb.append(": ").append(this.beanDefinition);
return sb.toString();
}
/**
* This implementation returns the long description. Can be overridden
* to return the short description or any kind of custom description instead.
* @see #getLongDescription()
* @see #getShortDescription()
*/
@Override
public String toString() {
return getLongDescription();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof BeanDefinitionHolder)) {
return false;
}
BeanDefinitionHolder otherHolder = (BeanDefinitionHolder) other;
return this.beanDefinition.equals(otherHolder.beanDefinition) &&
this.beanName.equals(otherHolder.beanName) &&
ObjectUtils.nullSafeEquals(this.aliases, otherHolder.aliases);
}
@Override
public int hashCode() {
int hashCode = this.beanDefinition.hashCode();
hashCode = 29 * hashCode + this.beanName.hashCode();
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.aliases);
return hashCode;
}
}

View File

@@ -0,0 +1,287 @@
/*
* Copyright 2002-2011 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.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValue;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringValueResolver;
/**
* Visitor class for traversing {@link BeanDefinition} objects, in particular
* the property values and constructor argument values contained in them,
* resolving bean metadata values.
*
* <p>Used by {@link PropertyPlaceholderConfigurer} to parse all String values
* contained in a BeanDefinition, resolving any placeholders found.
*
* @author Juergen Hoeller
* @author Sam Brannen
* @since 1.2
* @see BeanDefinition
* @see BeanDefinition#getPropertyValues
* @see BeanDefinition#getConstructorArgumentValues
* @see PropertyPlaceholderConfigurer
*/
public class BeanDefinitionVisitor {
private StringValueResolver valueResolver;
/**
* Create a new BeanDefinitionVisitor, applying the specified
* value resolver to all bean metadata values.
* @param valueResolver the StringValueResolver to apply
*/
public BeanDefinitionVisitor(StringValueResolver valueResolver) {
Assert.notNull(valueResolver, "StringValueResolver must not be null");
this.valueResolver = valueResolver;
}
/**
* Create a new BeanDefinitionVisitor for subclassing.
* Subclasses need to override the {@link #resolveStringValue} method.
*/
protected BeanDefinitionVisitor() {
}
/**
* Traverse the given BeanDefinition object and the MutablePropertyValues
* and ConstructorArgumentValues contained in them.
* @param beanDefinition the BeanDefinition object to traverse
* @see #resolveStringValue(String)
*/
public void visitBeanDefinition(BeanDefinition beanDefinition) {
visitParentName(beanDefinition);
visitBeanClassName(beanDefinition);
visitFactoryBeanName(beanDefinition);
visitFactoryMethodName(beanDefinition);
visitScope(beanDefinition);
visitPropertyValues(beanDefinition.getPropertyValues());
ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();
visitIndexedArgumentValues(cas.getIndexedArgumentValues());
visitGenericArgumentValues(cas.getGenericArgumentValues());
}
protected void visitParentName(BeanDefinition beanDefinition) {
String parentName = beanDefinition.getParentName();
if (parentName != null) {
String resolvedName = resolveStringValue(parentName);
if (!parentName.equals(resolvedName)) {
beanDefinition.setParentName(resolvedName);
}
}
}
protected void visitBeanClassName(BeanDefinition beanDefinition) {
String beanClassName = beanDefinition.getBeanClassName();
if (beanClassName != null) {
String resolvedName = resolveStringValue(beanClassName);
if (!beanClassName.equals(resolvedName)) {
beanDefinition.setBeanClassName(resolvedName);
}
}
}
protected void visitFactoryBeanName(BeanDefinition beanDefinition) {
String factoryBeanName = beanDefinition.getFactoryBeanName();
if (factoryBeanName != null) {
String resolvedName = resolveStringValue(factoryBeanName);
if (!factoryBeanName.equals(resolvedName)) {
beanDefinition.setFactoryBeanName(resolvedName);
}
}
}
protected void visitFactoryMethodName(BeanDefinition beanDefinition) {
String factoryMethodName = beanDefinition.getFactoryMethodName();
if (factoryMethodName != null) {
String resolvedName = resolveStringValue(factoryMethodName);
if (!factoryMethodName.equals(resolvedName)) {
beanDefinition.setFactoryMethodName(resolvedName);
}
}
}
protected void visitScope(BeanDefinition beanDefinition) {
String scope = beanDefinition.getScope();
if (scope != null) {
String resolvedScope = resolveStringValue(scope);
if (!scope.equals(resolvedScope)) {
beanDefinition.setScope(resolvedScope);
}
}
}
protected void visitPropertyValues(MutablePropertyValues pvs) {
PropertyValue[] pvArray = pvs.getPropertyValues();
for (PropertyValue pv : pvArray) {
Object newVal = resolveValue(pv.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
pvs.add(pv.getName(), newVal);
}
}
}
protected void visitIndexedArgumentValues(Map<Integer, ConstructorArgumentValues.ValueHolder> ias) {
for (ConstructorArgumentValues.ValueHolder valueHolder : ias.values()) {
Object newVal = resolveValue(valueHolder.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, valueHolder.getValue())) {
valueHolder.setValue(newVal);
}
}
}
protected void visitGenericArgumentValues(List<ConstructorArgumentValues.ValueHolder> gas) {
for (ConstructorArgumentValues.ValueHolder valueHolder : gas) {
Object newVal = resolveValue(valueHolder.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, valueHolder.getValue())) {
valueHolder.setValue(newVal);
}
}
}
@SuppressWarnings("rawtypes")
protected Object resolveValue(Object value) {
if (value instanceof BeanDefinition) {
visitBeanDefinition((BeanDefinition) value);
}
else if (value instanceof BeanDefinitionHolder) {
visitBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition());
}
else if (value instanceof RuntimeBeanReference) {
RuntimeBeanReference ref = (RuntimeBeanReference) value;
String newBeanName = resolveStringValue(ref.getBeanName());
if (!newBeanName.equals(ref.getBeanName())) {
return new RuntimeBeanReference(newBeanName);
}
}
else if (value instanceof RuntimeBeanNameReference) {
RuntimeBeanNameReference ref = (RuntimeBeanNameReference) value;
String newBeanName = resolveStringValue(ref.getBeanName());
if (!newBeanName.equals(ref.getBeanName())) {
return new RuntimeBeanNameReference(newBeanName);
}
}
else if (value instanceof Object[]) {
visitArray((Object[]) value);
}
else if (value instanceof List) {
visitList((List) value);
}
else if (value instanceof Set) {
visitSet((Set) value);
}
else if (value instanceof Map) {
visitMap((Map) value);
}
else if (value instanceof TypedStringValue) {
TypedStringValue typedStringValue = (TypedStringValue) value;
String stringValue = typedStringValue.getValue();
if (stringValue != null) {
String visitedString = resolveStringValue(stringValue);
typedStringValue.setValue(visitedString);
}
}
else if (value instanceof String) {
return resolveStringValue((String) value);
}
return value;
}
protected void visitArray(Object[] arrayVal) {
for (int i = 0; i < arrayVal.length; i++) {
Object elem = arrayVal[i];
Object newVal = resolveValue(elem);
if (!ObjectUtils.nullSafeEquals(newVal, elem)) {
arrayVal[i] = newVal;
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void visitList(List listVal) {
for (int i = 0; i < listVal.size(); i++) {
Object elem = listVal.get(i);
Object newVal = resolveValue(elem);
if (!ObjectUtils.nullSafeEquals(newVal, elem)) {
listVal.set(i, newVal);
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void visitSet(Set setVal) {
Set newContent = new LinkedHashSet();
boolean entriesModified = false;
for (Object elem : setVal) {
int elemHash = (elem != null ? elem.hashCode() : 0);
Object newVal = resolveValue(elem);
int newValHash = (newVal != null ? newVal.hashCode() : 0);
newContent.add(newVal);
entriesModified = entriesModified || (newVal != elem || newValHash != elemHash);
}
if (entriesModified) {
setVal.clear();
setVal.addAll(newContent);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void visitMap(Map<?, ?> mapVal) {
Map newContent = new LinkedHashMap();
boolean entriesModified = false;
for (Map.Entry entry : mapVal.entrySet()) {
Object key = entry.getKey();
int keyHash = (key != null ? key.hashCode() : 0);
Object newKey = resolveValue(key);
int newKeyHash = (newKey != null ? newKey.hashCode() : 0);
Object val = entry.getValue();
Object newVal = resolveValue(val);
newContent.put(newKey, newVal);
entriesModified = entriesModified || (newVal != val || newKey != key || newKeyHash != keyHash);
}
if (entriesModified) {
mapVal.clear();
mapVal.putAll(newContent);
}
}
/**
* Resolve the given String value, for example parsing placeholders.
* @param strVal the original String value
* @return the resolved String value
*/
protected String resolveStringValue(String strVal) {
if (this.valueResolver == null) {
throw new IllegalStateException("No StringValueResolver specified - pass a resolver " +
"object into the constructor or override the 'resolveStringValue' method");
}
String resolvedValue = this.valueResolver.resolveStringValue(strVal);
// Return original String if not modified.
return (strVal.equals(resolvedValue) ? strVal : resolvedValue);
}
}

View File

@@ -0,0 +1,84 @@
/*
* 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 org.springframework.util.Assert;
/**
* Context object for evaluating an expression within a bean definition.
*
* @author Juergen Hoeller
* @since 3.0
*/
public class BeanExpressionContext {
private final ConfigurableBeanFactory beanFactory;
private final Scope scope;
public BeanExpressionContext(ConfigurableBeanFactory beanFactory, Scope scope) {
Assert.notNull(beanFactory, "BeanFactory must not be null");
this.beanFactory = beanFactory;
this.scope = scope;
}
public final ConfigurableBeanFactory getBeanFactory() {
return this.beanFactory;
}
public final Scope getScope() {
return this.scope;
}
public boolean containsObject(String key) {
return (this.beanFactory.containsBean(key) ||
(this.scope != null && this.scope.resolveContextualObject(key) != null));
}
public Object getObject(String key) {
if (this.beanFactory.containsBean(key)) {
return this.beanFactory.getBean(key);
}
else if (this.scope != null){
return this.scope.resolveContextualObject(key);
}
else {
return null;
}
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof BeanExpressionContext)) {
return false;
}
BeanExpressionContext otherContext = (BeanExpressionContext) other;
return (this.beanFactory == otherContext.beanFactory && this.scope == otherContext.scope);
}
@Override
public int hashCode() {
return this.beanFactory.hashCode();
}
}

View File

@@ -0,0 +1,45 @@
/*
* 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.config;
import org.springframework.beans.BeansException;
/**
* Strategy interface for resolving a value through evaluating it
* as an expression, if applicable.
*
* <p>A raw {@link org.springframework.beans.factory.BeanFactory} does not
* contain a default implementation of this strategy. However,
* {@link org.springframework.context.ApplicationContext} implementations
* will provide expression support out of the box.
*
* @author Juergen Hoeller
* @since 3.0
*/
public interface BeanExpressionResolver {
/**
* Evaluate the given value as an expression, if applicable;
* return the value as-is otherwise.
* @param value the value to check
* @param evalContext the evaluation context
* @return the resolved value (potentially the given value as-is)
* @throws BeansException if evaluation failed
*/
Object evaluate(String value, BeanExpressionContext evalContext) throws BeansException;
}

View File

@@ -0,0 +1,57 @@
/*
* Copyright 2002-2005 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 org.springframework.beans.BeansException;
/**
* Allows for custom modification of an application context's bean definitions,
* adapting the bean property values of the context's underlying bean factory.
*
* <p>Application contexts can auto-detect BeanFactoryPostProcessor beans in
* their bean definitions and apply them before any other beans get created.
*
* <p>Useful for custom config files targeted at system administrators that
* override bean properties configured in the application context.
*
* <p>See PropertyResourceConfigurer and its concrete implementations
* for out-of-the-box solutions that address such configuration needs.
*
* <p>A BeanFactoryPostProcessor may interact with and modify bean
* definitions, but never bean instances. Doing so may cause premature bean
* instantiation, violating the container and causing unintended side-effects.
* If bean instance interaction is required, consider implementing
* {@link BeanPostProcessor} instead.
*
* @author Juergen Hoeller
* @since 06.07.2003
* @see BeanPostProcessor
* @see PropertyResourceConfigurer
*/
public interface BeanFactoryPostProcessor {
/**
* Modify the application context's internal bean factory after its standard
* initialization. All bean definitions will have been loaded, but no beans
* will have been instantiated yet. This allows for overriding or adding
* properties even to eager-initializing beans.
* @param beanFactory the bean factory used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

Some files were not shown because too many files have changed in this diff Show More