Moved over initial version of beans bundle

This commit is contained in:
Arjen Poutsma
2008-10-22 16:13:37 +00:00
parent 684a4f28c2
commit f11d3436ed
276 changed files with 43587 additions and 3 deletions

View File

@@ -0,0 +1,140 @@
/*
* 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.util.Arrays;
import java.util.Iterator;
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 propertyAccessExceptions = null;
List propertyValues = (pvs instanceof MutablePropertyValues ?
((MutablePropertyValues) pvs).getPropertyValueList() : Arrays.asList(pvs.getPropertyValues()));
for (Iterator it = propertyValues.iterator(); it.hasNext();) {
PropertyValue pv = (PropertyValue) it.next();
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();
}
propertyAccessExceptions.add(ex);
}
}
// If we encountered individual exceptions, throw the composite exception.
if (propertyAccessExceptions != null) {
PropertyAccessException[] paeArray = (PropertyAccessException[])
propertyAccessExceptions.toArray(new PropertyAccessException[propertyAccessExceptions.size()]);
throw new PropertyBatchUpdateException(paeArray);
}
}
public Object convertIfNecessary(Object value, Class requiredType) throws TypeMismatchException {
return convertIfNecessary(value, requiredType, null);
}
// Redefined with public visibility.
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,98 @@
/*
* 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;
}
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));
}
public int hashCode() {
return this.name.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.value);
}
public String toString() {
return "metadata attribute '" + this.name + "'";
}
}

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 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);
}
public void setAttribute(String name, Object value) {
super.setAttribute(name, new BeanMetadataAttribute(name, value));
}
public Object getAttribute(String name) {
BeanMetadataAttribute attribute = (BeanMetadataAttribute) super.getAttribute(name);
return (attribute != null ? attribute.getValue() : null);
}
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,598 @@
/*
* 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.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
*/
public abstract class BeanUtils {
private static final Log logger = LogFactory.getLog(BeanUtils.class);
private static final Map unknownEditorTypes = Collections.synchronizedMap(new WeakHashMap());
/**
* 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.
* <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 Object instantiateClass(Class 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((Class[]) null), null);
}
catch (NoSuchMethodException ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
/**
* 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 Object instantiateClass(Constructor 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(),
"Has the class definition changed? 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 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
* @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 = doFindMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName);
if (targetMethod == null) {
return 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 = doFindMethodWithMinimalParameters(clazz.getDeclaredMethods(), methodName);
if (targetMethod == null && clazz.getSuperclass() != null) {
return 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
*/
private static Method doFindMethodWithMinimalParameters(Method[] methods, String methodName)
throws IllegalArgumentException {
Method targetMethod = null;
int numMethodsFoundWithCurrentMinimumArgs = 0;
for (int i = 0; i < methods.length; i++) {
if (methods[i].getName().equals(methodName)) {
int numParams = methods[i].getParameterTypes().length;
if (targetMethod == null ||
numParams < targetMethod.getParameterTypes().length) {
targetMethod = methods[i];
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.getBeanInfo().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 (int i = 0; i < pds.length; i++) {
PropertyDescriptor pd = pds[i];
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 || unknownEditorTypes.containsKey(targetType)) {
return null;
}
ClassLoader cl = targetType.getClassLoader();
if (cl == null) {
cl = ClassLoader.getSystemClassLoader();
if (cl == null) {
return null;
}
}
String editorName = targetType.getName() + "Editor";
try {
Class editorClass = cl.loadClass(editorName);
if (!PropertyEditor.class.isAssignableFrom(editorClass)) {
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 (int i = 0; i < beanClasses.length; i++) {
PropertyDescriptor pd = getPropertyDescriptor(beanClasses[i], 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) || 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);
}
/**
* Determine if the given target type is assignable from the given value
* type, assuming setting by reflection. Considers primitive wrapper
* classes as assignable to the corresponding primitive types.
* @param targetType the target type
* @param valueType the value type that should be assigned to the target type
* @return if the target type is assignable from the value type
* @deprecated as of Spring 2.0, in favor of <code>ClassUtils.isAssignable</code>
* @see org.springframework.util.ClassUtils#isAssignable(Class, Class)
*/
public static boolean isAssignable(Class targetType, Class valueType) {
return ClassUtils.isAssignable(targetType, valueType);
}
/**
* Determine if the given type is assignable from the given value,
* assuming setting by reflection. Considers primitive wrapper classes
* as assignable to the corresponding primitive types.
* @param type the target type
* @param value the value that should be assigned to the type
* @return if the type is assignable from the value
* @deprecated as of Spring 2.0, in favor of <code>ClassUtils.isAssignableValue</code>
* @see org.springframework.util.ClassUtils#isAssignableValue(Class, Object)
*/
public static boolean isAssignable(Class type, Object value) {
return ClassUtils.isAssignableValue(type, value);
}
/**
* 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 ignoreList = (ignoreProperties != null) ? Arrays.asList(ignoreProperties) : null;
for (int i = 0; i < targetPds.length; i++) {
PropertyDescriptor targetPd = targetPds[i];
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, new Object[0]);
Method writeMethod = targetPd.getWriteMethod();
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(target, new Object[] {value});
}
catch (Throwable ex) {
throw new FatalBeanException("Could not copy properties from source to target", ex);
}
}
}
}
}
}

View File

@@ -0,0 +1,93 @@
/*
* 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;
/**
* 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.
* A <code>BeanWrapper</code> instance can be used repeatedly, with its
* {@link #setWrappedInstance(Object) target object} (the wrapped JavaBean
* instance) changing as required.
*
* <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 #setExtractOldValueForEditor
* @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 {
/**
* Change the wrapped JavaBean object.
* @param obj the bean instance to wrap
* @deprecated as of Spring 2.5,
* in favor of recreating a BeanWrapper per target instance
*/
void setWrappedInstance(Object obj);
/**
* 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 BeansException;
}

View File

@@ -0,0 +1,891 @@
/*
* 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.PropertyChangeEvent;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.JdkVersion;
import org.springframework.core.MethodParameter;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Default {@link BeanWrapper} implementation that should be sufficient
* for all typical use cases. Caches introspection results for efficiency.
*
* <p>Note: Auto-registers default property editors from the
* <code>org.springframework.beans.propertyeditors</code> package, which apply
* in addition to the JDK's standard PropertyEditors. Applications can call
* the {@link #registerCustomEditor(Class, java.beans.PropertyEditor)} method
* to register an editor for a particular instance (i.e. they are not shared
* across the application). See the base class
* {@link PropertyEditorRegistrySupport} for details.
*
* <p><code>BeanWrapperImpl</code> will convert collection and array values
* to the corresponding target collections or arrays, if necessary. Custom
* property editors that deal with collections or arrays can either be
* written via PropertyEditor's <code>setValue</code>, or against a
* comma-delimited String via <code>setAsText</code>, as String arrays are
* converted in such a format if the array itself is not assignable.
*
* <p><b>NOTE: As of Spring 2.5, this is - for almost all purposes - an
* internal class.</b> It is just public in order to allow for access from
* other framework packages. For standard application access purposes, use the
* {@link PropertyAccessorFactory#forBeanPropertyAccess} factory method instead.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @since 15 April 2001
* @see #registerCustomEditor
* @see #setPropertyValues
* @see #setPropertyValue
* @see #getPropertyValue
* @see #getPropertyType
* @see BeanWrapper
* @see PropertyEditorRegistrySupport
*/
public class BeanWrapperImpl extends AbstractPropertyAccessor implements BeanWrapper {
/**
* We'll create a lot of these objects, so we don't want a new logger every time.
*/
private static final Log logger = LogFactory.getLog(BeanWrapperImpl.class);
/** The wrapped object */
private Object object;
private String nestedPath = "";
private Object rootObject;
private TypeConverterDelegate typeConverterDelegate;
/**
* Cached introspections results for this object, to prevent encountering
* the cost of JavaBeans introspection every time.
*/
private CachedIntrospectionResults cachedIntrospectionResults;
/**
* Map with cached nested BeanWrappers: nested path -> BeanWrapper instance.
*/
private Map nestedBeanWrappers;
/**
* Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
* Registers default editors.
* @see #setWrappedInstance
*/
public BeanWrapperImpl() {
this(true);
}
/**
* Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
* @param registerDefaultEditors whether to register default editors
* (can be suppressed if the BeanWrapper won't need any type conversion)
* @see #setWrappedInstance
*/
public BeanWrapperImpl(boolean registerDefaultEditors) {
if (registerDefaultEditors) {
registerDefaultEditors();
}
this.typeConverterDelegate = new TypeConverterDelegate(this);
}
/**
* Create new BeanWrapperImpl for the given object.
* @param object object wrapped by this BeanWrapper
*/
public BeanWrapperImpl(Object object) {
registerDefaultEditors();
setWrappedInstance(object);
}
/**
* Create new BeanWrapperImpl, wrapping a new instance of the specified class.
* @param clazz class to instantiate and wrap
*/
public BeanWrapperImpl(Class clazz) {
registerDefaultEditors();
setWrappedInstance(BeanUtils.instantiateClass(clazz));
}
/**
* Create new BeanWrapperImpl for the given object,
* registering a nested path that the object is in.
* @param object object wrapped by this BeanWrapper
* @param nestedPath the nested path of the object
* @param rootObject the root object at the top of the path
*/
public BeanWrapperImpl(Object object, String nestedPath, Object rootObject) {
registerDefaultEditors();
setWrappedInstance(object, nestedPath, rootObject);
}
/**
* Create new BeanWrapperImpl for the given object,
* registering a nested path that the object is in.
* @param object object wrapped by this BeanWrapper
* @param nestedPath the nested path of the object
* @param superBw the containing BeanWrapper (must not be <code>null</code>)
*/
private BeanWrapperImpl(Object object, String nestedPath, BeanWrapperImpl superBw) {
setWrappedInstance(object, nestedPath, superBw.getWrappedInstance());
setExtractOldValueForEditor(superBw.isExtractOldValueForEditor());
}
//---------------------------------------------------------------------
// Implementation of BeanWrapper interface
//---------------------------------------------------------------------
/**
* Switch the target object, replacing the cached introspection results only
* if the class of the new object is different to that of the replaced object.
* @param object the new target object
*/
public void setWrappedInstance(Object object) {
setWrappedInstance(object, "", null);
}
/**
* Switch the target object, replacing the cached introspection results only
* if the class of the new object is different to that of the replaced object.
* @param object the new target object
* @param nestedPath the nested path of the object
* @param rootObject the root object at the top of the path
*/
public void setWrappedInstance(Object object, String nestedPath, Object rootObject) {
Assert.notNull(object, "Bean object must not be null");
this.object = object;
this.nestedPath = (nestedPath != null ? nestedPath : "");
this.rootObject = (!"".equals(this.nestedPath) ? rootObject : object);
this.nestedBeanWrappers = null;
this.typeConverterDelegate = new TypeConverterDelegate(this, object);
setIntrospectionClass(object.getClass());
}
public final Object getWrappedInstance() {
return this.object;
}
public final Class getWrappedClass() {
return (this.object != null ? this.object.getClass() : null);
}
/**
* Return the nested path of the object wrapped by this BeanWrapper.
*/
public final String getNestedPath() {
return this.nestedPath;
}
/**
* Return the root object at the top of the path of this BeanWrapper.
* @see #getNestedPath
*/
public final Object getRootInstance() {
return this.rootObject;
}
/**
* Return the class of the root object at the top of the path of this BeanWrapper.
* @see #getNestedPath
*/
public final Class getRootClass() {
return (this.rootObject != null ? this.rootObject.getClass() : null);
}
/**
* Set the class to introspect.
* Needs to be called when the target object changes.
* @param clazz the class to introspect
*/
protected void setIntrospectionClass(Class clazz) {
if (this.cachedIntrospectionResults != null &&
!clazz.equals(this.cachedIntrospectionResults.getBeanClass())) {
this.cachedIntrospectionResults = null;
}
}
/**
* Obtain a lazily initializted CachedIntrospectionResults instance
* for the wrapped object.
*/
private CachedIntrospectionResults getCachedIntrospectionResults() {
Assert.state(this.object != null, "BeanWrapper does not hold a bean instance");
if (this.cachedIntrospectionResults == null) {
this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(getWrappedClass());
}
return this.cachedIntrospectionResults;
}
public PropertyDescriptor[] getPropertyDescriptors() {
return getCachedIntrospectionResults().getBeanInfo().getPropertyDescriptors();
}
public PropertyDescriptor getPropertyDescriptor(String propertyName) throws BeansException {
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"No property '" + propertyName + "' found");
}
return pd;
}
/**
* Internal version of {@link #getPropertyDescriptor}:
* Returns <code>null</code> if not found rather than throwing an exception.
* @param propertyName the property to obtain the descriptor for
* @return the property descriptor for the specified property,
* or <code>null</code> if not found
* @throws BeansException in case of introspection failure
*/
protected PropertyDescriptor getPropertyDescriptorInternal(String propertyName) throws BeansException {
Assert.notNull(propertyName, "Property name must not be null");
BeanWrapperImpl nestedBw = getBeanWrapperForPropertyPath(propertyName);
return nestedBw.getCachedIntrospectionResults().getPropertyDescriptor(getFinalPath(nestedBw, propertyName));
}
public Class getPropertyType(String propertyName) throws BeansException {
try {
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
if (pd != null) {
return pd.getPropertyType();
}
else {
// Maybe an indexed/mapped property...
Object value = getPropertyValue(propertyName);
if (value != null) {
return value.getClass();
}
// Check to see if there is a custom editor,
// which might give an indication on the desired target type.
Class editorType = guessPropertyTypeFromEditors(propertyName);
if (editorType != null) {
return editorType;
}
}
}
catch (InvalidPropertyException ex) {
// Consider as not determinable.
}
return null;
}
public boolean isReadableProperty(String propertyName) {
try {
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
if (pd != null) {
if (pd.getReadMethod() != null) {
return true;
}
}
else {
// Maybe an indexed/mapped property...
getPropertyValue(propertyName);
return true;
}
}
catch (InvalidPropertyException ex) {
// Cannot be evaluated, so can't be readable.
}
return false;
}
public boolean isWritableProperty(String propertyName) {
try {
PropertyDescriptor pd = getPropertyDescriptorInternal(propertyName);
if (pd != null) {
if (pd.getWriteMethod() != null) {
return true;
}
}
else {
// Maybe an indexed/mapped property...
getPropertyValue(propertyName);
return true;
}
}
catch (InvalidPropertyException ex) {
// Cannot be evaluated, so can't be writable.
}
return false;
}
/**
* @deprecated in favor of <code>convertIfNecessary</code>
* @see #convertIfNecessary(Object, Class)
*/
public Object doTypeConversionIfNecessary(Object value, Class requiredType) throws TypeMismatchException {
return convertIfNecessary(value, requiredType, null);
}
public Object convertIfNecessary(
Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
catch (IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
/**
* Convert the given value for the specified property to the latter's type.
* <p>This method is only intended for optimizations in a BeanFactory.
* Use the <code>convertIfNecessary</code> methods for programmatic conversion.
* @param value the value to convert
* @param propertyName the target property
* (note that nested or indexed properties are not supported here)
* @return the new value, possibly the result of type conversion
* @throws TypeMismatchException if type conversion failed
*/
public Object convertForProperty(Object value, String propertyName) throws TypeMismatchException {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(propertyName);
if (pd == null) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"No property '" + propertyName + "' found");
}
try {
return this.typeConverterDelegate.convertIfNecessary(null, value, pd);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, null, value);
throw new TypeMismatchException(pce, pd.getPropertyType(), ex);
}
}
//---------------------------------------------------------------------
// Implementation methods
//---------------------------------------------------------------------
/**
* Get the last component of the path. Also works if not nested.
* @param bw BeanWrapper to work on
* @param nestedPath property path we know is nested
* @return last component of the path (the property on the target bean)
*/
private String getFinalPath(BeanWrapper bw, String nestedPath) {
if (bw == this) {
return nestedPath;
}
return nestedPath.substring(PropertyAccessorUtils.getLastNestedPropertySeparatorIndex(nestedPath) + 1);
}
/**
* Recursively navigate to return a BeanWrapper for the nested property path.
* @param propertyPath property property path, which may be nested
* @return a BeanWrapper for the target bean
*/
protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) {
int pos = PropertyAccessorUtils.getFirstNestedPropertySeparatorIndex(propertyPath);
// Handle nested properties recursively.
if (pos > -1) {
String nestedProperty = propertyPath.substring(0, pos);
String nestedPath = propertyPath.substring(pos + 1);
BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);
return nestedBw.getBeanWrapperForPropertyPath(nestedPath);
}
else {
return this;
}
}
/**
* Retrieve a BeanWrapper for the given nested property.
* Create a new one if not found in the cache.
* <p>Note: Caching nested BeanWrappers is necessary now,
* to keep registered custom editors for nested properties.
* @param nestedProperty property to create the BeanWrapper for
* @return the BeanWrapper instance, either cached or newly created
*/
private BeanWrapperImpl getNestedBeanWrapper(String nestedProperty) {
if (this.nestedBeanWrappers == null) {
this.nestedBeanWrappers = new HashMap();
}
// Get value of bean property.
PropertyTokenHolder tokens = getPropertyNameTokens(nestedProperty);
String canonicalName = tokens.canonicalName;
Object propertyValue = getPropertyValue(tokens);
if (propertyValue == null) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + canonicalName);
}
// Lookup cached sub-BeanWrapper, create new one if not found.
BeanWrapperImpl nestedBw = (BeanWrapperImpl) this.nestedBeanWrappers.get(canonicalName);
if (nestedBw == null || nestedBw.getWrappedInstance() != propertyValue) {
if (logger.isTraceEnabled()) {
logger.trace("Creating new nested BeanWrapper for property '" + canonicalName + "'");
}
nestedBw = newNestedBeanWrapper(propertyValue, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR);
// Inherit all type-specific PropertyEditors.
copyDefaultEditorsTo(nestedBw);
copyCustomEditorsTo(nestedBw, canonicalName);
this.nestedBeanWrappers.put(canonicalName, nestedBw);
}
else {
if (logger.isTraceEnabled()) {
logger.trace("Using cached nested BeanWrapper for property '" + canonicalName + "'");
}
}
return nestedBw;
}
/**
* Create a new nested BeanWrapper instance.
* <p>Default implementation creates a BeanWrapperImpl instance.
* Can be overridden in subclasses to create a BeanWrapperImpl subclass.
* @param object object wrapped by this BeanWrapper
* @param nestedPath the nested path of the object
* @return the nested BeanWrapper instance
*/
protected BeanWrapperImpl newNestedBeanWrapper(Object object, String nestedPath) {
return new BeanWrapperImpl(object, nestedPath, this);
}
/**
* Parse the given property name into the corresponding property name tokens.
* @param propertyName the property name to parse
* @return representation of the parsed property tokens
*/
private PropertyTokenHolder getPropertyNameTokens(String propertyName) {
PropertyTokenHolder tokens = new PropertyTokenHolder();
String actualName = null;
List keys = new ArrayList(2);
int searchIndex = 0;
while (searchIndex != -1) {
int keyStart = propertyName.indexOf(PROPERTY_KEY_PREFIX, searchIndex);
searchIndex = -1;
if (keyStart != -1) {
int keyEnd = propertyName.indexOf(PROPERTY_KEY_SUFFIX, keyStart + PROPERTY_KEY_PREFIX.length());
if (keyEnd != -1) {
if (actualName == null) {
actualName = propertyName.substring(0, keyStart);
}
String key = propertyName.substring(keyStart + PROPERTY_KEY_PREFIX.length(), keyEnd);
if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) {
key = key.substring(1, key.length() - 1);
}
keys.add(key);
searchIndex = keyEnd + PROPERTY_KEY_SUFFIX.length();
}
}
}
tokens.actualName = (actualName != null ? actualName : propertyName);
tokens.canonicalName = tokens.actualName;
if (!keys.isEmpty()) {
tokens.canonicalName +=
PROPERTY_KEY_PREFIX +
StringUtils.collectionToDelimitedString(keys, PROPERTY_KEY_SUFFIX + PROPERTY_KEY_PREFIX) +
PROPERTY_KEY_SUFFIX;
tokens.keys = StringUtils.toStringArray(keys);
}
return tokens;
}
//---------------------------------------------------------------------
// Implementation of PropertyAccessor interface
//---------------------------------------------------------------------
public Object getPropertyValue(String propertyName) throws BeansException {
BeanWrapperImpl nestedBw = getBeanWrapperForPropertyPath(propertyName);
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
return nestedBw.getPropertyValue(tokens);
}
private Object getPropertyValue(PropertyTokenHolder tokens) throws BeansException {
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
if (pd == null || pd.getReadMethod() == null) {
throw new NotReadablePropertyException(getRootClass(), this.nestedPath + propertyName);
}
Method readMethod = pd.getReadMethod();
try {
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
Object value = readMethod.invoke(this.object, (Object[]) null);
if (tokens.keys != null) {
// apply indexes and map keys
for (int i = 0; i < tokens.keys.length; i++) {
String key = tokens.keys[i];
if (value == null) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value of property referenced in indexed " +
"property path '" + propertyName + "': returned null");
}
else if (value.getClass().isArray()) {
value = Array.get(value, Integer.parseInt(key));
}
else if (value instanceof List) {
List list = (List) value;
value = list.get(Integer.parseInt(key));
}
else if (value instanceof Set) {
// Apply index to Iterator in case of a Set.
Set set = (Set) value;
int index = Integer.parseInt(key);
if (index < 0 || index >= set.size()) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot get element with index " + index + " from Set of size " +
set.size() + ", accessed using property path '" + propertyName + "'");
}
Iterator it = set.iterator();
for (int j = 0; it.hasNext(); j++) {
Object elem = it.next();
if (j == index) {
value = elem;
break;
}
}
}
else if (value instanceof Map) {
Map map = (Map) value;
Class mapKeyType = null;
if (JdkVersion.isAtLeastJava15()) {
mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(pd.getReadMethod(), i + 1);
}
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
Object convertedMapKey = this.typeConverterDelegate.convertIfNecessary(key, mapKeyType);
// Pass full property name and old value in here, since we want full
// conversion ability for map values.
value = map.get(convertedMapKey);
}
else {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Set nor a Map; returned value was [" + value + "]");
}
}
}
return value;
}
catch (InvocationTargetException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Getter for property '" + actualName + "' threw exception", ex);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Illegal attempt to get property '" + actualName + "' threw exception", ex);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Index of out of bounds in property path '" + propertyName + "'", ex);
}
catch (NumberFormatException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid index in property path '" + propertyName + "'", ex);
}
}
public void setPropertyValue(String propertyName, Object value) throws BeansException {
BeanWrapperImpl nestedBw = null;
try {
nestedBw = getBeanWrapperForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
PropertyTokenHolder tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
nestedBw.setPropertyValue(tokens, new PropertyValue(propertyName, value));
}
public void setPropertyValue(PropertyValue pv) throws BeansException {
PropertyTokenHolder tokens = (PropertyTokenHolder) pv.resolvedTokens;
if (tokens == null) {
String propertyName = pv.getName();
BeanWrapperImpl nestedBw = null;
try {
nestedBw = getBeanWrapperForPropertyPath(propertyName);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Nested property in path '" + propertyName + "' does not exist", ex);
}
tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
if (nestedBw == this) {
pv.getOriginalPropertyValue().resolvedTokens = tokens;
}
nestedBw.setPropertyValue(tokens, pv);
}
else {
setPropertyValue(tokens, pv);
}
}
private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv) throws BeansException {
String propertyName = tokens.canonicalName;
String actualName = tokens.actualName;
if (tokens.keys != null) {
// Apply indexes and map keys: fetch value for all keys but the last one.
PropertyTokenHolder getterTokens = new PropertyTokenHolder();
getterTokens.canonicalName = tokens.canonicalName;
getterTokens.actualName = tokens.actualName;
getterTokens.keys = new String[tokens.keys.length - 1];
System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
Object propValue = null;
try {
propValue = getPropertyValue(getterTokens);
}
catch (NotReadablePropertyException ex) {
throw new NotWritablePropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "'", ex);
}
// Set value for last key.
String key = tokens.keys[tokens.keys.length - 1];
if (propValue == null) {
throw new NullValueInNestedPathException(getRootClass(), this.nestedPath + propertyName,
"Cannot access indexed value in property referenced " +
"in indexed property path '" + propertyName + "': returned null");
}
else if (propValue.getClass().isArray()) {
Class requiredType = propValue.getClass().getComponentType();
int arrayIndex = Integer.parseInt(key);
Object oldValue = null;
try {
if (isExtractOldValueForEditor()) {
oldValue = Array.get(propValue, arrayIndex);
}
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(
propertyName, oldValue, pv.getValue(), requiredType);
Array.set(propValue, Integer.parseInt(key), convertedValue);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, requiredType, ex);
}
catch (IndexOutOfBoundsException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Invalid array index in property path '" + propertyName + "'", ex);
}
}
else if (propValue instanceof List) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
Class requiredType = null;
if (JdkVersion.isAtLeastJava15()) {
requiredType = GenericCollectionTypeResolver.getCollectionReturnType(
pd.getReadMethod(), tokens.keys.length);
}
List list = (List) propValue;
int index = Integer.parseInt(key);
Object oldValue = null;
if (isExtractOldValueForEditor() && index < list.size()) {
oldValue = list.get(index);
}
try {
Object convertedValue = this.typeConverterDelegate.convertIfNecessary(
propertyName, oldValue, pv.getValue(), requiredType);
if (index < list.size()) {
list.set(index, convertedValue);
}
else if (index >= list.size()) {
for (int i = list.size(); i < index; i++) {
try {
list.add(null);
}
catch (NullPointerException ex) {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Cannot set element with index " + index + " in List of size " +
list.size() + ", accessed using property path '" + propertyName +
"': List does not support filling up gaps with null elements");
}
}
list.add(convertedValue);
}
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, requiredType, ex);
}
}
else if (propValue instanceof Map) {
PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
Class mapKeyType = null;
Class mapValueType = null;
if (JdkVersion.isAtLeastJava15()) {
mapKeyType = GenericCollectionTypeResolver.getMapKeyReturnType(
pd.getReadMethod(), tokens.keys.length);
mapValueType = GenericCollectionTypeResolver.getMapValueReturnType(
pd.getReadMethod(), tokens.keys.length);
}
Map map = (Map) propValue;
Object convertedMapKey = null;
Object convertedMapValue = null;
try {
// IMPORTANT: Do not pass full property name in here - property editors
// must not kick in for map keys but rather only for map values.
convertedMapKey = this.typeConverterDelegate.convertIfNecessary(key, mapKeyType);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, null, pv.getValue());
throw new TypeMismatchException(pce, mapKeyType, ex);
}
Object oldValue = null;
if (isExtractOldValueForEditor()) {
oldValue = map.get(convertedMapKey);
}
try {
// Pass full property name and old value in here, since we want full
// conversion ability for map values.
convertedMapValue = this.typeConverterDelegate.convertIfNecessary(
propertyName, oldValue, pv.getValue(), mapValueType, null,
new MethodParameter(pd.getReadMethod(), -1, tokens.keys.length + 1));
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, mapValueType, ex);
}
map.put(convertedMapKey, convertedMapValue);
}
else {
throw new InvalidPropertyException(getRootClass(), this.nestedPath + propertyName,
"Property referenced in indexed property path '" + propertyName +
"' is neither an array nor a List nor a Map; returned value was [" + pv.getValue() + "]");
}
}
else {
PropertyDescriptor pd = pv.resolvedDescriptor;
if (pd == null || !pd.getWriteMethod().getDeclaringClass().isInstance(this.object)) {
pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
if (pd == null || pd.getWriteMethod() == null) {
PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass());
throw new NotWritablePropertyException(
getRootClass(), this.nestedPath + propertyName,
matches.buildErrorMessage(), matches.getPossibleMatches());
}
pv.getOriginalPropertyValue().resolvedDescriptor = pd;
}
Object oldValue = null;
try {
Object originalValue = pv.getValue();
Object valueToApply = originalValue;
if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
if (pv.isConverted()) {
valueToApply = pv.getConvertedValue();
}
else {
if (isExtractOldValueForEditor() && pd.getReadMethod() != null) {
Method readMethod = pd.getReadMethod();
if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())) {
readMethod.setAccessible(true);
}
try {
oldValue = readMethod.invoke(this.object, new Object[0]);
}
catch (Exception ex) {
if (logger.isDebugEnabled()) {
logger.debug("Could not read previous value of property '" +
this.nestedPath + propertyName + "'", ex);
}
}
}
valueToApply = this.typeConverterDelegate.convertIfNecessary(oldValue, originalValue, pd);
}
pv.getOriginalPropertyValue().conversionNecessary = Boolean.valueOf(valueToApply != originalValue);
}
Method writeMethod = pd.getWriteMethod();
if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())) {
writeMethod.setAccessible(true);
}
writeMethod.invoke(this.object, new Object[] {valueToApply});
}
catch (InvocationTargetException ex) {
PropertyChangeEvent propertyChangeEvent =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
if (ex.getTargetException() instanceof ClassCastException) {
throw new TypeMismatchException(propertyChangeEvent, pd.getPropertyType(), ex.getTargetException());
}
else {
throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException());
}
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new TypeMismatchException(pce, pd.getPropertyType(), ex);
}
catch (IllegalAccessException ex) {
PropertyChangeEvent pce =
new PropertyChangeEvent(this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
throw new MethodInvocationException(pce, ex);
}
}
}
public String toString() {
StringBuffer sb = new StringBuffer(getClass().getName());
if (this.object != null) {
sb.append(": wrapping object [").append(ObjectUtils.identityToString(this.object)).append("]");
}
else {
sb.append(": no wrapped object set");
}
return sb.toString();
}
//---------------------------------------------------------------------
// Inner class for internal use
//---------------------------------------------------------------------
private static class PropertyTokenHolder {
public String canonicalName;
public String actualName;
public String[] keys;
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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);
}
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()));
}
public int hashCode() {
return getMessage().hashCode();
}
}

View File

@@ -0,0 +1,273 @@
/*
* 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.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.HashMap;
import java.util.HashSet;
import java.util.Iterator;
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.core.JdkVersion;
import org.springframework.util.ClassUtils;
/**
* 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 acceptedClassLoaders = Collections.synchronizedSet(new HashSet());
/**
* 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 classCache = Collections.synchronizedMap(new WeakHashMap());
/**
* 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 it = classCache.keySet().iterator(); it.hasNext();) {
Class beanClass = (Class) it.next();
if (isUnderneathClassLoader(beanClass.getClassLoader(), classLoader)) {
it.remove();
}
}
}
synchronized (acceptedClassLoaders) {
for (Iterator it = acceptedClassLoaders.iterator(); it.hasNext();) {
ClassLoader registeredLoader = (ClassLoader) 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 = null;
Object value = classCache.get(beanClass);
if (value instanceof Reference) {
Reference ref = (Reference) value;
results = (CachedIntrospectionResults) ref.get();
}
else {
results = (CachedIntrospectionResults) value;
}
if (results == null) {
// can throw BeansException
results = new CachedIntrospectionResults(beanClass);
if (ClassUtils.isCacheSafe(beanClass, CachedIntrospectionResults.class.getClassLoader()) ||
isClassLoaderAccepted(beanClass.getClassLoader())) {
classCache.put(beanClass, results);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not strongly caching class [" + beanClass.getName() + "] because it is not cache-safe");
}
classCache.put(beanClass, new WeakReference(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).
Object[] acceptedLoaderArray = acceptedClassLoaders.toArray();
for (int i = 0; i < acceptedLoaderArray.length; i++) {
ClassLoader registeredLoader = (ClassLoader) acceptedLoaderArray[i];
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 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) throws BeansException {
try {
if (logger.isTraceEnabled()) {
logger.trace("Getting BeanInfo for class [" + beanClass.getName() + "]");
}
this.beanInfo = 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 HashMap();
// This call is slow so we do it once.
PropertyDescriptor[] pds = this.beanInfo.getPropertyDescriptors();
for (int i = 0; i < pds.length; i++) {
PropertyDescriptor pd = pds[i];
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 (JdkVersion.isAtLeastJava15()) {
pd = new GenericTypeAwarePropertyDescriptor(beanClass, pd.getName(),
pd.getReadMethod(), pd.getWriteMethod(), pd.getPropertyEditorClass());
}
this.propertyDescriptorCache.put(pd.getName(), pd);
}
}
catch (IntrospectionException ex) {
throw new FatalBeanException("Cannot get BeanInfo for object of class [" + beanClass.getName() + "]", ex);
}
}
BeanInfo getBeanInfo() {
return this.beanInfo;
}
Class getBeanClass() {
return this.beanInfo.getBeanDescriptor().getBeanClass();
}
PropertyDescriptor getPropertyDescriptor(String propertyName) {
return (PropertyDescriptor) this.propertyDescriptorCache.get(propertyName);
}
}

View File

@@ -0,0 +1,44 @@
/*
* 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 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 {
/**
* 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,136 @@
/*
* 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.PropertyChangeEvent;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import org.springframework.core.MethodParameter;
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 fieldMap = new HashMap();
private final TypeConverterDelegate typeConverterDelegate;
/**
* Create a new DirectFieldAccessor for the given target object.
* @param target the target object to access
*/
public DirectFieldAccessor(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) {
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);
}
public Class getPropertyType(String propertyName) throws BeansException {
Field field = (Field) this.fieldMap.get(propertyName);
if (field != null) {
return field.getType();
}
return null;
}
public Object getPropertyValue(String propertyName) throws BeansException {
Field 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);
}
}
public void setPropertyValue(String propertyName, Object newValue) throws BeansException {
Field 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(propertyName, oldValue, newValue, field.getType());
field.set(this.target, convertedValue);
}
catch (IllegalAccessException ex) {
throw new InvalidPropertyException(this.target.getClass(), propertyName, "Field is not accessible", ex);
}
catch (IllegalArgumentException ex) {
PropertyChangeEvent pce = new PropertyChangeEvent(this.target, propertyName, oldValue, newValue);
throw new TypeMismatchException(pce, field.getType(), ex);
}
}
public Object convertIfNecessary(
Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
catch (IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
}

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,114 @@
/*
* 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.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
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 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;
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()), new Class[] {readMethodToUse.getReturnType()});
}
this.readMethod = readMethodToUse;
this.writeMethod = writeMethodToUse;
this.propertyEditorClass = propertyEditorClass;
}
public Method getReadMethod() {
return this.readMethod;
}
public Method getWriteMethod() {
return this.writeMethod;
}
public Class getPropertyEditorClass() {
return this.propertyEditorClass;
}
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,350 @@
/*
* 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.io.Serializable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
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 {
/** List of PropertyValue objects */
private final List propertyValueList;
private Set processedProperties;
private volatile boolean converted = false;
/**
* Creates a new empty MutablePropertyValues object.
* Property values can be added with the <code>addPropertyValue</code> methods.
* @see #addPropertyValue(PropertyValue)
* @see #addPropertyValue(String, Object)
*/
public MutablePropertyValues() {
this.propertyValueList = new ArrayList();
}
/**
* 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(pvs.length);
for (int i = 0; i < pvs.length; i++) {
PropertyValue newPv = new PropertyValue(pvs[i]);
this.propertyValueList.add(newPv);
}
}
else {
this.propertyValueList = new ArrayList(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(original.size());
Iterator it = original.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
PropertyValue newPv = new PropertyValue((String) entry.getKey(), entry.getValue());
this.propertyValueList.add(newPv);
}
}
else {
this.propertyValueList = new ArrayList(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 propertyValueList) {
this.propertyValueList = (propertyValueList != null ? propertyValueList : new ArrayList());
}
/**
* 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 getPropertyValueList() {
return this.propertyValueList;
}
/**
* 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 object to allow creating objects, adding multiple PropertyValues
* in a single statement
*/
public MutablePropertyValues addPropertyValues(PropertyValues other) {
if (other != null) {
PropertyValue[] pvs = other.getPropertyValues();
for (int i = 0; i < pvs.length; i++) {
PropertyValue newPv = new PropertyValue(pvs[i]);
addPropertyValue(newPv);
}
}
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 object to allow creating objects, adding multiple
* PropertyValues in a single statement
*/
public MutablePropertyValues addPropertyValues(Map other) {
if (other != null) {
Iterator it = other.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
PropertyValue newPv = new PropertyValue((String) entry.getKey(), entry.getValue());
addPropertyValue(newPv);
}
}
return this;
}
/**
* Add a PropertyValue object, replacing any existing one
* for the corresponding property.
* @param pv PropertyValue object to add
* @return this object to allow creating objects, adding multiple
* PropertyValues in a single statement
*/
public MutablePropertyValues addPropertyValue(PropertyValue pv) {
for (int i = 0; i < this.propertyValueList.size(); i++) {
PropertyValue currentPv = (PropertyValue) 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.
* @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));
}
/**
* 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;
}
/**
* 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) {
removePropertyValue(getPropertyValue(propertyName));
}
/**
* Remove the given PropertyValue, if contained.
* @param pv the PropertyValue to remove
*/
public void removePropertyValue(PropertyValue pv) {
this.propertyValueList.remove(pv);
}
/**
* Clear this holder, removing all PropertyValues.
*/
public void clear() {
this.propertyValueList.clear();
}
public PropertyValue[] getPropertyValues() {
return (PropertyValue[])
this.propertyValueList.toArray(new PropertyValue[this.propertyValueList.size()]);
}
public PropertyValue getPropertyValue(String propertyName) {
for (int i = 0; i < this.propertyValueList.size(); i++) {
PropertyValue pv = (PropertyValue) this.propertyValueList.get(i);
if (pv.getName().equals(propertyName)) {
return pv;
}
}
return null;
}
/**
* 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();
}
this.processedProperties.add(propertyName);
}
public boolean contains(String propertyName) {
return (getPropertyValue(propertyName) != null ||
(this.processedProperties != null && this.processedProperties.contains(propertyName)));
}
public boolean isEmpty() {
return this.propertyValueList.isEmpty();
}
public int size() {
return this.propertyValueList.size();
}
public PropertyValues changesSince(PropertyValues old) {
MutablePropertyValues changes = new MutablePropertyValues();
if (old == this) {
return changes;
}
// for each property value in the new set
for (Iterator it = this.propertyValueList.iterator(); it.hasNext();) {
PropertyValue newPv = (PropertyValue) it.next();
// 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;
}
/**
* 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;
}
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);
}
public int hashCode() {
return this.propertyValueList.hashCode();
}
public String toString() {
PropertyValue[] pvs = getPropertyValues();
StringBuffer sb = new StringBuffer("PropertyValues: length=" + pvs.length + "; ");
sb.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,65 @@
/*
* 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.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.
* May be <code>null</code>; only available if an actual bean property
* was affected.
*/
public PropertyChangeEvent getPropertyChangeEvent() {
return this.propertyChangeEvent;
}
}

View File

@@ -0,0 +1,204 @@
/*
* 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.util.Map;
/**
* 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;
/**
* 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,184 @@
/*
* 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;
/**
* 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.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX_CHAR);
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 "";
}
StringBuffer buf = new StringBuffer(propertyName);
int searchIndex = 0;
while (searchIndex != -1) {
int keyStart = buf.indexOf(PropertyAccessor.PROPERTY_KEY_PREFIX, searchIndex);
searchIndex = -1;
if (keyStart != -1) {
int keyEnd = buf.indexOf(
PropertyAccessor.PROPERTY_KEY_SUFFIX, keyStart + PropertyAccessor.PROPERTY_KEY_PREFIX.length());
if (keyEnd != -1) {
String key = buf.substring(keyStart + PropertyAccessor.PROPERTY_KEY_PREFIX.length(), keyEnd);
if ((key.startsWith("'") && key.endsWith("'")) || (key.startsWith("\"") && key.endsWith("\""))) {
buf.delete(keyStart + 1, keyStart + 2);
buf.delete(keyEnd - 2, keyEnd - 1);
keyEnd = keyEnd - 2;
}
searchIndex = keyEnd + PropertyAccessor.PROPERTY_KEY_SUFFIX.length();
}
}
}
return buf.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,143 @@
/*
* 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.io.PrintStream;
import java.io.PrintWriter;
import org.springframework.util.Assert;
/**
* 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.
* Will return the empty array (not null) 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 one.
*/
public PropertyAccessException getPropertyAccessException(String propertyName) {
for (int i = 0; i < this.propertyAccessExceptions.length; i++) {
PropertyAccessException pae = this.propertyAccessExceptions[i];
if (propertyName.equals(pae.getPropertyChangeEvent().getPropertyName())) {
return pae;
}
}
return null;
}
public String getMessage() {
StringBuffer sb = new StringBuffer("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();
}
public String toString() {
StringBuffer sb = new StringBuffer();
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();
}
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);
}
}
}
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);
}
}
}
public boolean contains(Class exType) {
if (exType == null) {
return false;
}
if (exType.isInstance(this)) {
return true;
}
for (int i = 0; i < this.propertyAccessExceptions.length; i++) {
PropertyAccessException pae = this.propertyAccessExceptions[i];
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,539 @@
/*
* 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.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.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.regex.Pattern;
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.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.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.URIEditor;
import org.springframework.beans.propertyeditors.URLEditor;
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 boolean defaultEditorsActive = false;
private boolean configValueEditorsActive = false;
private boolean propertySpecificEditorsRegistered = false;
private Map defaultEditors;
private Map customEditors;
private Set sharedEditors;
private Map customEditorCache;
//---------------------------------------------------------------------
// 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;
}
/**
* 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.defaultEditors == null) {
doRegisterDefaultEditors();
}
return (PropertyEditor) this.defaultEditors.get(requiredType);
}
/**
* Actually register the default editors for this registry instance.
* @see org.springframework.beans.propertyeditors.ByteArrayPropertyEditor
* @see org.springframework.beans.propertyeditors.ClassEditor
* @see org.springframework.beans.propertyeditors.CharacterEditor
* @see org.springframework.beans.propertyeditors.CustomBooleanEditor
* @see org.springframework.beans.propertyeditors.CustomNumberEditor
* @see org.springframework.beans.propertyeditors.CustomCollectionEditor
* @see org.springframework.beans.propertyeditors.CustomMapEditor
* @see org.springframework.beans.propertyeditors.FileEditor
* @see org.springframework.beans.propertyeditors.InputStreamEditor
* @see org.springframework.jndi.JndiTemplateEditor
* @see org.springframework.beans.propertyeditors.LocaleEditor
* @see org.springframework.beans.propertyeditors.PropertiesEditor
* @see org.springframework.beans.PropertyValuesEditor
* @see org.springframework.core.io.support.ResourceArrayPropertyEditor
* @see org.springframework.core.io.ResourceEditor
* @see org.springframework.transaction.interceptor.TransactionAttributeEditor
* @see org.springframework.transaction.interceptor.TransactionAttributeSourceEditor
* @see org.springframework.beans.propertyeditors.URLEditor
*/
private void doRegisterDefaultEditors() {
this.defaultEditors = new HashMap(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(File.class, new FileEditor());
this.defaultEditors.put(InputStream.class, new InputStreamEditor());
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(URI.class, new URIEditor());
this.defaultEditors.put(URL.class, new URLEditor());
// 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.defaultEditors = this.defaultEditors;
target.defaultEditorsActive = this.defaultEditorsActive;
target.configValueEditorsActive = this.configValueEditorsActive;
}
//---------------------------------------------------------------------
// 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 (this.customEditors == null) {
this.customEditors = new LinkedHashMap(16);
}
if (propertyPath != null) {
this.customEditors.put(propertyPath, new CustomEditorHolder(propertyEditor, requiredType));
this.propertySpecificEditorsRegistered = true;
}
else {
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
*/
public void registerSharedEditor(Class requiredType, PropertyEditor propertyEditor) {
registerCustomEditor(requiredType, null, propertyEditor);
if (this.sharedEditors == null) {
this.sharedEditors = new HashSet();
}
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) {
if (this.customEditors == null) {
return null;
}
Class requiredTypeToUse = requiredType;
if (propertyPath != null) {
if (this.propertySpecificEditorsRegistered) {
// Check property-specific editor first.
PropertyEditor editor = getCustomEditor(propertyPath, requiredType);
if (editor == null) {
List strippedPaths = new LinkedList();
addStrippedPropertyPaths(strippedPaths, "", propertyPath);
for (Iterator it = strippedPaths.iterator(); it.hasNext() && editor == null;) {
String strippedPath = (String) 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 (this.customEditors == null) {
return false;
}
if (propertyPath != null && this.propertySpecificEditorsRegistered) {
for (Iterator it = this.customEditors.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
if (entry.getKey() instanceof String) {
String regPath = (String) entry.getKey();
if (PropertyAccessorUtils.matchesProperty(regPath, propertyPath)) {
CustomEditorHolder editorHolder = (CustomEditorHolder) entry.getValue();
if (editorHolder.getPropertyEditor(elementType) != null) {
return true;
}
}
}
}
}
// No property-specific editor -> check type-specific editor.
return (elementType != 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 = (CustomEditorHolder) this.customEditors.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) {
return null;
}
// Check directly registered editor for type.
PropertyEditor editor = (PropertyEditor) this.customEditors.get(requiredType);
if (editor == null) {
// Check cached editor for type, registered for superclass or interface.
if (this.customEditorCache != null) {
editor = (PropertyEditor) this.customEditorCache.get(requiredType);
}
if (editor == null) {
// Find editor for superclass or interface.
for (Iterator it = this.customEditors.keySet().iterator(); it.hasNext() && editor == null;) {
Object key = it.next();
if (key instanceof Class && ((Class) key).isAssignableFrom(requiredType)) {
editor = (PropertyEditor) 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();
}
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.customEditors != null) {
CustomEditorHolder editorHolder = (CustomEditorHolder) this.customEditors.get(propertyName);
if (editorHolder == null) {
List strippedPaths = new LinkedList();
addStrippedPropertyPaths(strippedPaths, "", propertyName);
for (Iterator it = strippedPaths.iterator(); it.hasNext() && editorHolder == null;) {
String strippedName = (String) it.next();
editorHolder = (CustomEditorHolder) this.customEditors.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 (Iterator it = this.customEditors.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
if (entry.getKey() instanceof Class) {
Class requiredType = (Class) entry.getKey();
PropertyEditor editor = (PropertyEditor) entry.getValue();
target.registerCustomEditor(requiredType, editor);
}
else if (entry.getKey() instanceof String) {
String editorPath = (String) entry.getKey();
CustomEditorHolder editorHolder = (CustomEditorHolder) 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 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-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.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() {
StringBuffer buf = new StringBuffer();
buf.append("Bean property '");
buf.append(this.propertyName);
buf.append("' is not writable or has an invalid setter method. ");
if (ObjectUtils.isEmpty(this.possibleMatches)) {
buf.append("Does the parameter type of the setter match the return type of the getter?");
}
else {
buf.append("Did you mean ");
for (int i = 0; i < this.possibleMatches.length; i++) {
buf.append('\'');
buf.append(this.possibleMatches[i]);
if (i < this.possibleMatches.length - 2) {
buf.append("', ");
}
else if (i == this.possibleMatches.length - 2){
buf.append("', or ");
}
}
buf.append("'?");
}
return buf.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 candidates = new ArrayList();
for (int i = 0; i < propertyDescriptors.length; i++) {
if (propertyDescriptors[i].getWriteMethod() != null) {
String possibleAlternative = propertyDescriptors[i].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,184 @@
/*
* 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.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 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.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.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;
}
/**
* 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;
}
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));
}
public int hashCode() {
return this.name.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.value);
}
public String toString() {
return "bean property '" + this.name + "'";
}
}

View File

@@ -0,0 +1,64 @@
/*
* 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;
/**
* 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);
/**
* 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();
/**
* 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);
}

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;
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();
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,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;
import org.springframework.core.MethodParameter;
/**
* 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 Object convertIfNecessary(Object value, Class requiredType) throws TypeMismatchException {
return convertIfNecessary(value, requiredType, null);
}
public Object convertIfNecessary(
Object value, Class requiredType, MethodParameter methodParam) throws TypeMismatchException {
try {
return this.typeConverterDelegate.convertIfNecessary(value, requiredType, methodParam);
}
catch (IllegalArgumentException ex) {
throw new TypeMismatchException(value, requiredType, ex);
}
}
}

View File

@@ -0,0 +1,68 @@
/*
* 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 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()
*/
Object convertIfNecessary(Object value, Class 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()
*/
Object convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam)
throws TypeMismatchException;
}

View File

@@ -0,0 +1,542 @@
/*
* 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.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Map;
import java.util.WeakHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.CollectionFactory;
import org.springframework.core.GenericCollectionTypeResolver;
import org.springframework.core.JdkVersion;
import org.springframework.core.MethodParameter;
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 static final Map unknownEditorTypes = Collections.synchronizedMap(new WeakHashMap());
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)
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public Object convertIfNecessary(Object newValue, Class requiredType) throws IllegalArgumentException {
return convertIfNecessary(null, null, newValue, requiredType, null, null);
}
/**
* 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 Object convertIfNecessary(Object newValue, Class requiredType, MethodParameter methodParam)
throws IllegalArgumentException {
return convertIfNecessary(null, null, newValue, requiredType, null, methodParam);
}
/**
* 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 Object convertIfNecessary(
String propertyName, Object oldValue, Object newValue, Class requiredType)
throws IllegalArgumentException {
return convertIfNecessary(propertyName, oldValue, newValue, requiredType, null, null);
}
/**
* Convert the value to the required type for the specified property.
* @param oldValue the previous value, if available (may be <code>null</code>)
* @param newValue the proposed new value
* @param descriptor the JavaBeans descriptor for the property
* @return the new value, possibly the result of type conversion
* @throws IllegalArgumentException if type conversion failed
*/
public Object convertIfNecessary(Object oldValue, Object newValue, PropertyDescriptor descriptor)
throws IllegalArgumentException {
return convertIfNecessary(
descriptor.getName(), oldValue, newValue, descriptor.getPropertyType(), descriptor,
BeanUtils.getWriteMethodParameter(descriptor));
}
/**
* 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 descriptor the JavaBeans descriptor for the property
* @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
*/
protected Object convertIfNecessary(
String propertyName, Object oldValue, Object newValue, Class requiredType,
PropertyDescriptor descriptor, MethodParameter methodParam)
throws IllegalArgumentException {
Object convertedValue = newValue;
// Custom editor for this type?
PropertyEditor editor = this.propertyEditorRegistry.findCustomEditor(requiredType, propertyName);
// Value not of required type?
if (editor != null || (requiredType != null && !ClassUtils.isAssignableValue(requiredType, convertedValue))) {
if (editor == null) {
editor = findDefaultEditor(requiredType, descriptor);
}
convertedValue = doConvertValue(oldValue, convertedValue, requiredType, editor);
}
if (requiredType != null) {
// Try to apply some standard type conversion rules if appropriate.
if (convertedValue != null) {
if (String.class.equals(requiredType) && ClassUtils.isPrimitiveOrWrapper(convertedValue.getClass())) {
// We can stringify any primitive value...
return convertedValue.toString();
}
else if (requiredType.isArray()) {
// Array required -> apply appropriate conversion of elements.
return convertToTypedArray(convertedValue, propertyName, requiredType.getComponentType());
}
else if (convertedValue instanceof Collection && CollectionFactory.isApproximableCollectionType(requiredType)) {
// Convert elements to target type, if determined.
convertedValue = convertToTypedCollection((Collection) convertedValue, propertyName, methodParam);
}
else if (convertedValue instanceof Map && CollectionFactory.isApproximableMapType(requiredType)) {
// Convert keys and values to respective target type, if determined.
convertedValue = convertToTypedMap((Map) convertedValue, propertyName, methodParam);
}
else if (convertedValue instanceof String && !requiredType.isInstance(convertedValue)) {
String strValue = ((String) convertedValue).trim();
if (JdkVersion.isAtLeastJava15() && requiredType.isEnum() && "".equals(strValue)) {
// It's an empty enum identifier: reset the enum value to null.
return null;
}
// 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(strValue);
convertedValue = enumField.get(null);
}
catch (Throwable ex) {
if (logger.isTraceEnabled()) {
logger.trace("Field [" + convertedValue + "] isn't an enum value", ex);
}
}
}
}
if (!ClassUtils.isAssignableValue(requiredType, convertedValue)) {
// Definitely doesn't match: throw IllegalArgumentException.
StringBuffer msg = new StringBuffer();
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 '" + propertyName + "'");
}
if (editor != null) {
msg.append(": PropertyEditor [" + editor.getClass().getName() + "] returned inappropriate value");
}
else {
msg.append(": no matching editors or conversion strategy found");
}
throw new IllegalArgumentException(msg.toString());
}
}
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, PropertyDescriptor descriptor) {
PropertyEditor editor = null;
if (descriptor != null) {
if (JdkVersion.isAtLeastJava15()) {
editor = descriptor.createPropertyEditor(this.targetObject);
}
else {
Class editorClass = descriptor.getPropertyEditorClass();
if (editorClass != null) {
editor = (PropertyEditor) BeanUtils.instantiateClass(editorClass);
}
}
}
if (editor == null && requiredType != null) {
// No custom editor -> check BeanWrapperImpl's default editors.
editor = (PropertyEditor) this.propertyEditorRegistry.getDefaultEditor(requiredType);
if (editor == null && !String.class.equals(requiredType)) {
// No BeanWrapper default editor -> check standard JavaBean editor.
editor = BeanUtils.findEditorByConvention(requiredType);
if (editor == null && !unknownEditorTypes.containsKey(requiredType)) {
// Deprecated global PropertyEditorManager fallback...
editor = PropertyEditorManager.findEditor(requiredType);
if (editor == null) {
// Regular case as of Spring 2.5
unknownEditorTypes.put(requiredType, Boolean.TRUE);
}
else {
logger.warn("PropertyEditor [" + editor.getClass().getName() +
"] found through deprecated global PropertyEditorManager fallback - " +
"consider using a more isolated form of registration, e.g. on the BeanWrapper/BeanFactory!");
}
}
}
}
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 = null;
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.
}
}
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 (editor != null && convertedValue instanceof String) {
// 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);
}
}
return convertedValue;
}
/**
* 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;
}
}
protected Collection convertToTypedCollection(
Collection original, String propertyName, MethodParameter methodParam) {
Class elementType = null;
if (methodParam != null && JdkVersion.isAtLeastJava15()) {
elementType = GenericCollectionTypeResolver.getCollectionParameterType(methodParam);
}
if (elementType == null &&
!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
return original;
}
Collection convertedCopy = null;
Iterator it = null;
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;
}
convertedCopy = CollectionFactory.createApproximateCollection(original, original.size());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot access Collection of type [" + original.getClass().getName() +
"] - injecting original Collection as-is", ex);
}
return original;
}
boolean actuallyConverted = false;
int i = 0;
for (; it.hasNext(); i++) {
Object element = it.next();
String indexedPropertyName = buildIndexedPropertyName(propertyName, i);
if (methodParam != null) {
methodParam.increaseNestingLevel();
}
Object convertedElement =
convertIfNecessary(indexedPropertyName, null, element, elementType, null, methodParam);
if (methodParam != null) {
methodParam.decreaseNestingLevel();
}
convertedCopy.add(convertedElement);
actuallyConverted = actuallyConverted || (element != convertedElement);
}
return (actuallyConverted ? convertedCopy : original);
}
protected Map convertToTypedMap(Map original, String propertyName, MethodParameter methodParam) {
Class keyType = null;
Class valueType = null;
if (methodParam != null && JdkVersion.isAtLeastJava15()) {
keyType = GenericCollectionTypeResolver.getMapKeyParameterType(methodParam);
valueType = GenericCollectionTypeResolver.getMapValueParameterType(methodParam);
}
if (keyType == null && valueType == null &&
!this.propertyEditorRegistry.hasCustomEditorForElement(null, propertyName)) {
return original;
}
Map convertedCopy = null;
Iterator it = null;
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");
}
}
convertedCopy = CollectionFactory.createApproximateMap(original, original.size());
}
catch (Throwable ex) {
if (logger.isDebugEnabled()) {
logger.debug("Cannot access Map of type [" + original.getClass().getName() +
"] - injecting original Map as-is", ex);
}
return original;
}
boolean actuallyConverted = false;
while (it.hasNext()) {
Map.Entry entry = (Map.Entry) it.next();
Object key = entry.getKey();
Object value = entry.getValue();
String keyedPropertyName = buildKeyedPropertyName(propertyName, key);
if (methodParam != null) {
methodParam.increaseNestingLevel();
methodParam.setTypeIndexForCurrentLevel(0);
}
Object convertedKey = convertIfNecessary(keyedPropertyName, null, key, keyType, null, methodParam);
if (methodParam != null) {
methodParam.setTypeIndexForCurrentLevel(1);
}
Object convertedValue = convertIfNecessary(keyedPropertyName, null, value, valueType, null, methodParam);
if (methodParam != null) {
methodParam.decreaseNestingLevel();
}
convertedCopy.put(convertedKey, convertedValue);
actuallyConverted = actuallyConverted || (key != convertedKey) || (value != convertedValue);
}
return (actuallyConverted ? convertedCopy : original);
}
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);
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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.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>)
*/
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,7 @@
<html>
<body>
Support package for beans-style handling of Java 5 annotations.
</body>
</html>

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;
/**
* 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
* @since 2.0
* @see BeanNameAware
* @see BeanFactoryAware
* @see InitializingBean
*/
public interface BeanClassLoaderAware {
/**
* 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,203 @@
/*
* 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.Iterator;
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 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();
}
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 (Throwable[]) this.relatedCauses.toArray(new Throwable[this.relatedCauses.size()]);
}
public String toString() {
StringBuffer sb = new StringBuffer(super.toString());
if (this.relatedCauses != null) {
for (Iterator it = this.relatedCauses.iterator(); it.hasNext();) {
Throwable relatedCause = (Throwable) it.next();
sb.append("\nRelated cause: ");
sb.append(relatedCause);
}
}
return sb.toString();
}
public void printStackTrace(PrintStream ps) {
synchronized (ps) {
super.printStackTrace(ps);
if (this.relatedCauses != null) {
for (Iterator it = this.relatedCauses.iterator(); it.hasNext();) {
Throwable relatedCause = (Throwable) it.next();
ps.println("Related cause:");
relatedCause.printStackTrace(ps);
}
}
}
}
public void printStackTrace(PrintWriter pw) {
synchronized (pw) {
super.printStackTrace(pw);
if (this.relatedCauses != null) {
for (Iterator it = this.relatedCauses.iterator(); it.hasNext();) {
Throwable relatedCause = (Throwable) it.next();
pw.println("Related cause:");
relatedCause.printStackTrace(pw);
}
}
}
}
public boolean contains(Class exClass) {
if (super.contains(exClass)) {
return true;
}
if (this.relatedCauses != null) {
for (Iterator it = this.relatedCauses.iterator(); it.hasNext();) {
Throwable relatedCause = (Throwable) it.next();
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,259 @@
/*
* 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;
/**
* 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
* @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
*/
Object getBean(String name, Class 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 with the given name? More specifically,
* is {@link #getBean} able to obtain a bean instance for the given name?
* <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 a bean with the given name is defined
*/
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,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.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
* @since 11.03.2003
* @see BeanNameAware
* @see BeanClassLoaderAware
* @see InitializingBean
* @see org.springframework.context.ApplicationContextAware
*/
public interface BeanFactoryAware {
/**
* 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,421 @@
/*
* 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.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
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.indexOf(GENERATED_BEAN_NAME_SEPARATOR) != -1);
}
/**
* 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 resultList = new ArrayList();
resultList.addAll(Arrays.asList(result));
for (int i = 0; i < parentResult.length; i++) {
String beanName = parentResult[i];
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 resultList = new ArrayList();
resultList.addAll(Arrays.asList(result));
for (int i = 0; i < parentResult.length; i++) {
String beanName = parentResult[i];
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.
* @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 Map beansOfTypeIncludingAncestors(ListableBeanFactory lbf, Class type)
throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map result = new LinkedHashMap(4);
result.putAll(lbf.getBeansOfType(type));
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
Map parentResult = beansOfTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type);
for (Iterator it = parentResult.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
String beanName = (String) 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).
* @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 Map beansOfTypeIncludingAncestors(
ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map result = new LinkedHashMap(4);
result.putAll(lbf.getBeansOfType(type, includeNonSingletons, allowEagerInit));
if (lbf instanceof HierarchicalBeanFactory) {
HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
Map parentResult = beansOfTypeIncludingAncestors(
(ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
for (Iterator it = parentResult.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
String beanName = (String) 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.
* @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 Object beanOfTypeIncludingAncestors(ListableBeanFactory lbf, Class type)
throws BeansException {
Map 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).
* @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 Object beanOfTypeIncludingAncestors(
ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
Map 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 Object beanOfType(ListableBeanFactory lbf, Class type) throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map 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 Object beanOfType(
ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException {
Assert.notNull(lbf, "ListableBeanFactory must not be null");
Map 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,36 @@
/*
* 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 instance has been requested for a bean
* which has been defined 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,51 @@
/*
* 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;
/**
* 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
* @since 01.11.2003
* @see BeanClassLoaderAware
* @see BeanFactoryAware
* @see InitializingBean
*/
public interface BeanNameAware {
/**
* 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-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;
/**
* 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 {
/**
* 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
*/
Object 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,213 @@
/*
* 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.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)
*/
Map getBeansOfType(Class 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)
*/
Map getBeansOfType(Class type, boolean includeNonSingletons, boolean allowEagerInit)
throws BeansException;
}

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,95 @@
/*
* 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;
/**
* 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
* @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 [" +
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-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;
/**
* 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 {
/**
* 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
*/
Object getObject() throws BeansException;
}

View File

@@ -0,0 +1,75 @@
/*
* 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;
/**
* 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 extends FactoryBean {
/**
* 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,540 @@
/*
* 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.BeanFactoryUtils;
import org.springframework.beans.factory.ListableBeanFactory;
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 Map instances = new HashMap();
/**
* 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 = (BeanFactoryLocator) 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 bfgInstancesByKey = new HashMap();
private final Map bfgInstancesByObj = new HashMap();
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 = (BeanFactoryGroup) 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 = null;
if (factoryKey != null) {
beanFactory = (BeanFactory) bfg.definition.getBean(factoryKey, BeanFactory.class);
}
else if (bfg.definition instanceof ListableBeanFactory) {
beanFactory = (BeanFactory)
BeanFactoryUtils.beanOfType((ListableBeanFactory) bfg.definition, BeanFactory.class);
}
else {
throw new IllegalStateException(
"Factory key is null, and underlying factory is not a ListableBeanFactory: " + bfg.definition);
}
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 = (BeanFactoryGroup) 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,49 @@
/*
* 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;
}
protected BeanFactory getBeanFactory(ELContext elContext) {
return this.beanFactory;
}
}

View File

@@ -0,0 +1,111 @@
/*
* 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());
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;
}
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;
}
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");
}
}
}
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;
}
public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext elContext, Object base) {
return null;
}
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,7 @@
<html>
<body>
Support classes for accessing a Spring BeanFactory from Unified EL.
</body>
</html>

View File

@@ -0,0 +1,11 @@
<html>
<body>
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>
</body>
</html>

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,81 @@
/*
* 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.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 that autowiring information was not specified.
* In some cases it may be necessary to specify autowiring status,
* but merely confirm that this should be inherited from an enclosing
* container definition scope.
*/
INHERITED(-1),
/**
* 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,67 @@
/*
* 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.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>Please do 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
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.CONSTRUCTOR, ElementType.FIELD, ElementType.METHOD})
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,548 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.annotation;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
import org.springframework.beans.factory.config.RuntimeBeanReference;
import org.springframework.beans.factory.support.MergedBeanDefinitionPostProcessor;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.core.GenericTypeResolver;
import org.springframework.core.MethodParameter;
import org.springframework.core.Ordered;
import org.springframework.core.PriorityOrdered;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor} implementation
* that autowires annotated fields, setter methods and arbitrary config methods.
* Such members to be injected are detected through a Java 5 annotation:
* by default, Spring's {@link Autowired} annotation.
*
* <p>Only one constructor (at max) of any given bean class may carry this
* annotation with the 'required' parameter set to <code>true</code>,
* indicating <i>the</i> constructor to autowire when used as a Spring bean.
* If multiple <i>non-required</i> constructors carry the annotation, they
* will be considered as candidates for autowiring. The constructor with
* the greatest number of dependencies that can be satisfied by matching
* beans in the Spring container will be chosen. If none of the candidates
* can be satisfied, then a default constructor (if present) will be used.
* An annotated constructor does not have to be public.
*
* <p>Fields are injected right after construction of a bean, before any
* config methods are invoked. Such a config field does not have to be public.
*
* <p>Config methods may have an arbitrary name and any number of arguments;
* each of those arguments will be autowired with a matching bean in the
* Spring container. Bean property setter methods are effectively just
* a special case of such a general config method. Such config methods
* do not have to be public.
*
* <p>Note: A default AutowiredAnnotationBeanPostProcessor will be registered
* by the "context:annotation-config" and "context:component-scan" XML tags.
* Remove or turn off the default annotation configuration there if you intend
* to specify a custom AutowiredAnnotationBeanPostProcessor bean definition.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @since 2.5
* @see #setAutowiredAnnotationType
* @see Autowired
* @see org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
*/
public class AutowiredAnnotationBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter
implements MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
protected final Log logger = LogFactory.getLog(AutowiredAnnotationBeanPostProcessor.class);
private Class<? extends Annotation> autowiredAnnotationType = Autowired.class;
private String requiredParameterName = "required";
private boolean requiredParameterValue = true;
private int order = Ordered.LOWEST_PRECEDENCE - 2;
private ConfigurableListableBeanFactory beanFactory;
private final Map<Class<?>, Constructor[]> candidateConstructorsCache =
new ConcurrentHashMap<Class<?>, Constructor[]>();
private final Map<Class<?>, InjectionMetadata> injectionMetadataCache =
new ConcurrentHashMap<Class<?>, InjectionMetadata>();
/**
* Set the 'autowired' annotation type, to be used on constructors, fields,
* setter methods and arbitrary config methods.
* <p>The default autowired annotation type is the Spring-provided
* {@link Autowired} annotation.
* <p>This setter property exists so that developers can provide their own
* (non-Spring-specific) annotation type to indicate that a member is
* supposed to be autowired.
*/
public void setAutowiredAnnotationType(Class<? extends Annotation> autowiredAnnotationType) {
Assert.notNull(autowiredAnnotationType, "'autowiredAnnotationType' must not be null");
this.autowiredAnnotationType = autowiredAnnotationType;
}
/**
* Return the 'autowired' annotation type.
*/
protected Class<? extends Annotation> getAutowiredAnnotationType() {
return this.autowiredAnnotationType;
}
/**
* Set the name of a parameter of the annotation that specifies
* whether it is required.
* @see #setRequiredParameterValue(boolean)
*/
public void setRequiredParameterName(String requiredParameterName) {
this.requiredParameterName = requiredParameterName;
}
/**
* Set the boolean value that marks a dependency as required
* <p>For example if using 'required=true' (the default),
* this value should be <code>true</code>; but if using
* 'optional=false', this value should be <code>false</code>.
* @see #setRequiredParameterName(String)
*/
public void setRequiredParameterValue(boolean requiredParameterValue) {
this.requiredParameterValue = requiredParameterValue;
}
public void setOrder(int order) {
this.order = order;
}
public int getOrder() {
return this.order;
}
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (!(beanFactory instanceof ConfigurableListableBeanFactory)) {
throw new IllegalArgumentException(
"AutowiredAnnotationBeanPostProcessor requires a ConfigurableListableBeanFactory");
}
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
if (beanType != null) {
InjectionMetadata metadata = findAutowiringMetadata(beanType);
metadata.checkConfigMembers(beanDefinition);
}
}
public Constructor[] determineCandidateConstructors(Class beanClass, String beanName) throws BeansException {
// Quick check on the concurrent map first, with minimal locking.
Constructor[] candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
synchronized (this.candidateConstructorsCache) {
candidateConstructors = this.candidateConstructorsCache.get(beanClass);
if (candidateConstructors == null) {
Constructor[] rawCandidates = beanClass.getDeclaredConstructors();
List<Constructor> candidates = new ArrayList<Constructor>(rawCandidates.length);
Constructor requiredConstructor = null;
Constructor defaultConstructor = null;
for (int i = 0; i < rawCandidates.length; i++) {
Constructor<?> candidate = rawCandidates[i];
Annotation annotation = candidate.getAnnotation(getAutowiredAnnotationType());
if (annotation != null) {
if (requiredConstructor != null) {
throw new BeanCreationException("Invalid autowire-marked constructor: " + candidate +
". Found another constructor with 'required' Autowired annotation: " + requiredConstructor);
}
if (candidate.getParameterTypes().length == 0) {
throw new IllegalStateException("Autowired annotation requires at least one argument: " + candidate);
}
boolean required = determineRequiredStatus(annotation);
if (required) {
if (!candidates.isEmpty()) {
throw new BeanCreationException("Invalid autowire-marked constructors: " + candidates +
". Found another constructor with 'required' Autowired annotation: " + requiredConstructor);
}
requiredConstructor = candidate;
}
candidates.add(candidate);
}
else if (candidate.getParameterTypes().length == 0) {
defaultConstructor = candidate;
}
}
if (!candidates.isEmpty()) {
// Add default constructor to list of optional constructors, as fallback.
if (requiredConstructor == null && defaultConstructor != null) {
candidates.add(defaultConstructor);
}
candidateConstructors = (Constructor[]) candidates.toArray(new Constructor[candidates.size()]);
}
else {
candidateConstructors = new Constructor[0];
}
this.candidateConstructorsCache.put(beanClass, candidateConstructors);
}
}
}
return (candidateConstructors.length > 0 ? candidateConstructors : null);
}
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
try {
metadata.injectFields(bean, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Autowiring of fields failed", ex);
}
return true;
}
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
try {
metadata.injectMethods(bean, beanName, pvs);
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Autowiring of methods failed", ex);
}
return pvs;
}
/**
* 'Native' processing method for direct calls with an arbitrary target
* instance, resolving all of its fields and methods which are annotated
* with <code>@Autowired</code>.
* @param bean the target instance to process
*/
public void processInjection(Object bean) throws BeansException {
InjectionMetadata metadata = findAutowiringMetadata(bean.getClass());
try {
metadata.injectFields(bean, null);
metadata.injectMethods(bean, null, null);
}
catch (Throwable ex) {
throw new BeanCreationException("Autowiring of fields/methods failed", ex);
}
}
private InjectionMetadata findAutowiringMetadata(final Class clazz) {
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(clazz);
if (metadata == null) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(clazz);
if (metadata == null) {
final InjectionMetadata newMetadata = new InjectionMetadata(clazz);
ReflectionUtils.doWithFields(clazz, new ReflectionUtils.FieldCallback() {
public void doWith(Field field) {
Annotation annotation = field.getAnnotation(getAutowiredAnnotationType());
if (annotation != null) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("Autowired annotation is not supported on static fields");
}
boolean required = determineRequiredStatus(annotation);
newMetadata.addInjectedField(new AutowiredFieldElement(field, required));
}
}
});
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
Annotation annotation = method.getAnnotation(getAutowiredAnnotationType());
if (annotation != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("Autowired annotation is not supported on static methods");
}
if (method.getParameterTypes().length == 0) {
throw new IllegalStateException("Autowired annotation requires at least one argument: " + method);
}
boolean required = determineRequiredStatus(annotation);
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(method);
newMetadata.addInjectedMethod(new AutowiredMethodElement(method, required, pd));
}
}
});
metadata = newMetadata;
this.injectionMetadataCache.put(clazz, metadata);
}
}
}
return metadata;
}
/**
* Obtain all beans of the given type as autowire candidates.
* @param type the type of the bean
* @return the target beans, or an empty Collection if no bean of this type is found
* @throws BeansException if bean retrieval failed
*/
protected Map findAutowireCandidates(Class type) throws BeansException {
if (this.beanFactory == null) {
throw new IllegalStateException("No BeanFactory configured - " +
"override the getBeanOfType method or specify the 'beanFactory' property");
}
return BeanFactoryUtils.beansOfTypeIncludingAncestors(this.beanFactory, type);
}
/**
* Determine if the annotated field or method requires its dependency.
* <p>A 'required' dependency means that autowiring should fail when no beans
* are found. Otherwise, the autowiring process will simply bypass the field
* or method when no beans are found.
* @param annotation the Autowired annotation
* @return whether the annotation indicates that a dependency is required
*/
protected boolean determineRequiredStatus(Annotation annotation) {
try {
Method method = ReflectionUtils.findMethod(annotation.annotationType(), this.requiredParameterName);
return (this.requiredParameterValue == (Boolean) ReflectionUtils.invokeMethod(method, annotation));
}
catch (Exception ex) {
// required by default
return true;
}
}
/**
* Register the specified bean as dependent on the autowired beans.
*/
private void registerDependentBeans(String beanName, Set<String> autowiredBeanNames) {
if (beanName != null) {
for (Iterator it = autowiredBeanNames.iterator(); it.hasNext();) {
String autowiredBeanName = (String) it.next();
beanFactory.registerDependentBean(autowiredBeanName, beanName);
if (logger.isDebugEnabled()) {
logger.debug("Autowiring by type from bean name '" + beanName +
"' to bean named '" + autowiredBeanName + "'");
}
}
}
}
/**
* Class representing injection information about an annotated field.
*/
private class AutowiredFieldElement extends InjectionMetadata.InjectedElement {
private final boolean required;
private volatile boolean cached = false;
private volatile Object cachedFieldValue;
public AutowiredFieldElement(Field field, boolean required) {
super(field, null);
this.required = required;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
Field field = (Field) this.member;
try {
Object value = null;
if (this.cached) {
if (this.cachedFieldValue instanceof DependencyDescriptor) {
DependencyDescriptor descriptor = (DependencyDescriptor) this.cachedFieldValue;
TypeConverter typeConverter = beanFactory.getTypeConverter();
value = beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
}
else if (this.cachedFieldValue instanceof RuntimeBeanReference) {
value = beanFactory.getBean(((RuntimeBeanReference) this.cachedFieldValue).getBeanName());
}
else {
value = this.cachedFieldValue;
}
}
else {
Set<String> autowiredBeanNames = new LinkedHashSet<String>(1);
TypeConverter typeConverter = beanFactory.getTypeConverter();
DependencyDescriptor descriptor = new DependencyDescriptor(field, this.required);
this.cachedFieldValue = descriptor;
value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);
if (value != null) {
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName)) {
if (beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new RuntimeBeanReference(autowiredBeanName);
}
}
}
}
else {
this.cachedFieldValue = null;
}
this.cached = true;
}
if (value != null) {
ReflectionUtils.makeAccessible(field);
field.set(bean, value);
}
}
catch (Throwable ex) {
throw new BeanCreationException("Could not autowire field: " + field, ex);
}
}
}
/**
* Class representing injection information about an annotated method.
*/
private class AutowiredMethodElement extends InjectionMetadata.InjectedElement {
private final boolean required;
private volatile boolean cached = false;
private volatile Object[] cachedMethodArguments;
public AutowiredMethodElement(Method method, boolean required, PropertyDescriptor pd) {
super(method, pd);
this.required = required;
}
@Override
protected void inject(Object bean, String beanName, PropertyValues pvs) throws Throwable {
if (this.skip == null && this.pd != null && pvs != null && pvs.contains(this.pd.getName())) {
// Explicit value provided as part of the bean definition.
this.skip = Boolean.TRUE;
}
if (this.skip != null && this.skip.booleanValue()) {
return;
}
Method method = (Method) this.member;
try {
Object[] arguments = null;
if (this.cached) {
if (this.cachedMethodArguments != null) {
arguments = new Object[this.cachedMethodArguments.length];
for (int i = 0; i < arguments.length; i++) {
Object cachedArg = this.cachedMethodArguments[i];
if (cachedArg instanceof DependencyDescriptor) {
DependencyDescriptor descriptor = (DependencyDescriptor) cachedArg;
TypeConverter typeConverter = beanFactory.getTypeConverter();
arguments[i] = beanFactory.resolveDependency(descriptor, beanName, null, typeConverter);
}
else if (cachedArg instanceof RuntimeBeanReference) {
arguments[i] = beanFactory.getBean(((RuntimeBeanReference) cachedArg).getBeanName());
}
else {
arguments[i] = cachedArg;
}
}
}
}
else {
Class[] paramTypes = method.getParameterTypes();
arguments = new Object[paramTypes.length];
Set<String> autowiredBeanNames = new LinkedHashSet<String>(arguments.length);
TypeConverter typeConverter = beanFactory.getTypeConverter();
this.cachedMethodArguments = new Object[arguments.length];
for (int i = 0; i < arguments.length; i++) {
MethodParameter methodParam = new MethodParameter(method, i);
GenericTypeResolver.resolveParameterType(methodParam, bean.getClass());
DependencyDescriptor descriptor = new DependencyDescriptor(methodParam, this.required);
this.cachedMethodArguments[i] = descriptor;
arguments[i] = beanFactory.resolveDependency(
descriptor, beanName, autowiredBeanNames, typeConverter);
if (arguments[i] == null) {
arguments = null;
break;
}
}
if (arguments != null) {
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == paramTypes.length) {
Iterator<String> it = autowiredBeanNames.iterator();
for (int i = 0; i < paramTypes.length; i++) {
String autowiredBeanName = it.next();
if (beanFactory.containsBean(autowiredBeanName)) {
if (beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
this.cachedMethodArguments[i] = new RuntimeBeanReference(autowiredBeanName);
}
}
else {
this.cachedMethodArguments[i] = arguments[i];
}
}
}
}
else {
this.cachedMethodArguments = null;
}
this.cached = true;
}
if (this.skip == null) {
if (this.pd != null && pvs instanceof MutablePropertyValues) {
((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
}
this.skip = Boolean.FALSE;
}
if (arguments != null) {
ReflectionUtils.makeAccessible(method);
method.invoke(bean, arguments);
}
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
catch (Throwable ex) {
throw new BeanCreationException("Could not autowire method: " + method, ex);
}
}
}
}

View File

@@ -0,0 +1,63 @@
/*
* 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.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
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Inherited
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,122 @@
/*
* 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.Annotation;
import java.util.Iterator;
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;
}
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)) {
throw new IllegalStateException(
"CustomAutowireConfigurer needs to operate on a QualifierAnnotationAutowireCandidateResolver");
}
QualifierAnnotationAutowireCandidateResolver resolver =
(QualifierAnnotationAutowireCandidateResolver) dlbf.getAutowireCandidateResolver();
for (Iterator it = this.customQualifierTypes.iterator(); it.hasNext();) {
Class customType = null;
Object value = it.next();
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,310 @@
/*
* 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.io.Serializable;
import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Iterator;
import java.util.LinkedHashSet;
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
* @see org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
*/
public class InitDestroyAnnotationBeanPostProcessor
implements DestructionAwareBeanPostProcessor, MergedBeanDefinitionPostProcessor, PriorityOrdered, Serializable {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private Class<? extends Annotation> initAnnotationType;
private Class<? extends Annotation> destroyAnnotationType;
private int order = Ordered.LOWEST_PRECEDENCE - 1;
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);
for (Iterator<LifecycleElement> it = metadata.getInitMethods().iterator(); it.hasNext();) {
String methodName = it.next().getMethod().getName();
if (!beanDefinition.isExternallyManagedInitMethod(methodName)) {
beanDefinition.registerExternallyManagedInitMethod(methodName);
}
else {
it.remove();
}
}
for (Iterator<LifecycleElement> it = metadata.getDestroyMethods().iterator(); it.hasNext();) {
String methodName = it.next().getMethod().getName();
if (!beanDefinition.isExternallyManagedDestroyMethod(methodName)) {
beanDefinition.registerExternallyManagedDestroyMethod(methodName);
}
else {
it.remove();
}
}
}
}
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(final Class clazz) {
final LifecycleMetadata newMetadata = new LifecycleMetadata();
final boolean debug = logger.isDebugEnabled();
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
public void doWith(Method method) {
if (initAnnotationType != null) {
if (method.getAnnotation(initAnnotationType) != null) {
newMetadata.addInitMethod(method);
if (debug) {
logger.debug("Found init method on class [" + clazz.getName() + "]: " + method);
}
}
}
if (destroyAnnotationType != null) {
if (method.getAnnotation(destroyAnnotationType) != null) {
newMetadata.addDestroyMethod(method);
if (debug) {
logger.debug("Found destroy method on class [" + clazz.getName() + "]: " + method);
}
}
}
}
});
return newMetadata;
}
/**
* Class representing information about annotated init and destroy methods.
*/
private class LifecycleMetadata {
private final Set<LifecycleElement> initMethods = new LinkedHashSet<LifecycleElement>();
private final Set<LifecycleElement> destroyMethods = new LinkedHashSet<LifecycleElement>();
public void addInitMethod(Method method) {
this.initMethods.add(new LifecycleElement(method));
}
public Set<LifecycleElement> getInitMethods() {
return this.initMethods;
}
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 addDestroyMethod(Method method) {
this.destroyMethods.add(new LifecycleElement(method));
}
public Set<LifecycleElement> getDestroyMethods() {
return this.destroyMethods;
}
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;
public LifecycleElement(Method method) {
if (method.getParameterTypes().length != 0) {
throw new IllegalStateException("Lifecycle method annotation requires a no-arg method: " + method);
}
this.method = method;
}
public Method getMethod() {
return this.method;
}
public void invoke(Object target) throws Throwable {
ReflectionUtils.makeAccessible(this.method);
this.method.invoke(target, (Object[]) null);
}
public boolean equals(Object other) {
return (this == other || (other instanceof LifecycleElement &&
this.method.getName().equals(((LifecycleElement) other).method.getName())));
}
public int hashCode() {
return this.method.getName().hashCode();
}
}
}

View File

@@ -0,0 +1,253 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.beans.factory.annotation;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Arrays;
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 String targetClassName;
private final Set<InjectedElement> injectedFields = new LinkedHashSet<InjectedElement>();
private final Set<InjectedElement> injectedMethods = new LinkedHashSet<InjectedElement>();
public InjectionMetadata() {
}
public InjectionMetadata(Class targetClass) {
this.targetClassName = targetClass.getName();
}
public void addInjectedField(InjectedElement element) {
if (logger.isDebugEnabled()) {
logger.debug("Found injected field on class [" + this.targetClassName + "]: " + element);
}
this.injectedFields.add(element);
}
public void addInjectedMethod(InjectedElement element) {
if (logger.isDebugEnabled()) {
logger.debug("Found injected method on class [" + this.targetClassName + "]: " + element);
}
this.injectedMethods.add(element);
}
public void checkConfigMembers(RootBeanDefinition beanDefinition) {
doRegisterConfigMembers(beanDefinition, this.injectedFields);
doRegisterConfigMembers(beanDefinition, this.injectedMethods);
}
private void doRegisterConfigMembers(RootBeanDefinition beanDefinition, Set<InjectedElement> members) {
for (Iterator<InjectedElement> it = members.iterator(); it.hasNext();) {
Member member = it.next().getMember();
if (!beanDefinition.isExternallyManagedConfigMember(member)) {
beanDefinition.registerExternallyManagedConfigMember(member);
}
else {
it.remove();
}
}
}
public void injectFields(Object target, String beanName) throws Throwable {
if (!this.injectedFields.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (InjectedElement element : this.injectedFields) {
if (debug) {
logger.debug("Processing injected field of bean '" + beanName + "': " + element);
}
element.inject(target, beanName, null);
}
}
}
public void injectMethods(Object target, String beanName, PropertyValues pvs) throws Throwable {
if (!this.injectedMethods.isEmpty()) {
boolean debug = logger.isDebugEnabled();
for (InjectedElement element : this.injectedMethods) {
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 (this.skip == null) {
this.skip = Boolean.valueOf(checkPropertySkipping(pvs));
}
if (this.skip.booleanValue()) {
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.pd != null && pvs != null) {
if (pvs.contains(this.pd.getName())) {
// Explicit value provided as part of the bean definition.
return true;
}
else if (pvs instanceof MutablePropertyValues) {
((MutablePropertyValues) pvs).registerProcessedProperty(this.pd.getName());
}
}
return false;
}
/**
* Either this or {@link #inject} needs to be overridden.
*/
protected Object getResourceToInject(Object target, String requestingBeanName) {
return null;
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof InjectedElement)) {
return false;
}
InjectedElement otherElement = (InjectedElement) other;
if (this.isField) {
return this.member.equals(otherElement.member);
}
else {
return (otherElement.member instanceof Method &&
this.member.getName().equals(otherElement.member.getName()) &&
Arrays.equals(((Method) this.member).getParameterTypes(),
((Method) otherElement.member).getParameterTypes()));
}
}
public int hashCode() {
return this.member.getClass().hashCode() * 29 + this.member.getName().hashCode();
}
public String toString() {
return getClass().getSimpleName() + " for " + this.member;
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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.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
*/
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Qualifier {
String value() default "";
}

View File

@@ -0,0 +1,179 @@
/*
* 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.Annotation;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.SimpleTypeConverter;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.config.DependencyDescriptor;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.AutowireCandidateQualifier;
import org.springframework.beans.factory.support.AutowireCandidateResolver;
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 qualifier annotations on the field or parameter to be autowired.
*
* @author Mark Fisher
* @author Juergen Hoeller
* @since 2.5
* @see AutowireCandidateQualifier
* @see Qualifier
*/
public class QualifierAnnotationAutowireCandidateResolver implements AutowireCandidateResolver {
private final Set<Class<? extends Annotation>> qualifierTypes;
/**
* Create a new QualifierAnnotationAutowireCandidateResolver
* for Spring's standard {@link Qualifier} annotation.
*/
public QualifierAnnotationAutowireCandidateResolver() {
this.qualifierTypes = new HashSet<Class<? extends Annotation>>(1);
this.qualifierTypes.add(Qualifier.class);
}
/**
* 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 = new HashSet<Class<? extends Annotation>>(1);
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 = new HashSet<Class<? extends Annotation>>(qualifierTypes);
}
/**
* Register the given type to be used as a qualifier when autowiring.
* <p>This implementation only supports annotations as qualifier types.
* @param qualifierType the annotation type to register
*/
public void addQualifierType(Class<? extends Annotation> qualifierType) {
this.qualifierTypes.add(qualifierType);
}
/**
* Determine if 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.
*/
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 || ObjectUtils.isEmpty(descriptor.getAnnotations())) {
// no qualification necessary
return true;
}
AbstractBeanDefinition bd = (AbstractBeanDefinition) bdHolder.getBeanDefinition();
SimpleTypeConverter typeConverter = new SimpleTypeConverter();
Annotation[] annotations = (Annotation[]) descriptor.getAnnotations();
for (Annotation annotation : annotations) {
Class<? extends Annotation> type = annotation.annotationType();
if (isQualifier(type)) {
AutowireCandidateQualifier qualifier = bd.getQualifier(type.getName());
if (qualifier == null) {
qualifier = bd.getQualifier(ClassUtils.getShortName(type));
}
if (qualifier == null && bd.hasBeanClass()) {
// look for matching annotation on the target class
Class<?> beanClass = bd.getBeanClass();
Annotation targetAnnotation = beanClass.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.equals(bdHolder.getBeanName()) ||
ObjectUtils.containsElement(bdHolder.getAliases(), 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;
}
/**
* Checks whether the given annotation type is a recognized qualifier type.
*/
private boolean isQualifier(Class<? extends Annotation> annotationType) {
for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
return true;
}
}
return false;
}
}

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,169 @@
/*
* 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.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.BeanInitializationException;
import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
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 PriorityOrdered {
private Class<? extends Annotation> requiredAnnotationType = Required.class;
private int order = Ordered.LOWEST_PRECEDENCE - 1;
/** 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 setOrder(int order) {
this.order = order;
}
public int getOrder() {
return this.order;
}
public PropertyValues postProcessPropertyValues(
PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName)
throws BeansException {
if (!this.validatedBeanNames.contains(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;
}
/**
* 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,7 @@
<html>
<body>
Support package for annotation-driven bean configuration.
</body>
</html>

View File

@@ -0,0 +1,262 @@
/*
* 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 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
implements FactoryBean, 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 Object singletonInstance;
private Object earlySingletonInstance;
/**
* Set if a singleton should be created, or a new object
* on each request else. 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 Object 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.
*/
private Object 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 = 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 Object 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 Object 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(Object instance) throws Exception {
}
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] ? Boolean.TRUE : Boolean.FALSE);
}
else if (ReflectionUtils.isHashCodeMethod(method)) {
// Use hashCode of reference proxy.
return new Integer(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,317 @@
/*
* 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 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
*/
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
*/
Object createBean(Class 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
* @see #AUTOWIRE_AUTODETECT
*/
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 autowiredBeanNames, TypeConverter typeConverter) throws BeansException;
}

View File

@@ -0,0 +1,218 @@
/*
* 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.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 specifed factory bean, if any,
* or as static method on the local bean class else.
* @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.
*/
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 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 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.
*/
boolean isSingleton();
/**
* Return whether this bean is "abstract", that is, not meant to be instantiated.
*/
boolean isAbstract();
/**
* Return whether this bean should be lazily initialized, that is, not
* eagerly instantiated on startup.
*/
boolean isLazyInit();
/**
* 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,173 @@
/*
* 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.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();
}
/**
* Return a friendly, short description for the bean, stating name and aliases.
* @see #getBeanName()
* @see #getAliases()
*/
public String getShortDescription() {
StringBuffer sb = new StringBuffer();
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() {
StringBuffer sb = new StringBuffer(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()
*/
public String toString() {
return getLongDescription();
}
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);
}
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,268 @@
/*
* 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 java.util.Iterator;
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
* @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.getFactoryBeanName();
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 (int i = 0; i < pvArray.length; i++) {
PropertyValue pv = pvArray[i];
Object newVal = resolveValue(pv.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {
pvs.addPropertyValue(pv.getName(), newVal);
}
}
}
protected void visitIndexedArgumentValues(Map ias) {
for (Iterator it = ias.values().iterator(); it.hasNext();) {
ConstructorArgumentValues.ValueHolder valueHolder =
(ConstructorArgumentValues.ValueHolder) it.next();
Object newVal = resolveValue(valueHolder.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, valueHolder.getValue())) {
valueHolder.setValue(newVal);
}
}
}
protected void visitGenericArgumentValues(List gas) {
for (Iterator it = gas.iterator(); it.hasNext();) {
ConstructorArgumentValues.ValueHolder valueHolder =
(ConstructorArgumentValues.ValueHolder) it.next();
Object newVal = resolveValue(valueHolder.getValue());
if (!ObjectUtils.nullSafeEquals(newVal, valueHolder.getValue())) {
valueHolder.setValue(newVal);
}
}
}
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 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 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);
}
}
}
protected void visitSet(Set setVal) {
Set newContent = new LinkedHashSet();
boolean entriesModified = false;
for (Iterator it = setVal.iterator(); it.hasNext();) {
Object elem = it.next();
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);
}
}
protected void visitMap(Map mapVal) {
Map newContent = new LinkedHashMap();
boolean entriesModified = false;
for (Iterator it = mapVal.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
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");
}
return this.valueResolver.resolveStringValue(strVal);
}
}

View File

@@ -0,0 +1,51 @@
/*
* 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.
*
* @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;
}

View File

@@ -0,0 +1,78 @@
/*
* 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.config;
import org.springframework.beans.BeansException;
/**
* Factory hook that allows for custom modification of new bean instances,
* e.g. checking for marker interfaces or wrapping them with proxies.
*
* <p>ApplicationContexts can autodetect BeanPostProcessor beans in their
* bean definitions and apply them to any beans subsequently created.
* Plain bean factories allow for programmatic registration of post-processors,
* applying to all beans created through this factory.
*
* <p>Typically, post-processors that populate beans via marker interfaces
* or the like will implement {@link #postProcessBeforeInitialization},
* while post-processors that wrap beans with proxies will normally
* implement {@link #postProcessAfterInitialization}.
*
* @author Juergen Hoeller
* @since 10.10.2003
* @see InstantiationAwareBeanPostProcessor
* @see DestructionAwareBeanPostProcessor
* @see ConfigurableBeanFactory#addBeanPostProcessor
* @see BeanFactoryPostProcessor
*/
public interface BeanPostProcessor {
/**
* Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
* initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* @param bean 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 org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
* initialization callbacks (like InitializingBean's <code>afterPropertiesSet</code>
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding <code>bean instanceof FactoryBean</code> checks.
* <p>This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other BeanPostProcessor callbacks.
* @param bean 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 org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

View File

@@ -0,0 +1,40 @@
/*
* 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.config;
import org.springframework.beans.BeanMetadataElement;
/**
* Interface that exposes a reference to a bean name in an abstract fashion.
* This interface does not necessarily imply a reference to an actual bean
* instance; it just expresses a logical reference to the name of a bean.
*
* <p>Serves as common interface implemented by any kind of bean reference
* holder, such as {@link RuntimeBeanReference RuntimeBeanReference} and
* {@link RuntimeBeanNameReference RuntimeBeanNameReference}.
*
* @author Juergen Hoeller
* @since 2.0
*/
public interface BeanReference extends BeanMetadataElement {
/**
* Return the target bean name that this reference points to (never <code>null</code>).
*/
String getBeanName();
}

View File

@@ -0,0 +1,108 @@
/*
* 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.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.FactoryBeanNotInitializedException;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.SmartFactoryBean;
/**
* FactoryBean that exposes an arbitrary target bean under a different name.
*
* <p>Usually, the target bean will reside in a different bean definition file,
* using this FactoryBean to link it in and expose it under a different name.
* Effectively, this corresponds to an alias for the target bean.
*
* <p><b>NOTE:</b> For XML bean definition files, an <code>&lt;alias&gt;</code>
* tag is available that effectively achieves the same.
*
* <p>A special capability of this FactoryBean is enabled through its configuration
* as bean definition: The "targetBeanName" can be substituted through a placeholder,
* in combination with Spring's {@link PropertyPlaceholderConfigurer}.
* Thanks to Marcus Bristav for pointing this out!
*
* @author Juergen Hoeller
* @since 1.2
* @see #setTargetBeanName
* @see PropertyPlaceholderConfigurer
*/
public class BeanReferenceFactoryBean implements SmartFactoryBean, BeanFactoryAware {
private String targetBeanName;
private BeanFactory beanFactory;
/**
* Set the name of the target bean.
* <p>This property is required. The value for this property can be
* substituted through a placeholder, in combination with Spring's
* PropertyPlaceholderConfigurer.
* @param targetBeanName the name of the target bean
* @see PropertyPlaceholderConfigurer
*/
public void setTargetBeanName(String targetBeanName) {
this.targetBeanName = targetBeanName;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
if (this.targetBeanName == null) {
throw new IllegalArgumentException("'targetBeanName' is required");
}
if (!this.beanFactory.containsBean(this.targetBeanName)) {
throw new NoSuchBeanDefinitionException(this.targetBeanName, this.beanFactory.toString());
}
}
public Object getObject() throws BeansException {
if (this.beanFactory == null) {
throw new FactoryBeanNotInitializedException();
}
return this.beanFactory.getBean(this.targetBeanName);
}
public Class getObjectType() {
if (this.beanFactory == null) {
return null;
}
return this.beanFactory.getType(this.targetBeanName);
}
public boolean isSingleton() {
if (this.beanFactory == null) {
throw new FactoryBeanNotInitializedException();
}
return this.beanFactory.isSingleton(this.targetBeanName);
}
public boolean isPrototype() {
if (this.beanFactory == null) {
throw new FactoryBeanNotInitializedException();
}
return this.beanFactory.isPrototype(this.targetBeanName);
}
public boolean isEagerInit() {
return false;
}
}

View File

@@ -0,0 +1,69 @@
/*
* 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.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* Factory bean for
* <a href="http://jakarta.apache.org/commons/logging.html">commons-logging</a>
* {@link org.apache.commons.logging.Log} instances.
*
* <p>Useful for sharing Log instances among multiple beans instead of using
* one Log instance per class name, e.g. for common log topics.
*
* @author Juergen Hoeller
* @see org.apache.commons.logging.Log
* @since 16.11.2003
*/
public class CommonsLogFactoryBean implements FactoryBean, InitializingBean {
private Log log;
/**
* The name of the log.
* <p>This property is required.
* @param logName the name of the log
*/
public void setLogName(String logName) {
this.log = LogFactory.getLog(logName);
}
public void afterPropertiesSet() {
if (this.log == null) {
throw new IllegalArgumentException("logName is required");
}
}
public Object getObject() {
return log;
}
public Class getObjectType() {
return (this.log != null ? this.log.getClass() : Log.class);
}
public boolean isSingleton() {
return true;
}
}

View File

@@ -0,0 +1,329 @@
/*
* 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 java.beans.PropertyEditor;
import org.springframework.beans.PropertyEditorRegistrar;
import org.springframework.beans.PropertyEditorRegistry;
import org.springframework.beans.TypeConverter;
import org.springframework.beans.factory.BeanDefinitionStoreException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.HierarchicalBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.util.StringValueResolver;
/**
* Configuration interface to be implemented by most bean factories. Provides
* facilities to configure a bean factory, in addition to the bean factory
* client methods in the {@link org.springframework.beans.factory.BeanFactory}
* interface.
*
* <p>This bean factory interface 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
* needs. This extended interface is just meant to allow for framework-internal
* plug'n'play and for special access to bean factory configuration methods.
*
* @author Juergen Hoeller
* @since 03.11.2003
* @see org.springframework.beans.factory.BeanFactory
* @see org.springframework.beans.factory.ListableBeanFactory
* @see ConfigurableListableBeanFactory
*/
public interface ConfigurableBeanFactory extends HierarchicalBeanFactory, SingletonBeanRegistry {
/**
* Scope identifier for the standard singleton scope: "singleton".
* Custom scopes can be added via <code>registerScope</code>.
* @see #registerScope
*/
String SCOPE_SINGLETON = "singleton";
/**
* Scope identifier for the standard prototype scope: "prototype".
* Custom scopes can be added via <code>registerScope</code>.
* @see #registerScope
*/
String SCOPE_PROTOTYPE = "prototype";
/**
* Set the parent of this bean factory.
* <p>Note that the parent cannot be changed: It should only be set outside
* a constructor if it isn't available at the time of factory instantiation.
* @param parentBeanFactory the parent BeanFactory
* @throws IllegalStateException if this factory is already associated with
* a parent BeanFactory
* @see #getParentBeanFactory()
*/
void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;
/**
* Set the class loader to use for loading bean classes.
* Default is the thread context class loader.
* <p>Note that this class loader will only apply to bean definitions
* that do not carry a resolved bean class yet. This is the case as of
* Spring 2.0 by default: Bean definitions only carry bean class names,
* to be resolved once the factory processes the bean definition.
* @param beanClassLoader the class loader to use,
* or <code>null</code> to suggest the default class loader
*/
void setBeanClassLoader(ClassLoader beanClassLoader);
/**
* Return this factory's class loader for loading bean classes.
*/
ClassLoader getBeanClassLoader();
/**
* Specify a temporary ClassLoader to use for type matching purposes.
* Default is none, simply using the standard bean ClassLoader.
* <p>A temporary ClassLoader is usually just specified if
* <i>load-time weaving</i> is involved, to make sure that actual bean
* classes are loaded as lazily as possible. The temporary loader is
* then removed once the BeanFactory completes its bootstrap phase.
*/
void setTempClassLoader(ClassLoader tempClassLoader);
/**
* Return the temporary ClassLoader to use for type matching purposes,
* if any.
*/
ClassLoader getTempClassLoader();
/**
* Set whether to cache bean metadata such as given bean definitions
* (in merged fashion) and resolved bean classes. Default is on.
* <p>Turn this flag off to enable hot-refreshing of bean definition objects
* and in particular bean classes. If this flag is off, any creation of a bean
* instance will re-query the bean class loader for newly resolved classes.
*/
void setCacheBeanMetadata(boolean cacheBeanMetadata);
/**
* Return whether to cache bean metadata such as given bean definitions
* (in merged fashion) and resolved bean classes.
*/
boolean isCacheBeanMetadata();
/**
* Add a PropertyEditorRegistrar to be applied to all bean creation processes.
* <p>Such a registrar creates new PropertyEditor instances and registers them
* on the given registry, fresh for each bean creation attempt. This avoids
* the need for synchronization on custom editors; hence, it is generally
* preferable to use this method instead of {@link #registerCustomEditor}.
* @param registrar the PropertyEditorRegistrar to register
*/
void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);
/**
* Register the given custom property editor for all properties of the
* given type. To be invoked during factory configuration.
* <p>Note that this method will register a shared custom editor instance;
* access to that instance will be synchronized for thread-safety. It is
* generally preferable to use {@link #addPropertyEditorRegistrar} instead
* of this method, to avoid for the need for synchronization on custom editors.
* @param requiredType type of the property
* @param propertyEditorClass the {@link PropertyEditor} class to register
*/
void registerCustomEditor(Class requiredType, Class propertyEditorClass);
/**
* Register the given custom property editor for all properties of the
* given type. To be invoked during factory configuration.
* <p>Note that this method will register a shared custom editor instance;
* access to that instance will be synchronized for thread-safety. It is
* generally preferable to use {@link #addPropertyEditorRegistrar} instead
* of this method, to avoid for the need for synchronization on custom editors.
* @param requiredType type of the property
* @param propertyEditor editor to register
* @deprecated as of Spring 2.0.7, in favor of {@link #addPropertyEditorRegistrar}
* and {@link #registerCustomEditor(Class, Class)}
*/
void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor);
/**
* Initialize the given PropertyEditorRegistry with the custom editors
* that have been registered with this BeanFactory.
* @param registry the PropertyEditorRegistry to initialize
*/
void copyRegisteredEditorsTo(PropertyEditorRegistry registry);
/**
* Set a custom type converter that this BeanFactory should use for converting
* bean property values, constructor argument values, etc.
* <p>This will override the default PropertyEditor mechanism and hence make
* any custom editors or custom editor registrars irrelevant.
* @see #addPropertyEditorRegistrar
* @see #registerCustomEditor
*/
void setTypeConverter(TypeConverter typeConverter);
/**
* Obtain a type converter as used by this BeanFactory. This may be a fresh
* instance for each call, since TypeConverters are usually <i>not</i> thread-safe.
* <p>If the default PropertyEditor mechanism is active, the returned
* TypeConverter will be aware of all custom editors that have been registered.
*/
TypeConverter getTypeConverter();
/**
* Add a new BeanPostProcessor that will get applied to beans created
* by this factory. To be invoked during factory configuration.
* @param beanPostProcessor the bean processor to register
*/
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);
/**
* Return the current number of registered BeanPostProcessors, if any.
*/
int getBeanPostProcessorCount();
/**
* Register the given scope, backed by the given Scope implementation.
* @param scopeName the scope identifier
* @param scope the backing Scope implementation
*/
void registerScope(String scopeName, Scope scope);
/**
* Return the names of all currently registered scopes.
* <p>This will only return the names of explicitly registered scopes.
* Built-in scopes such as "singleton" and "prototype" won't be exposed.
* @return the array of scope names, or an empty array if none
* @see #registerScope
*/
String[] getRegisteredScopeNames();
/**
* Return the Scope implementation for the given scope name, if any.
* <p>This will only return explicitly registered scopes.
* Built-in scopes such as "singleton" and "prototype" won't be exposed.
* @param scopeName the name of the scope
* @return the registered Scope implementation, or <code>null</code> if none
* @see #registerScope
*/
Scope getRegisteredScope(String scopeName);
/**
* Copy all relevant configuration from the given other factory.
* <p>Should include all standard configuration settings as well as
* BeanPostProcessors, Scopes, and factory-specific internal settings.
* Should not include any metadata of actual bean definitions,
* such as BeanDefinition objects and bean name aliases.
* @param otherFactory the other BeanFactory to copy from
*/
void copyConfigurationFrom(ConfigurableBeanFactory otherFactory);
/**
* Given a bean name, create an alias. We typically use this method to
* support names that are illegal within XML ids (used for bean names).
* <p>Typically invoked during factory configuration, but can also be
* used for runtime registration of aliases. Therefore, a factory
* implementation should synchronize alias access.
* @param beanName the canonical name of the target bean
* @param alias the alias to be registered for the bean
* @throws BeanDefinitionStoreException if the alias is already in use
*/
void registerAlias(String beanName, String alias) throws BeanDefinitionStoreException;
/**
* Resolve all alias target names and aliases registered in this
* factory, applying the given StringValueResolver to them.
* <p>The value resolver may for example resolve placeholders
* in target bean names and even in alias names.
* @param valueResolver the StringValueResolver to apply
*/
void resolveAliases(StringValueResolver valueResolver);
/**
* Return a merged BeanDefinition for the given bean name,
* merging a child bean definition with its parent if necessary.
* Considers bean definitions in ancestor factories as well.
* @param beanName the name of the bean to retrieve the merged definition for
* @return a (potentially merged) BeanDefinition for the given bean
* @throws NoSuchBeanDefinitionException if there is no bean definition with the given name
*/
BeanDefinition getMergedBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
/**
* Determine whether the bean with the given name is a FactoryBean.
* @param name the name of the bean to check
* @return whether the bean is a FactoryBean
* (<code>false</code> means the bean exists but is not a FactoryBean)
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
*/
boolean isFactoryBean(String name) throws NoSuchBeanDefinitionException;
/**
* Determine whether the specified bean is currently in creation.
* @param beanName the name of the bean
* @return whether the bean is currently in creation
*/
boolean isCurrentlyInCreation(String beanName);
/**
* Register a dependent bean for the given bean,
* to be destroyed before the given bean is destroyed.
* @param beanName the name of the bean
* @param dependentBeanName the name of the dependent bean
*/
void registerDependentBean(String beanName, String dependentBeanName);
/**
* Return the names of all beans which depend on the specified bean, if any.
* @param beanName the name of the bean
* @return the array of dependent bean names, or an empty array if none
*/
String[] getDependentBeans(String beanName);
/**
* Return the names of all beans that the specified bean depends on, if any.
* @param beanName the name of the bean
* @return the array of names of beans which the bean depends on,
* or an empty array if none
*/
String[] getDependenciesForBean(String beanName);
/**
* Destroy the given bean instance (usually a prototype instance
* obtained from this factory) according to its bean definition.
* <p>Any exception that arises during destruction should be caught
* and logged instead of propagated to the caller of this method.
* @param beanName the name of the bean definition
* @param beanInstance the bean instance to destroy
*/
void destroyBean(String beanName, Object beanInstance);
/**
* Destroy the specified scoped bean in the current target scope, if any.
* <p>Any exception that arises during destruction should be caught
* and logged instead of propagated to the caller of this method.
* @param beanName the name of the scoped bean
*/
void destroyScopedBean(String beanName);
/**
* Destroy all singleton beans in this factory, including inner beans that have
* been registered as disposable. To be called on shutdown of a factory.
* <p>Any exception that arises during destruction should be caught
* and logged instead of propagated to the caller of this method.
*/
void destroySingletons();
}

View File

@@ -0,0 +1,133 @@
/*
* 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;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
/**
* Configuration interface to be implemented by most listable bean factories.
* In addition to {@link ConfigurableBeanFactory}, it provides facilities to
* analyze and modify bean definitions, and to pre-instantiate singletons.
*
* <p>This subinterface of {@link org.springframework.beans.factory.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. This interface is just meant to allow for framework-internal
* plug'n'play even when needing access to bean factory configuration methods.
*
* @author Juergen Hoeller
* @since 03.11.2003
* @see org.springframework.context.support.AbstractApplicationContext#getBeanFactory()
*/
public interface ConfigurableListableBeanFactory
extends ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory {
/**
* Ignore the given dependency type for autowiring:
* for example, String. Default is none.
* @param type the dependency type to ignore
*/
void ignoreDependencyType(Class type);
/**
* Ignore the given dependency interface for autowiring.
* <p>This will typically be used by application contexts to register
* dependencies that are resolved in other ways, like BeanFactory through
* BeanFactoryAware or ApplicationContext through ApplicationContextAware.
* <p>By default, only the BeanFactoryAware interface is ignored.
* For further types to ignore, invoke this method for each type.
* @param ifc the dependency interface to ignore
* @see org.springframework.beans.factory.BeanFactoryAware
* @see org.springframework.context.ApplicationContextAware
*/
void ignoreDependencyInterface(Class ifc);
/**
* Register a special dependency type with corresponding autowired value.
* <p>This is intended for factory/context references that are supposed
* to be autowirable but are not defined as beans in the factory:
* e.g. a dependency of type ApplicationContext resolved to the
* ApplicationContext instance that the bean is living in.
* <p>Note: There are no such default types registered in a plain BeanFactory,
* not even for the BeanFactory interface itself.
* @param dependencyType the dependency type to register. This will typically
* be a base interface such as BeanFactory, with extensions of it resolved
* as well if declared as an autowiring dependency (e.g. ListableBeanFactory),
* as long as the given value actually implements the extended interface.
* @param autowiredValue the corresponding autowired value. This may also be an
* implementation of the {@link org.springframework.beans.factory.ObjectFactory}
* interface, which allows for lazy resolution of the actual target value.
*/
void registerResolvableDependency(Class dependencyType, Object autowiredValue);
/**
* Determine whether the specified bean qualifies as an autowire candidate,
* to be injected into other beans which declare a dependency of matching type.
* <p>This method checks ancestor factories as well.
* @param beanName the name of the bean to check
* @param descriptor the descriptor of the dependency to resolve
* @return whether the bean should be considered as autowire candidate
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
*/
boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)
throws NoSuchBeanDefinitionException;
/**
* Return the registered BeanDefinition for the specified bean, allowing access
* to its property values and constructor argument value (which can be
* modified during bean factory post-processing).
* <p>A returned BeanDefinition object should not be a copy but the original
* definition object as registered in the factory. This means that it should
* be castable to a more specific implementation type, if necessary.
* <p><b>NOTE:</b> This method does <i>not</i> consider ancestor factories.
* It is only meant for accessing local bean definitions of this factory.
* @param beanName the name of the bean
* @return the registered BeanDefinition
* @throws NoSuchBeanDefinitionException if there is no bean with the given name
* defined in this factory
*/
BeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;
/**
* Freeze all bean definitions, signalling that the registered bean definitions
* will not be modified or post-processed any further.
* <p>This allows the factory to aggressively cache bean definition metadata.
*/
void freezeConfiguration();
/**
* Return whether this factory's bean definitions are frozen,
* i.e. are not supposed to be modified or post-processed any further.
* @return <code>true</code> if the factory's configuration is considered frozen
*/
boolean isConfigurationFrozen();
/**
* Ensure that all non-lazy-init singletons are instantiated, also considering
* {@link org.springframework.beans.factory.FactoryBean FactoryBeans}.
* Typically invoked at the end of factory setup, if desired.
* @throws BeansException if one of the singleton beans could not be created.
* Note: This may have left the factory with some beans already initialized!
* Call {@link #destroySingletons()} for full cleanup in this case.
* @see #destroySingletons()
*/
void preInstantiateSingletons() throws BeansException;
}

View File

@@ -0,0 +1,502 @@
/*
* 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.config;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.springframework.beans.BeanMetadataElement;
import org.springframework.beans.Mergeable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ObjectUtils;
/**
* Holder for constructor argument values, typically as part of a bean definition.
*
* <p>Supports values for a specific index in the constructor argument list
* as well as for generic argument matches by type.
*
* @author Juergen Hoeller
* @since 09.11.2003
* @see BeanDefinition#getConstructorArgumentValues
*/
public class ConstructorArgumentValues {
private final Map indexedArgumentValues = new HashMap();
private final List genericArgumentValues = new LinkedList();
/**
* Create a new empty ConstructorArgumentValues object.
*/
public ConstructorArgumentValues() {
}
/**
* Deep copy constructor.
* @param original the ConstructorArgumentValues to copy
*/
public ConstructorArgumentValues(ConstructorArgumentValues original) {
addArgumentValues(original);
}
/**
* Copy all given argument values into this object, using separate holder
* instances to keep the values independent from the original object.
* <p>Note: Identical ValueHolder instances will only be registered once,
* to allow for merging and re-merging of argument value definitions. Distinct
* ValueHolder instances carrying the same content are of course allowed.
*/
public void addArgumentValues(ConstructorArgumentValues other) {
if (other != null) {
for (Iterator it = other.indexedArgumentValues.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
ValueHolder valueHolder = (ValueHolder) entry.getValue();
addOrMergeIndexedArgumentValue(entry.getKey(), valueHolder.copy());
}
for (Iterator it = other.genericArgumentValues.iterator(); it.hasNext();) {
ValueHolder valueHolder = (ValueHolder) it.next();
if (!this.genericArgumentValues.contains(valueHolder)) {
this.genericArgumentValues.add(valueHolder.copy());
}
}
}
}
/**
* Add argument value for the given index in the constructor argument list.
* @param index the index in the constructor argument list
* @param value the argument value
*/
public void addIndexedArgumentValue(int index, Object value) {
addIndexedArgumentValue(index, new ValueHolder(value));
}
/**
* Add argument value for the given index in the constructor argument list.
* @param index the index in the constructor argument list
* @param value the argument value
* @param type the type of the constructor argument
*/
public void addIndexedArgumentValue(int index, Object value, String type) {
addIndexedArgumentValue(index, new ValueHolder(value, type));
}
/**
* Add argument value for the given index in the constructor argument list.
* @param index the index in the constructor argument list
* @param newValue the argument value in the form of a ValueHolder
*/
public void addIndexedArgumentValue(int index, ValueHolder newValue) {
Assert.isTrue(index >= 0, "Index must not be negative");
Assert.notNull(newValue, "ValueHolder must not be null");
addOrMergeIndexedArgumentValue(new Integer(index), newValue);
}
/**
* Add argument value for the given index in the constructor argument list,
* merging the new value (typically a collection) with the current value
* if demanded: see {@link org.springframework.beans.Mergeable}.
* @param key the index in the constructor argument list
* @param newValue the argument value in the form of a ValueHolder
*/
private void addOrMergeIndexedArgumentValue(Object key, ValueHolder newValue) {
ValueHolder currentValue = (ValueHolder) this.indexedArgumentValues.get(key);
if (currentValue != null && newValue.getValue() instanceof Mergeable) {
Mergeable mergeable = (Mergeable) newValue.getValue();
if (mergeable.isMergeEnabled()) {
newValue.setValue(mergeable.merge(currentValue.getValue()));
}
}
this.indexedArgumentValues.put(key, newValue);
}
/**
* Get argument value for the given index in the constructor argument list.
* @param index the index in the constructor argument list
* @param requiredType the type to match (can be <code>null</code> to match
* untyped values only)
* @return the ValueHolder for the argument, or <code>null</code> if none set
*/
public ValueHolder getIndexedArgumentValue(int index, Class requiredType) {
Assert.isTrue(index >= 0, "Index must not be negative");
ValueHolder valueHolder = (ValueHolder) this.indexedArgumentValues.get(new Integer(index));
if (valueHolder != null) {
if (valueHolder.getType() == null ||
(requiredType != null && requiredType.getName().equals(valueHolder.getType()))) {
return valueHolder;
}
}
return null;
}
/**
* Return the map of indexed argument values.
* @return unmodifiable Map with Integer index as key and ValueHolder as value
* @see ValueHolder
*/
public Map getIndexedArgumentValues() {
return Collections.unmodifiableMap(this.indexedArgumentValues);
}
/**
* Add generic argument value to be matched by type.
* <p>Note: A single generic argument value will just be used once,
* rather than matched multiple times (as of Spring 1.1).
* @param value the argument value
*/
public void addGenericArgumentValue(Object value) {
this.genericArgumentValues.add(new ValueHolder(value));
}
/**
* Add generic argument value to be matched by type.
* <p>Note: A single generic argument value will just be used once,
* rather than matched multiple times (as of Spring 1.1).
* @param value the argument value
* @param type the type of the constructor argument
*/
public void addGenericArgumentValue(Object value, String type) {
this.genericArgumentValues.add(new ValueHolder(value, type));
}
/**
* Add generic argument value to be matched by type.
* <p>Note: A single generic argument value will just be used once,
* rather than matched multiple times (as of Spring 1.1).
* @param newValue the argument value in the form of a ValueHolder
* <p>Note: Identical ValueHolder instances will only be registered once,
* to allow for merging and re-merging of argument value definitions. Distinct
* ValueHolder instances carrying the same content are of course allowed.
*/
public void addGenericArgumentValue(ValueHolder newValue) {
Assert.notNull(newValue, "ValueHolder must not be null");
if (!this.genericArgumentValues.contains(newValue)) {
this.genericArgumentValues.add(newValue);
}
}
/**
* Look for a generic argument value that matches the given type.
* @param requiredType the type to match (can be <code>null</code> to find
* an arbitrary next generic argument value)
* @return the ValueHolder for the argument, or <code>null</code> if none set
*/
public ValueHolder getGenericArgumentValue(Class requiredType) {
return getGenericArgumentValue(requiredType, null);
}
/**
* Look for the next generic argument value that matches the given type,
* ignoring argument values that have already been used in the current
* resolution process.
* @param requiredType the type to match (can be <code>null</code> to find
* an arbitrary next generic argument value)
* @param usedValueHolders a Set of ValueHolder objects that have already been used
* in the current resolution process and should therefore not be returned again
* @return the ValueHolder for the argument, or <code>null</code> if none found
*/
public ValueHolder getGenericArgumentValue(Class requiredType, Set usedValueHolders) {
for (Iterator it = this.genericArgumentValues.iterator(); it.hasNext();) {
ValueHolder valueHolder = (ValueHolder) it.next();
if (usedValueHolders == null || !usedValueHolders.contains(valueHolder)) {
if (requiredType != null) {
// Check matching type.
if (valueHolder.getType() != null) {
if (valueHolder.getType().equals(requiredType.getName())) {
return valueHolder;
}
}
else if (ClassUtils.isAssignableValue(requiredType, valueHolder.getValue())) {
return valueHolder;
}
}
else {
// No required type specified -> consider untyped values only.
if (valueHolder.getType() == null) {
return valueHolder;
}
}
}
}
return null;
}
/**
* Return the list of generic argument values.
* @return unmodifiable List of ValueHolders
* @see ValueHolder
*/
public List getGenericArgumentValues() {
return Collections.unmodifiableList(this.genericArgumentValues);
}
/**
* Look for an argument value that either corresponds to the given index
* in the constructor argument list or generically matches by type.
* @param index the index in the constructor argument list
* @param requiredType the type to match
* @return the ValueHolder for the argument, or <code>null</code> if none set
*/
public ValueHolder getArgumentValue(int index, Class requiredType) {
return getArgumentValue(index, requiredType, null);
}
/**
* Look for an argument value that either corresponds to the given index
* in the constructor argument list or generically matches by type.
* @param index the index in the constructor argument list
* @param requiredType the type to match (can be <code>null</code> to find
* an untyped argument value)
* @param usedValueHolders a Set of ValueHolder objects that have already
* been used in the current resolution process and should therefore not
* be returned again (allowing to return the next generic argument match
* in case of multiple generic argument values of the same type)
* @return the ValueHolder for the argument, or <code>null</code> if none set
*/
public ValueHolder getArgumentValue(int index, Class requiredType, Set usedValueHolders) {
Assert.isTrue(index >= 0, "Index must not be negative");
ValueHolder valueHolder = getIndexedArgumentValue(index, requiredType);
if (valueHolder == null) {
valueHolder = getGenericArgumentValue(requiredType, usedValueHolders);
}
return valueHolder;
}
/**
* Return the number of argument values held in this instance,
* counting both indexed and generic argument values.
*/
public int getArgumentCount() {
return (this.indexedArgumentValues.size() + this.genericArgumentValues.size());
}
/**
* Return if this holder does not contain any argument values,
* neither indexed ones nor generic ones.
*/
public boolean isEmpty() {
return (this.indexedArgumentValues.isEmpty() && this.genericArgumentValues.isEmpty());
}
/**
* Clear this holder, removing all argument values.
*/
public void clear() {
this.indexedArgumentValues.clear();
this.genericArgumentValues.clear();
}
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof ConstructorArgumentValues)) {
return false;
}
ConstructorArgumentValues that = (ConstructorArgumentValues) other;
if (this.genericArgumentValues.size() != that.genericArgumentValues.size() ||
this.indexedArgumentValues.size() != that.indexedArgumentValues.size()) {
return false;
}
Iterator it1 = this.genericArgumentValues.iterator();
Iterator it2 = that.genericArgumentValues.iterator();
while (it1.hasNext() && it2.hasNext()) {
ValueHolder vh1 = (ValueHolder) it1.next();
ValueHolder vh2 = (ValueHolder) it2.next();
if (!vh1.contentEquals(vh2)) {
return false;
}
}
for (Iterator it = this.indexedArgumentValues.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
ValueHolder vh1 = (ValueHolder) entry.getValue();
ValueHolder vh2 = (ValueHolder) that.indexedArgumentValues.get(entry.getKey());
if (!vh1.contentEquals(vh2)) {
return false;
}
}
return true;
}
public int hashCode() {
int hashCode = 7;
for (Iterator it = this.genericArgumentValues.iterator(); it.hasNext();) {
ValueHolder valueHolder = (ValueHolder) it.next();
hashCode = 31 * hashCode + valueHolder.contentHashCode();
}
hashCode = 29 * hashCode;
for (Iterator it = this.indexedArgumentValues.entrySet().iterator(); it.hasNext();) {
Map.Entry entry = (Map.Entry) it.next();
Integer key = (Integer) entry.getKey();
ValueHolder value = (ValueHolder) entry.getValue();
hashCode = 31 * hashCode + (value.contentHashCode() ^ key.hashCode());
}
return hashCode;
}
/**
* Holder for a constructor argument value, with an optional type
* attribute indicating the target type of the actual constructor argument.
*/
public static class ValueHolder implements BeanMetadataElement {
private Object value;
private String type;
private Object source;
private boolean converted = false;
private Object convertedValue;
/**
* Create a new ValueHolder for the given value.
* @param value the argument value
*/
public ValueHolder(Object value) {
this.value = value;
}
/**
* Create a new ValueHolder for the given value and type.
* @param value the argument value
* @param type the type of the constructor argument
*/
public ValueHolder(Object value, String type) {
this.value = value;
this.type = type;
}
/**
* Set the value for the constructor argument.
* Only necessary for manipulating a registered value,
* for example in BeanFactoryPostProcessors.
* @see PropertyPlaceholderConfigurer
*/
public void setValue(Object value) {
this.value = value;
}
/**
* Return the value for the constructor argument.
*/
public Object getValue() {
return this.value;
}
/**
* Set the type of the constructor argument.
* Only necessary for manipulating a registered value,
* for example in BeanFactoryPostProcessors.
* @see PropertyPlaceholderConfigurer
*/
public void setType(String type) {
this.type = type;
}
/**
* Return the type of the constructor argument.
*/
public String getType() {
return this.type;
}
/**
* 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;
}
/**
* 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;
}
/**
* Determine whether the content of this ValueHolder is equal
* to the content of the given other ValueHolder.
* <p>Note that ValueHolder does not implement <code>equals</code>
* directly, to allow for multiple ValueHolder instances with the
* same content to reside in the same Set.
*/
private boolean contentEquals(ValueHolder other) {
return (this == other ||
(ObjectUtils.nullSafeEquals(this.value, other.value) && ObjectUtils.nullSafeEquals(this.type, other.type)));
}
/**
* Determine whether the hash code of the content of this ValueHolder.
* <p>Note that ValueHolder does not implement <code>hashCode</code>
* directly, to allow for multiple ValueHolder instances with the
* same content to reside in the same Set.
*/
private int contentHashCode() {
return ObjectUtils.nullSafeHashCode(this.value) * 29 + ObjectUtils.nullSafeHashCode(this.type);
}
/**
* Create a copy of this ValueHolder: that is, an independent
* ValueHolder instance with the same contents.
*/
public ValueHolder copy() {
ValueHolder copy = new ValueHolder(this.value, this.type);
copy.setSource(this.source);
return copy;
}
}
}

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