Moved over initial version of beans bundle
This commit is contained in:
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + "'";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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()];
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 + "'";
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
Support package for beans-style handling of Java 5 annotations.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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"><?xml version="1.0" encoding="UTF-8"?>
|
||||
* <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
*
|
||||
* <beans>
|
||||
*
|
||||
* <bean id="com.mycompany.myapp"
|
||||
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||||
* <constructor-arg>
|
||||
* <list>
|
||||
* <value>com/mycompany/myapp/util/applicationContext.xml</value>
|
||||
* <value>com/mycompany/myapp/dataaccess/applicationContext.xml</value>
|
||||
* <value>com/mycompany/myapp/dataaccess/services.xml</value>
|
||||
* </list>
|
||||
* </constructor-arg>
|
||||
* </bean>
|
||||
*
|
||||
* </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"><?xml version="1.0" encoding="UTF-8"?>
|
||||
* <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
*
|
||||
* <beans>
|
||||
*
|
||||
* <bean id="com.mycompany.myapp.util" lazy-init="true"
|
||||
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||||
* <constructor-arg>
|
||||
* <value>com/mycompany/myapp/util/applicationContext.xml</value>
|
||||
* </constructor-arg>
|
||||
* </bean>
|
||||
*
|
||||
* <!-- child of above -->
|
||||
* <bean id="com.mycompany.myapp.dataaccess" lazy-init="true"
|
||||
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||||
* <constructor-arg>
|
||||
* <list><value>com/mycompany/myapp/dataaccess/applicationContext.xml</value></list>
|
||||
* </constructor-arg>
|
||||
* <constructor-arg>
|
||||
* <ref bean="com.mycompany.myapp.util"/>
|
||||
* </constructor-arg>
|
||||
* </bean>
|
||||
*
|
||||
* <!-- child of above -->
|
||||
* <bean id="com.mycompany.myapp.services" lazy-init="true"
|
||||
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||||
* <constructor-arg>
|
||||
* <list><value>com/mycompany/myapp/dataaccess.services.xml</value></value>
|
||||
* </constructor-arg>
|
||||
* <constructor-arg>
|
||||
* <ref bean="com.mycompany.myapp.dataaccess"/>
|
||||
* </constructor-arg>
|
||||
* </bean>
|
||||
*
|
||||
* <!-- define an alias -->
|
||||
* <bean id="com.mycompany.myapp.mypackage"
|
||||
* class="java.lang.String">
|
||||
* <constructor-arg>
|
||||
* <value>com.mycompany.myapp.services</value>
|
||||
* </constructor-arg>
|
||||
* </bean>
|
||||
*
|
||||
* </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"><?xml version="1.0" encoding="UTF-8"?>
|
||||
* <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
*
|
||||
* <beans>
|
||||
* <bean id="com.mycompany.myapp.util" lazy-init="true"
|
||||
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||||
* <constructor-arg>
|
||||
* <value>com/mycompany/myapp/util/applicationContext.xml</value>
|
||||
* </constructor-arg>
|
||||
* </bean>
|
||||
* </beans>
|
||||
* </pre>
|
||||
*
|
||||
* <code>beanRefFactory.xml</code> file inside jar for data-access module:<br>
|
||||
*
|
||||
* <pre class="code"><?xml version="1.0" encoding="UTF-8"?>
|
||||
* <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
*
|
||||
* <beans>
|
||||
* <!-- child of util -->
|
||||
* <bean id="com.mycompany.myapp.dataaccess" lazy-init="true"
|
||||
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||||
* <constructor-arg>
|
||||
* <list><value>com/mycompany/myapp/dataaccess/applicationContext.xml</value></list>
|
||||
* </constructor-arg>
|
||||
* <constructor-arg>
|
||||
* <ref bean="com.mycompany.myapp.util"/>
|
||||
* </constructor-arg>
|
||||
* </bean>
|
||||
* </beans>
|
||||
* </pre>
|
||||
*
|
||||
* <code>beanRefFactory.xml</code> file inside jar for services module:
|
||||
*
|
||||
* <pre class="code"><?xml version="1.0" encoding="UTF-8"?>
|
||||
* <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
*
|
||||
* <beans>
|
||||
* <!-- child of data-access -->
|
||||
* <bean id="com.mycompany.myapp.services" lazy-init="true"
|
||||
* class="org.springframework.context.support.ClassPathXmlApplicationContext">
|
||||
* <constructor-arg>
|
||||
* <list><value>com/mycompany/myapp/dataaccess/services.xml</value></list>
|
||||
* </constructor-arg>
|
||||
* <constructor-arg>
|
||||
* <ref bean="com.mycompany.myapp.dataaccess"/>
|
||||
* </constructor-arg>
|
||||
* </bean>
|
||||
* </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"><?xml version="1.0" encoding="UTF-8"?>
|
||||
* <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
|
||||
*
|
||||
* <beans>
|
||||
* <!-- define an alias for "com.mycompany.myapp.services" -->
|
||||
* <alias name="com.mycompany.myapp.services" alias="com.mycompany.myapp.mypackage"/>
|
||||
* </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 + "]");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
Support classes for accessing a Spring BeanFactory from Unified EL.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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>
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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">
|
||||
* <bean id="customAutowireConfigurer" class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
|
||||
* <property name="customQualifierTypes">
|
||||
* <set>
|
||||
* <value>mypackage.MyQualifier</value>
|
||||
* </set>
|
||||
* </property>
|
||||
* </bean></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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 "";
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
<html>
|
||||
<body>
|
||||
|
||||
Support package for annotation-driven bean configuration.
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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><alias></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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user