PropertySource(abstract)
package org.springframework.core.env;
/**
* Abstract base class representing a source of name/value property pairs. The underlying
* {@linkplain #getSource() source object} may be of any type {@code T} that encapsulates
* properties. Examples include {@link java.util.Properties} objects, {@link java.util.Map}
* objects, {@code ServletContext} and {@code ServletConfig} objects (for access to init
* parameters). Explore the {@code PropertySource} type hierarchy to see provided
* implementations.
*
* <p>{@code PropertySource} objects are not typically used in isolation, but rather
* through a {@link PropertySources} object, which aggregates property sources and in
* conjunction with a {@link PropertyResolver} implementation that can perform
* precedence-based searches across the set of {@code PropertySources}.
*
* <p>{@code PropertySource} identity is determined not based on the content of
* encapsulated properties, but rather based on the {@link #getName() name} of the
* {@code PropertySource} alone. This is useful for manipulating {@code PropertySource}
* objects when in collection contexts. See operations in {@link MutablePropertySources}
* as well as the {@link #named(String)} and {@link #toString()} methods for details.
*
* <p>Note that when working with @{@link
* org.springframework.context.annotation.Configuration Configuration} classes that
* the @{@link org.springframework.context.annotation.PropertySource PropertySource}
* annotation provides a convenient and declarative way of adding property sources to the
* enclosing {@code Environment}.
*
* @author Chris Beams
* @since 3.1
* @see PropertySources
* @see PropertyResolver
* @see PropertySourcesPropertyResolver
* @see MutablePropertySources
* @see org.springframework.context.annotation.PropertySource
*/
public abstract class PropertySource<T> {
protected final Log logger = LogFactory.getLog(getClass());
protected final String name;
protected final T source;
/**
* Create a new {@code PropertySource} with the given name and source object.
*/
public PropertySource(String name, T source) {
Assert.hasText(name, "Property source name must contain at least one character");
Assert.notNull(source, "Property source must not be null");
this.name = name;
this.source = source;
}
/**
* Create a new {@code PropertySource} with the given name and with a new
* {@code Object} instance as the underlying source.
* <p>Often useful in testing scenarios when creating anonymous implementations
* that never query an actual source but rather return hard-coded values.
*/
@SuppressWarnings("unchecked")
public PropertySource(String name) {
this(name, (T) new Object());
}
/**
* Return the name of this {@code PropertySource}
*/
public String getName() {
return this.name;
}
/**
* Return the underlying source object for this {@code PropertySource}.
*/
public T getSource() {
return this.source;
}
/**
* Return whether this {@code PropertySource} contains the given name.
* <p>This implementation simply checks for a {@code null} return value
* from {@link #getProperty(String)}. Subclasses may wish to implement
* a more efficient algorithm if possible.
* @param name the property name to find
*/
public boolean containsProperty(String name) {
return (getProperty(name) != null);
}
/**
* Return the value associated with the given name,
* or {@code null} if not found.
* @param name the property to find
* @see PropertyResolver#getRequiredProperty(String)
*/
public abstract Object getProperty(String name);
/**
* This {@code PropertySource} object is equal to the given object if:
* <ul>
* <li>they are the same instance
* <li>the {@code name} properties for both objects are equal
* </ul>
* <p>No properties other than {@code name} are evaluated.
*/
@Override
public boolean equals(Object obj) {
return (this == obj || (obj instanceof PropertySource &&
ObjectUtils.nullSafeEquals(this.name, ((PropertySource<?>) obj).name)));
}
/**
* Return a hash code derived from the {@code name} property
* of this {@code PropertySource} object.
*/
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.name);
}
/**
* Produce concise output (type and name) if the current log level does not include
* debug. If debug is enabled, produce verbose output including the hash code of the
* PropertySource instance and every name/value property pair.
* <p>This variable verbosity is useful as a property source such as system properties
* or environment variables may contain an arbitrary number of property pairs,
* potentially leading to difficult to read exception and log messages.
* @see Log#isDebugEnabled()
*/
@Override
public String toString() {
if (logger.isDebugEnabled()) {
return getClass().getSimpleName() + "@" + System.identityHashCode(this) +
" {name='" + this.name + "', properties=" + this.source + "}";
}
else {
return getClass().getSimpleName() + " {name='" + this.name + "'}";
}
}
/**
* Return a {@code PropertySource} implementation intended for collection comparison purposes only.
* <p>Primarily for internal use, but given a collection of {@code PropertySource} objects, may be
* used as follows:
* <pre class="code">
* {@code List<PropertySource<?>> sources = new ArrayList<PropertySource<?>>();
* sources.add(new MapPropertySource("sourceA", mapA));
* sources.add(new MapPropertySource("sourceB", mapB));
* assert sources.contains(PropertySource.named("sourceA"));
* assert sources.contains(PropertySource.named("sourceB"));
* assert !sources.contains(PropertySource.named("sourceC"));
* }</pre>
* The returned {@code PropertySource} will throw {@code UnsupportedOperationException}
* if any methods other than {@code equals(Object)}, {@code hashCode()}, and {@code toString()}
* are called.
* @param name the name of the comparison {@code PropertySource} to be created and returned.
*/
public static PropertySource<?> named(String name) {
return new ComparisonPropertySource(name);
}
PropertySource$StubPropertySource
/**
* {@code PropertySource} to be used as a placeholder in cases where an actual
* property source cannot be eagerly initialized at application context
* creation time. For example, a {@code ServletContext}-based property source
* must wait until the {@code ServletContext} object is available to its enclosing
* {@code ApplicationContext}. In such cases, a stub should be used to hold the
* intended default position/order of the property source, then be replaced
* during context refresh.
* @see org.springframework.context.support.AbstractApplicationContext#initPropertySources()
* @see org.springframework.web.context.support.StandardServletEnvironment
* @see org.springframework.web.context.support.ServletContextPropertySource
*/
public static class StubPropertySource extends PropertySource<Object> {
public StubPropertySource(String name) {
super(name, new Object());
}
/**
* Always returns {@code null}.
*/
@Override
public String getProperty(String name) {
return null;
}
}
PropertySource$ComparisonPropertySource
/**
* @see PropertySource#named(String)
*/
static class ComparisonPropertySource extends StubPropertySource {
private static final String USAGE_ERROR =
"ComparisonPropertySource instances are for use with collection comparison only";
public ComparisonPropertySource(String name) {
super(name);
}
@Override
public Object getSource() {
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
public boolean containsProperty(String name) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
@Override
public String getProperty(String name) {
throw new UnsupportedOperationException(USAGE_ERROR);
}
}
}
PropertySources(Interface)
package org.springframework.core.env;
/**
* Holder containing one or more {@link PropertySource} objects.
*
* @author Chris Beams
* @since 3.1
*/
public interface PropertySources extends Iterable<PropertySource<?>> {
/**
* Return whether a property source with the given name is contained.
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
*/
boolean contains(String name);
/**
* Return the property source with the given name, {@code null} if not found.
* @param name the {@linkplain PropertySource#getName() name of the property source} to find
*/
PropertySource<?> get(String name);
}
MutablePropertySources
package org.springframework.core.env;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Default implementation of the {@link PropertySources} interface.
* Allows manipulation of contained property sources and provides a constructor
* for copying an existing {@code PropertySources} instance.
*
* <p>Where <em>precedence</em> is mentioned in methods such as {@link #addFirst}
* and {@link #addLast}, this is with regard to the order in which property sources
* will be searched when resolving a given property with a {@link PropertyResolver}.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see PropertySourcesPropertyResolver
*/
public class MutablePropertySources implements PropertySources {
private final Log logger;
private final List<PropertySource<?>> propertySourceList = new CopyOnWriteArrayList<PropertySource<?>>();
/**
* Create a new {@link MutablePropertySources} object.
*/
public MutablePropertySources() {
this.logger = LogFactory.getLog(getClass());
}
/**
* Create a new {@code MutablePropertySources} from the given propertySources
* object, preserving the original order of contained {@code PropertySource} objects.
*/
public MutablePropertySources(PropertySources propertySources) {
this();
for (PropertySource<?> propertySource : propertySources) {
addLast(propertySource);
}
}
/**
* Create a new {@link MutablePropertySources} object and inherit the given logger,
* usually from an enclosing {@link Environment}.
*/
MutablePropertySources(Log logger) {
this.logger = logger;
}
@Override
public boolean contains(String name) {
return this.propertySourceList.contains(PropertySource.named(name));
}
@Override
public PropertySource<?> get(String name) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
return (index != -1 ? this.propertySourceList.get(index) : null);
}
@Override
public Iterator<PropertySource<?>> iterator() {
return this.propertySourceList.iterator();
}
/**
* Add the given property source object with highest precedence.
*/
public void addFirst(PropertySource<?> propertySource) {
if (logger.isDebugEnabled()) {
logger.debug("Adding PropertySource '" + propertySource.getName()
+ "' with highest search precedence");
}
removeIfPresent(propertySource);
this.propertySourceList.add(0, propertySource);
}
/**
* Add the given property source object with lowest precedence.
*/
public void addLast(PropertySource<?> propertySource) {
if (logger.isDebugEnabled()) {
logger.debug("Adding PropertySource '" + propertySource.getName() + "' with lowest search precedence");
}
removeIfPresent(propertySource);
this.propertySourceList.add(propertySource);
}
/**
* Add the given property source object with precedence immediately higher
* than the named relative property source.
*/
public void addBefore(String relativePropertySourceName, PropertySource<?> propertySource) {
if (logger.isDebugEnabled()) {
logger.debug("Adding PropertySource '" + propertySource.getName() +
"' with search precedence immediately higher than '" + relativePropertySourceName + "'");
}
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index, propertySource);
}
/**
* Add the given property source object with precedence immediately lower
* than the named relative property source.
*/
public void addAfter(String relativePropertySourceName, PropertySource<?> propertySource) {
if (logger.isDebugEnabled()) {
logger.debug("Adding PropertySource '" + propertySource.getName() +
"' with search precedence immediately lower than '" + relativePropertySourceName + "'");
}
assertLegalRelativeAddition(relativePropertySourceName, propertySource);
removeIfPresent(propertySource);
int index = assertPresentAndGetIndex(relativePropertySourceName);
addAtIndex(index + 1, propertySource);
}
/**
* Return the precedence of the given property source, {@code -1} if not found.
*/
public int precedenceOf(PropertySource<?> propertySource) {
return this.propertySourceList.indexOf(propertySource);
}
/**
* Remove and return the property source with the given name, {@code null} if not found.
* @param name the name of the property source to find and remove
*/
public PropertySource<?> remove(String name) {
if (logger.isDebugEnabled()) {
logger.debug("Removing PropertySource '" + name + "'");
}
int index = this.propertySourceList.indexOf(PropertySource.named(name));
return (index != -1 ? this.propertySourceList.remove(index) : null);
}
/**
* Replace the property source with the given name with the given property source object.
* @param name the name of the property source to find and replace
* @param propertySource the replacement property source
* @throws IllegalArgumentException if no property source with the given name is present
* @see #contains
*/
public void replace(String name, PropertySource<?> propertySource) {
if (logger.isDebugEnabled()) {
logger.debug("Replacing PropertySource '" + name + "' with '" + propertySource.getName() + "'");
}
int index = assertPresentAndGetIndex(name);
this.propertySourceList.set(index, propertySource);
}
/**
* Return the number of {@link PropertySource} objects contained.
*/
public int size() {
return this.propertySourceList.size();
}
@Override
public String toString() {
return this.propertySourceList.toString();
}
/**
* Ensure that the given property source is not being added relative to itself.
*/
protected void assertLegalRelativeAddition(String relativePropertySourceName
, PropertySource<?> propertySource) {
String newPropertySourceName = propertySource.getName();
if (relativePropertySourceName.equals(newPropertySourceName)) {
throw new IllegalArgumentException(
"PropertySource named '" + newPropertySourceName + "' cannot be added relative to itself");
}
}
/**
* Remove the given property source if it is present.
*/
protected void removeIfPresent(PropertySource<?> propertySource) {
this.propertySourceList.remove(propertySource);
}
/**
* Add the given property source at a particular index in the list.
*/
private void addAtIndex(int index, PropertySource<?> propertySource) {
removeIfPresent(propertySource);
this.propertySourceList.add(index, propertySource);
}
/**
* Assert that the named property source is present and return its index.
* @param name {@linkplain PropertySource#getName() name of the property source} to find
* @throws IllegalArgumentException if the named property source is not present
*/
private int assertPresentAndGetIndex(String name) {
int index = this.propertySourceList.indexOf(PropertySource.named(name));
if (index == -1) {
throw new IllegalArgumentException("PropertySource named '" + name + "' does not exist");
}
return index;
}
}
PropertyResolver(Interface)
ConfigurablePropertyResolver(Interface)
PropertySourcesPropertyResolver
PropertySourcesLoader
/*
* Copyright 2012-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.boot.env;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.SpringFactoriesLoader;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;
/**
* Utility that can be used to {@link MutablePropertySources} using
* {@link PropertySourceLoader}s.
*
* @author Phillip Webb
*/
public class PropertySourcesLoader {
private static final Log logger = LogFactory.getLog(PropertySourcesLoader.class);
private final MutablePropertySources propertySources;
private final List<PropertySourceLoader> loaders;
/**
* Create a new {@link PropertySourceLoader} instance backed by a new
* {@link MutablePropertySources}.
*/
public PropertySourcesLoader() {
this(new MutablePropertySources());
}
/**
* Create a new {@link PropertySourceLoader} instance backed by the specified
* {@link MutablePropertySources}.
* @param propertySources the destination property sources
*/
public PropertySourcesLoader(MutablePropertySources propertySources) {
Assert.notNull(propertySources, "PropertySources must not be null");
this.propertySources = propertySources;
this.loaders = SpringFactoriesLoader.loadFactories(PropertySourceLoader.class,
getClass().getClassLoader());
}
/**
* Load the specified resource (if possible) and add it as the first source.
* @param resource the source resource (may be {@code null}).
* @return the loaded property source or {@code null}
* @throws IOException if the source cannot be loaded
*/
public PropertySource<?> load(Resource resource) throws IOException {
return load(resource, null);
}
/**
* Load the profile-specific properties from the specified resource (if any) and add
* it as the first source.
* @param resource the source resource (may be {@code null}).
* @param profile a specific profile to load or {@code null} to load the default.
* @return the loaded property source or {@code null}
* @throws IOException if the source cannot be loaded
*/
public PropertySource<?> load(Resource resource, String profile) throws IOException {
return load(resource, resource.getDescription(), profile);
}
/**
* Load the profile-specific properties from the specified resource (if any), give the
* name provided and add it as the first source.
* @param resource the source resource (may be {@code null}).
* @param name the root property name (may be {@code null}).
* @param profile a specific profile to load or {@code null} to load the default.
* @return the loaded property source or {@code null}
* @throws IOException if the source cannot be loaded
*/
public PropertySource<?> load(Resource resource, String name, String profile)
throws IOException {
return load(resource, null, name, profile);
}
/**
* Load the profile-specific properties from the specified resource (if any), give the
* name provided and add it to a group of property sources identified by the group
* name. Property sources are added to the end of a group, but new groups are added as
* the first in the chain being assembled. This means the normal sequence of calls is
* to first create the group for the default (null) profile, and then add specific
* groups afterwards (with the highest priority last). Property resolution from the
* resulting sources will consider all keys for a given group first and then move to
* the next group.
* @param resource the source resource (may be {@code null}).
* @param group an identifier for the group that this source belongs to
* @param name the root property name (may be {@code null}).
* @param profile a specific profile to load or {@code null} to load the default.
* @return the loaded property source or {@code null}
* @throws IOException if the source cannot be loaded
*/
public PropertySource<?> load(Resource resource, String group, String name,
String profile) throws IOException {
if (isFile(resource)) {
String sourceName = generatePropertySourceName(name, profile);
// 使用properties/yml加载器加载配置
for (PropertySourceLoader loader : this.loaders) {
if (canLoadFileExtension(loader, resource)) {
// 加载器加载配置文件的结果封装为PropertySource对象
// yml加载器将内容封装为map放入PropertySource
PropertySource<?> specific = loader.load(sourceName, resource,
profile);
// 一个profile对应一个分组group,将profile对应的分组group放入
// PropertySourcesLoader的属性MutablePropertySources propertySources中
addPropertySource(group, specific, profile);
return specific;
}
}
}
return null;
}
private boolean isFile(Resource resource) {
return resource != null && resource.exists() && StringUtils
.hasText(StringUtils.getFilenameExtension(resource.getFilename()));
}
private String generatePropertySourceName(String name, String profile) {
return (profile == null ? name : name + "#" + profile);
}
private boolean canLoadFileExtension(PropertySourceLoader loader, Resource resource) {
String filename = resource.getFilename().toLowerCase();
for (String extension : loader.getFileExtensions()) {
if (filename.endsWith("." + extension.toLowerCase())) {
return true;
}
}
return false;
}
private void addPropertySource(String basename, PropertySource<?> source,
String profile) {
if (source == null) {
return;
}
if (basename == null) {
this.propertySources.addLast(source);
return;
}
EnumerableCompositePropertySource group = getGeneric(basename);
group.add(source);
logger.trace("Adding PropertySource: " + source + " in group: " + basename);
if (this.propertySources.contains(group.getName())) {
this.propertySources.replace(group.getName(), group);
}
else {
this.propertySources.addFirst(group);
}
}
private EnumerableCompositePropertySource getGeneric(String name) {
PropertySource<?> source = this.propertySources.get(name);
if (source instanceof EnumerableCompositePropertySource) {
return (EnumerableCompositePropertySource) source;
}
EnumerableCompositePropertySource composite = new EnumerableCompositePropertySource(
name);
return composite;
}
/**
* Return the {@link MutablePropertySources} being loaded.
* @return the property sources
*/
public MutablePropertySources getPropertySources() {
return this.propertySources;
}
/**
* Returns all file extensions that could be loaded.
* @return the file extensions
*/
public Set<String> getAllFileExtensions() {
Set<String> fileExtensions = new LinkedHashSet<String>();
//this.loaders两个对象:PropertiesPropertySourceLoader/YamlPropertySourceLoader
for (PropertySourceLoader loader : this.loaders) {
//PropertiesPropertySourceLoader#getFileExtensions:new String[] { "properties", "xml" }
//YamlPropertySourceLoader#getFileExtensions:new String[] { "yml", "yaml" }
fileExtensions.addAll(Arrays.asList(loader.getFileExtensions()));
}
return fileExtensions;
}
}
PropertyValue
PropertyValue对象来保存单个bean属性的信息和值。
比起仅仅将所有属性存储在一个以属性名为键的map中,使用一个PropertyValue对象,会有更大的灵活性,且优化了索引属性的能力。
注意,值不需要是最终需要的类型:一个BeanWrapper对象应该可以处理所有必要的转换,因为这个对象不知道它将应用到的对象的任何信息。
package org.springframework.beans;
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
*/
@SuppressWarnings("serial")
public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable {
private final String name;
private final Object value;
private boolean optional = false;
private boolean converted = false;
private Object convertedValue;
/** Package-visible field that indicates whether conversion is necessary */
volatile Boolean conversionNecessary;
/** Package-visible field for caching the resolved property path tokens */
transient volatile Object resolvedTokens;
/**
* Create a new PropertyValue instance.
* @param name the name of the property (never {@code null})
* @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})
*/
public PropertyValue(PropertyValue original) {
Assert.notNull(original, "Original must not be null");
this.name = original.getName();
this.value = original.getValue();
this.optional = original.isOptional();
this.converted = original.converted;
this.convertedValue = original.convertedValue;
this.conversionNecessary = original.conversionNecessary;
this.resolvedTokens = original.resolvedTokens;
setSource(original.getSource());
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})
* @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.optional = original.isOptional();
this.conversionNecessary = original.conversionNecessary;
this.resolvedTokens = original.resolvedTokens;
setSource(original);
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;
Object source = getSource();
while (source instanceof PropertyValue && source != original) {
original = (PropertyValue) source;
source = original.getSource();
}
return original;
}
/**
* Set whether this is an optional value, that is, to be ignored
* when no corresponding property exists on the target class.
* @since 3.0
*/
public void setOptional(boolean optional) {
this.optional = optional;
}
/**
* Return whether this is an optional value, that is, to be ignored
* when no corresponding property exists on the target class.
* @since 3.0
*/
public boolean isOptional() {
return this.optional;
}
/**
* Return whether this holder contains a converted value already ({@code true}),
* or whether the value still needs to be converted ({@code false}).
*/
public synchronized boolean isConverted() {
return this.converted;
}
/**
* Set the converted value of the constructor argument,
* after processed type conversion.
*/
public synchronized void setConvertedValue(Object value) {
this.converted = true;
this.convertedValue = value;
}
/**
* Return the converted value of the constructor argument,
* after processed type conversion.
*/
public synchronized Object getConvertedValue() {
return this.convertedValue;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof PropertyValue)) {
return false;
}
PropertyValue otherPv = (PropertyValue) other;
return (this.name.equals(otherPv.name) &&
ObjectUtils.nullSafeEquals(this.value, otherPv.value) &&
ObjectUtils.nullSafeEquals(getSource(), otherPv.getSource()));
}
@Override
public int hashCode() {
return this.name.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.value);
}
@Override
public String toString() {
return "bean property '" + this.name + "'";
}
}
PropertyValues
包含一个或多个PropertyValue对象的容器,通常包括针对特定目标bean的一次更新。
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}
*/
PropertyValue getPropertyValue(String propertyName);
/**
* Return the changes since the previous PropertyValues.
* Subclasses should also override {@code equals}.
* @param old old property values
* @return PropertyValues updated or new properties.
* Return empty PropertyValues if there are no changes.
* @see Object#equals
*/
PropertyValues changesSince(PropertyValues old);
/**
* Is there a property value (or other processing entry) for this property?
* @param propertyName the name of the property we're interested in
* @return whether there is a property value for this property
*/
boolean contains(String propertyName);
/**
* Does this holder not contain any PropertyValue objects at all?
*/
boolean isEmpty();
}