Polishing along with backports to 3.1.4

This commit is contained in:
Juergen Hoeller 2012-12-12 12:38:34 +01:00 committed by unknown
parent 3458d4d945
commit 6e8117c627
2 changed files with 91 additions and 82 deletions

View File

@ -36,8 +36,9 @@ import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils; import org.springframework.util.StringUtils;
/** /**
* Simple PropertyAccessor that uses reflection to access properties for reading and writing. A property can be accessed * Simple PropertyAccessor that uses reflection to access properties for reading and writing.
* if it is accessible as a field on the object or through a getter (if being read) or a setter (if being written). * A property can be accessed if it is accessible as a field on the object or through a
* getter (if being read) or a setter (if being written).
* *
* @author Andy Clement * @author Andy Clement
* @author Juergen Hoeller * @author Juergen Hoeller
@ -45,11 +46,11 @@ import org.springframework.util.StringUtils;
*/ */
public class ReflectivePropertyAccessor implements PropertyAccessor { public class ReflectivePropertyAccessor implements PropertyAccessor {
protected final Map<CacheKey, InvokerPair> readerCache = new ConcurrentHashMap<CacheKey, InvokerPair>(); private final Map<CacheKey, InvokerPair> readerCache = new ConcurrentHashMap<CacheKey, InvokerPair>();
protected final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>(); private final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>();
protected final Map<CacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<CacheKey, TypeDescriptor>(); private final Map<CacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<CacheKey, TypeDescriptor>();
/** /**
@ -365,52 +366,6 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return null; return null;
} }
/**
* Captures the member (method/field) to call reflectively to access a property value and the type descriptor for the
* value returned by the reflective call.
*/
private static class InvokerPair {
final Member member;
final TypeDescriptor typeDescriptor;
public InvokerPair(Member member, TypeDescriptor typeDescriptor) {
this.member = member;
this.typeDescriptor = typeDescriptor;
}
}
private static class CacheKey {
private final Class clazz;
private final String name;
public CacheKey(Class clazz, String name) {
this.clazz = clazz;
this.name = name;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CacheKey)) {
return false;
}
CacheKey otherKey = (CacheKey) other;
return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name));
}
@Override
public int hashCode() {
return this.clazz.hashCode() * 29 + this.name.hashCode();
}
}
/** /**
* Attempt to create an optimized property accessor tailored for a property of a particular name on * Attempt to create an optimized property accessor tailored for a property of a particular name on
* a particular class. The general ReflectivePropertyAccessor will always work but is not optimal * a particular class. The general ReflectivePropertyAccessor will always work but is not optimal
@ -463,29 +418,82 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return this; return this;
} }
/** /**
* An optimized form of a PropertyAccessor that will use reflection but only knows how to access a particular property * Captures the member (method/field) to call reflectively to access a property value
* on a particular class. This is unlike the general ReflectivePropertyResolver which manages a cache of methods/fields that * and the type descriptor for the value returned by the reflective call.
* may be invoked to access different properties on different classes. This optimal accessor exists because looking up
* the appropriate reflective object by class/name on each read is not cheap.
*/ */
static class OptimalPropertyAccessor implements PropertyAccessor { private static class InvokerPair {
final Member member;
final TypeDescriptor typeDescriptor;
public InvokerPair(Member member, TypeDescriptor typeDescriptor) {
this.member = member;
this.typeDescriptor = typeDescriptor;
}
}
private static class CacheKey {
private final Class clazz;
private final String name;
public CacheKey(Class clazz, String name) {
this.clazz = clazz;
this.name = name;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof CacheKey)) {
return false;
}
CacheKey otherKey = (CacheKey) other;
return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name));
}
@Override
public int hashCode() {
return this.clazz.hashCode() * 29 + this.name.hashCode();
}
}
/**
* An optimized form of a PropertyAccessor that will use reflection but only knows
* how to access a particular property on a particular class. This is unlike the
* general ReflectivePropertyResolver which manages a cache of methods/fields that
* may be invoked to access different properties on different classes. This optimal
* accessor exists because looking up the appropriate reflective object by class/name
* on each read is not cheap.
*/
private static class OptimalPropertyAccessor implements PropertyAccessor {
private final Member member; private final Member member;
private final TypeDescriptor typeDescriptor; private final TypeDescriptor typeDescriptor;
private final boolean needsToBeMadeAccessible; private final boolean needsToBeMadeAccessible;
OptimalPropertyAccessor(InvokerPair target) { OptimalPropertyAccessor(InvokerPair target) {
this.member = target.member; this.member = target.member;
this.typeDescriptor = target.typeDescriptor; this.typeDescriptor = target.typeDescriptor;
if (this.member instanceof Field) { if (this.member instanceof Field) {
Field field = (Field)member; Field field = (Field) this.member;
needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) || !Modifier.isPublic(field.getDeclaringClass().getModifiers())) this.needsToBeMadeAccessible = (!Modifier.isPublic(field.getModifiers()) ||
&& !field.isAccessible(); !Modifier.isPublic(field.getDeclaringClass().getModifiers())) && !field.isAccessible();
} }
else { else {
Method method = (Method)member; Method method = (Method) this.member;
needsToBeMadeAccessible = ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) this.needsToBeMadeAccessible = ((!Modifier.isPublic(method.getModifiers()) ||
&& !method.isAccessible()); !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible());
} }
} }
@ -501,8 +509,8 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
if (type.isArray()) { if (type.isArray()) {
return false; return false;
} }
if (member instanceof Method) { if (this.member instanceof Method) {
Method method = (Method)member; Method method = (Method) this.member;
String getterName = "get" + StringUtils.capitalize(name); String getterName = "get" + StringUtils.capitalize(name);
if (getterName.equals(method.getName())) { if (getterName.equals(method.getName())) {
return true; return true;
@ -511,31 +519,31 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
return getterName.equals(method.getName()); return getterName.equals(method.getName());
} }
else { else {
Field field = (Field)member; Field field = (Field) this.member;
return field.getName().equals(name); return field.getName().equals(name);
} }
} }
public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException { public TypedValue read(EvaluationContext context, Object target, String name) throws AccessException {
if (member instanceof Method) { if (this.member instanceof Method) {
try { try {
if (needsToBeMadeAccessible) { if (this.needsToBeMadeAccessible) {
ReflectionUtils.makeAccessible((Method) member); ReflectionUtils.makeAccessible((Method) this.member);
} }
Object value = ((Method) member).invoke(target); Object value = ((Method) this.member).invoke(target);
return new TypedValue(value, typeDescriptor.narrow(value)); return new TypedValue(value, this.typeDescriptor.narrow(value));
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access property '" + name + "' through getter", ex); throw new AccessException("Unable to access property '" + name + "' through getter", ex);
} }
} }
if (member instanceof Field) { if (this.member instanceof Field) {
try { try {
if (needsToBeMadeAccessible) { if (this.needsToBeMadeAccessible) {
ReflectionUtils.makeAccessible((Field)member); ReflectionUtils.makeAccessible((Field) this.member);
} }
Object value = ((Field)member).get(target); Object value = ((Field) this.member).get(target);
return new TypedValue(value, typeDescriptor.narrow(value)); return new TypedValue(value, this.typeDescriptor.narrow(value));
} }
catch (Exception ex) { catch (Exception ex) {
throw new AccessException("Unable to access field: " + name, ex); throw new AccessException("Unable to access field: " + name, ex);
@ -544,12 +552,11 @@ public class ReflectivePropertyAccessor implements PropertyAccessor {
throw new AccessException("Neither getter nor field found for property '" + name + "'"); throw new AccessException("Neither getter nor field found for property '" + name + "'");
} }
public boolean canWrite(EvaluationContext context, Object target, String name) throws AccessException { public boolean canWrite(EvaluationContext context, Object target, String name) {
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor"); throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
} }
public void write(EvaluationContext context, Object target, String name, Object newValue) public void write(EvaluationContext context, Object target, String name, Object newValue) {
throws AccessException {
throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor"); throw new UnsupportedOperationException("Should not be called on an OptimalPropertyAccessor");
} }
} }

View File

@ -55,6 +55,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
private static final Log logger = LogFactory.getLog(ResourceDatabasePopulator.class); private static final Log logger = LogFactory.getLog(ResourceDatabasePopulator.class);
private List<Resource> scripts = new ArrayList<Resource>(); private List<Resource> scripts = new ArrayList<Resource>();
private String sqlScriptEncoding; private String sqlScriptEncoding;
@ -127,6 +128,7 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
this.ignoreFailedDrops = ignoreFailedDrops; this.ignoreFailedDrops = ignoreFailedDrops;
} }
public void populate(Connection connection) throws SQLException { public void populate(Connection connection) throws SQLException {
for (Resource script : this.scripts) { for (Resource script : this.scripts) {
executeSqlScript(connection, applyEncodingIfNecessary(script), this.continueOnError, this.ignoreFailedDrops); executeSqlScript(connection, applyEncodingIfNecessary(script), this.continueOnError, this.ignoreFailedDrops);
@ -191,8 +193,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop"); boolean dropStatement = StringUtils.startsWithIgnoreCase(statement.trim(), "drop");
if (continueOnError || (dropStatement && ignoreFailedDrops)) { if (continueOnError || (dropStatement && ignoreFailedDrops)) {
if (logger.isDebugEnabled()) { if (logger.isDebugEnabled()) {
logger.debug("Failed to execute SQL script statement at line " + lineNumber logger.debug("Failed to execute SQL script statement at line " + lineNumber +
+ " of resource " + resource + ": " + statement, ex); " of resource " + resource + ": " + statement, ex);
} }
} }
else { else {
@ -227,8 +229,8 @@ public class ResourceDatabasePopulator implements DatabasePopulator {
String currentStatement = lnr.readLine(); String currentStatement = lnr.readLine();
StringBuilder scriptBuilder = new StringBuilder(); StringBuilder scriptBuilder = new StringBuilder();
while (currentStatement != null) { while (currentStatement != null) {
if (StringUtils.hasText(currentStatement) if (StringUtils.hasText(currentStatement) &&
&& (this.commentPrefix != null && !currentStatement.startsWith(this.commentPrefix))) { (this.commentPrefix != null && !currentStatement.startsWith(this.commentPrefix))) {
if (scriptBuilder.length() > 0) { if (scriptBuilder.length() > 0) {
scriptBuilder.append('\n'); scriptBuilder.append('\n');
} }