📄 beanwrapperimpl.java
字号:
/*
* Copyright 2002-2004 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF 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.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.io.File;
import java.io.InputStream;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.propertyeditors.ByteArrayPropertyEditor;
import org.springframework.beans.propertyeditors.ClassEditor;
import org.springframework.beans.propertyeditors.FileEditor;
import org.springframework.beans.propertyeditors.InputStreamEditor;
import org.springframework.beans.propertyeditors.LocaleEditor;
import org.springframework.beans.propertyeditors.PropertiesEditor;
import org.springframework.beans.propertyeditors.StringArrayPropertyEditor;
import org.springframework.beans.propertyeditors.URLEditor;
import org.springframework.util.StringUtils;
/**
* Default implementation of the BeanWrapper interface that should be sufficient
* for all normal uses. Caches introspection results for efficiency.
*
* <p>Note: This class never tries to load a class by name, as this can pose
* class loading problems in J2EE applications with multiple deployment modules.
* The caller is responsible for loading a target class.
*
* <p>Note: Auto-registers all default property editors (not the custom ones)
* in the org.springframework.beans.propertyeditors package.
* Applications can either use a standard PropertyEditorManager to register a
* custom editor before using a BeanWrapperImpl instance, or call the instance's
* registerCustomEditor method to register an editor for the particular instance.
*
* <p>BeanWrapperImpl will convert List and array values to the corresponding
* target arrays, if necessary. Custom property editors that deal with Lists or
* arrays can be written against a comma delimited String as String arrays are
* converted in such a format if the array itself is not assignable.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Jean-Pierre Pawlak
* @since 15 April 2001
* @version $Id: BeanWrapperImpl.java,v 1.37 2004/04/05 07:17:55 jhoeller Exp $
* @see #registerCustomEditor
* @see java.beans.PropertyEditorManager
* @see org.springframework.beans.propertyeditors.ClassEditor
* @see org.springframework.beans.propertyeditors.FileEditor
* @see org.springframework.beans.propertyeditors.LocaleEditor
* @see org.springframework.beans.propertyeditors.PropertiesEditor
* @see org.springframework.beans.propertyeditors.StringArrayPropertyEditor
* @see org.springframework.beans.propertyeditors.URLEditor
*/
public class BeanWrapperImpl 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);
/** Registry for default PropertyEditors */
private static final Map defaultEditors = new HashMap();
static {
// Register default editors in this class, for restricted environments.
// We're not using the JRE's PropertyEditorManager to avoid potential
// SecurityExceptions when running in a SecurityManager.
defaultEditors.put(byte[].class, ByteArrayPropertyEditor.class);
defaultEditors.put(Class.class, ClassEditor.class);
defaultEditors.put(File.class, FileEditor.class);
defaultEditors.put(InputStream.class, InputStreamEditor.class);
defaultEditors.put(Locale.class, LocaleEditor.class);
defaultEditors.put(Properties.class, PropertiesEditor.class);
defaultEditors.put(String[].class, StringArrayPropertyEditor.class);
defaultEditors.put(URL.class, URLEditor.class);
}
//---------------------------------------------------------------------
// Instance data
//---------------------------------------------------------------------
/** The wrapped object */
private Object object;
/** The nested path of the object */
private String nestedPath = "";
/* Map with cached nested BeanWrappers */
private Map nestedBeanWrappers;
/** Map with custom PropertyEditor instances */
private Map customEditors;
/**
* Cached introspections results for this object, to prevent encountering the cost
* of JavaBeans introspection every time.
*/
private CachedIntrospectionResults cachedIntrospectionResults;
//---------------------------------------------------------------------
// Constructors
//---------------------------------------------------------------------
/**
* Create new empty BeanWrapperImpl. Wrapped instance needs to be set afterwards.
* @see #setWrappedInstance
*/
public BeanWrapperImpl() {
}
/**
* Create new BeanWrapperImpl for the given object.
* @param object object wrapped by this BeanWrapper.
* @throws BeansException if the object cannot be wrapped by a BeanWrapper
*/
public BeanWrapperImpl(Object object) throws BeansException {
setWrappedInstance(object);
}
/**
* 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
* @throws BeansException if the object cannot be wrapped by a BeanWrapper
*/
public BeanWrapperImpl(Object object, String nestedPath) throws BeansException {
setWrappedInstance(object);
this.nestedPath = nestedPath;
}
/**
* Create new BeanWrapperImpl, wrapping a new instance of the specified class.
* @param clazz class to instantiate and wrap
* @throws BeansException if the class cannot be wrapped by a BeanWrapper
*/
public BeanWrapperImpl(Class clazz) throws BeansException {
setWrappedInstance(BeanUtils.instantiateClass(clazz));
}
//---------------------------------------------------------------------
// Implementation of BeanWrapper
//---------------------------------------------------------------------
/**
* Switches 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 new target
* @throws BeansException if the object cannot be changed
*/
public void setWrappedInstance(Object object) throws BeansException {
if (object == null) {
throw new FatalBeanException("Cannot set BeanWrapperImpl target to a null object");
}
this.object = object;
this.nestedBeanWrappers = null;
if (this.cachedIntrospectionResults == null ||
!this.cachedIntrospectionResults.getBeanClass().equals(object.getClass())) {
this.cachedIntrospectionResults = CachedIntrospectionResults.forClass(object.getClass());
}
}
public Class getWrappedClass() {
return object.getClass();
}
public Object getWrappedInstance() {
return object;
}
public void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor) {
registerCustomEditor(requiredType, null, propertyEditor);
}
public void registerCustomEditor(Class requiredType, String propertyPath, PropertyEditor propertyEditor) {
if (propertyPath != null) {
List bws = getBeanWrappersForPropertyPath(propertyPath);
for (Iterator it = bws.iterator(); it.hasNext();) {
BeanWrapperImpl bw = (BeanWrapperImpl) it.next();
bw.doRegisterCustomEditor(requiredType, getFinalPath(propertyPath), propertyEditor);
}
}
else {
doRegisterCustomEditor(requiredType, propertyPath, propertyEditor);
}
}
private void doRegisterCustomEditor(Class requiredType, String propertyName, PropertyEditor propertyEditor) {
if (this.customEditors == null) {
this.customEditors = new HashMap();
}
if (propertyName != null) {
this.customEditors.put(propertyName, propertyEditor);
}
else {
if (requiredType == null) {
throw new IllegalArgumentException("No propertyName and no requiredType specified");
}
this.customEditors.put(requiredType, propertyEditor);
}
}
public PropertyEditor findCustomEditor(Class requiredType, String propertyPath) {
if (propertyPath != null) {
BeanWrapperImpl bw = getBeanWrapperForPropertyPath(propertyPath);
return bw.doFindCustomEditor(requiredType, getFinalPath(propertyPath));
}
else {
return doFindCustomEditor(requiredType, propertyPath);
}
}
private PropertyEditor doFindCustomEditor(Class requiredType, String propertyName) {
if (this.customEditors == null) {
return null;
}
if (propertyName != null) {
// check property-specific editor first
try {
PropertyEditor editor = (PropertyEditor) this.customEditors.get(propertyName);
if (editor == null) {
int keyIndex = propertyName.indexOf('[');
if (keyIndex != -1) {
editor = (PropertyEditor) this.customEditors.get(propertyName.substring(0, keyIndex));
}
}
if (editor != null) {
return editor;
}
else {
if (requiredType == null) {
// try property type
requiredType = getPropertyDescriptor(propertyName).getPropertyType();
}
}
}
catch (BeansException ex) {
// probably an indexed or mapped property
// we need to retrieve the value to determine the type
requiredType = getPropertyValue(propertyName).getClass();
}
}
// no property-specific editor -> check type-specific editor
return (PropertyEditor) this.customEditors.get(requiredType);
}
/**
* Is the property nested? That is, does it contain the nested
* property separator (usually ".").
* @param path property path
* @return boolean is the property nested
*/
private boolean isNestedProperty(String path) {
return path.indexOf(NESTED_PROPERTY_SEPARATOR) != -1;
}
/**
* Get the last component of the path. Also works if not nested.
* @param nestedPath property path we know is nested
* @return last component of the path (the property on the target bean)
*/
private String getFinalPath(String nestedPath) {
return nestedPath.substring(nestedPath.lastIndexOf(NESTED_PROPERTY_SEPARATOR) + 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
*/
private BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) {
int pos = propertyPath.indexOf(NESTED_PROPERTY_SEPARATOR);
// handle nested properties recursively
if (pos > -1) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -