AOP的cache 大家都很熟悉,关于cachekey的生成方式有多种,最简单的是className+methodName+parameters ,这种方法的弊端太多,如果parameter是一个复合对像,那么在拼key时就比较麻烦。
我在以前的一片文章中讲过用annotation来标注复合对象中需要作为cache的子对象,能解决复合对象generate cachekey的问题,但这是一种侵入式的,需要加入额外的代码,真正的AOP cache应该式非侵入式的,就是cache的生效不需要其它代码有任何额外的工作。做function的程序员不需要关心cache的任何东西,cache对他们来说是透明的。
所以这里我想到了一种用HashCode来作为AOP cache的实现。这样不管参数的类型是什么,对于我generate cachekey 都没有影响。
具体实现类:
[b]
Key generate Interface
[/b]
[b]
CacheGenerate 实现类
[/b]
我在以前的一片文章中讲过用annotation来标注复合对象中需要作为cache的子对象,能解决复合对象generate cachekey的问题,但这是一种侵入式的,需要加入额外的代码,真正的AOP cache应该式非侵入式的,就是cache的生效不需要其它代码有任何额外的工作。做function的程序员不需要关心cache的任何东西,cache对他们来说是透明的。
所以这里我想到了一种用HashCode来作为AOP cache的实现。这样不管参数的类型是什么,对于我generate cachekey 都没有影响。
具体实现类:
[b]
Key generate Interface
[/b]
import java.io.Serializable;
import org.aopalliance.intercept.MethodInvocation;
/**
* <p>
* Generates a unique key based on the description of an invocation to an
* intercepted method.
* </p>
*
* @author Elicer Zheng
*/
public interface CacheKeyGenerator {
/**
* Generates the key for a cache entry.
*
* @param methodInvocation
* the description of an invocation to the intercepted method.
* @return the created key.
*/
Serializable generateKey(MethodInvocation methodInvocation);
}
[b]
CacheGenerate 实现类
[/b]
import java.io.Serializable;
import java.lang.reflect.Method;
import org.aopalliance.intercept.MethodInvocation;
/**
* <p>
* Generates the key for a cache entry using the hashCode of the intercepted
* method and its arguments.
* </p>
*
* @author Elicer Zheng
*/
public class HashCodeCacheKeyGenerator implements CacheKeyGenerator {
/**
* Flag that indicates if this generator should generate the hash code of the
* arguments passed to the method to apply caching to. If <code>false</code>,
* this generator uses the default hash code of the arguments.
*/
private boolean generateArgumentHashCode;
/**
* Construct a <code>HashCodeCacheKeyGenerator</code>.
*/
public HashCodeCacheKeyGenerator() {
super();
}
/**
* Construct a <code>HashCodeCacheKeyGenerator</code>.
*
* @param generateArgumentHashCode
* the new value for the flag that indicates if this generator should
* generate the hash code of the arguments passed to the method to
* apply caching to. If <code>false</code>, this generator uses
* the default hash code of the arguments.
*/
public HashCodeCacheKeyGenerator(boolean generateArgumentHashCode) {
this();
setGenerateArgumentHashCode(generateArgumentHashCode);
}
/**
* @see CacheKeyGenerator#generateKey(MethodInvocation)
*/
public final Serializable generateKey(MethodInvocation methodInvocation) {
HashCodeCalculator hashCodeCalculator = new HashCodeCalculator();
Method method = methodInvocation.getMethod();
hashCodeCalculator.append(System.identityHashCode(method));
Object[] methodArguments = methodInvocation.getArguments();
if (methodArguments != null) {
int methodArgumentCount = methodArguments.length;
for (int i = 0; i < methodArgumentCount; i++) {
Object methodArgument = methodArguments[i];
int hash = 0;
if (generateArgumentHashCode) {
hash = ReflectionsUtility.reflectionHashCode(methodArgument);
} else {
hash = ObjectsUtility.nullSafeHashCode(methodArgument);
}
hashCodeCalculator.append(hash);
}
}
long checkSum = hashCodeCalculator.getCheckSum();
int hashCode = hashCodeCalculator.getHashCode();
Serializable cacheKey = new HashCodeCacheKey(checkSum, hashCode);
return cacheKey;
}
/**
* Sets the flag that indicates if this generator should generate the hash
* code of the arguments passed to the method to apply caching to. If
* <code>false</code>, this generator uses the default hash code of the
* arguments.
*
* @param newGenerateArgumentHashCode
* the new value of the flag
*/
public final void setGenerateArgumentHashCode(
boolean newGenerateArgumentHashCode) {
generateArgumentHashCode = newGenerateArgumentHashCode;
}
}
import java.io.Serializable;
/**
* <p>
* Cache key which value is based on a pre-calculated hash code.
* </p>
*
* @author Elicer Zheng
*/
public final class HashCodeCacheKey implements Serializable {
private static final long serialVersionUID = 3904677167731454262L;
/**
* Number that helps keep the uniqueness of this key.
*/
private long checkSum;
/**
* Pre-calculated hash code.
*/
private int hashCode;
/**
* Construct a <code>HashCodeCacheKey</code>.
*/
public HashCodeCacheKey() {
super();
}
/**
* Construct a <code>HashCodeCacheKey</code>.
*
* @param newCheckSum
* the number that helps keep the uniqueness of this key
* @param newHashCode
* the pre-calculated hash code
*/
public HashCodeCacheKey(long newCheckSum, int newHashCode) {
this();
setCheckSum(newCheckSum);
setHashCode(newHashCode);
}
/**
* @see Object#equals(Object)
*/
public boolean equals(Object obj) {
if (this == obj) return true;
if (!(obj instanceof HashCodeCacheKey)) return false;
HashCodeCacheKey other = (HashCodeCacheKey) obj;
if (checkSum != other.checkSum) return false;
if (hashCode != other.hashCode) return false;
return true;
}
/**
* @return the number that helps keep the uniqueness of this key
*/
public long getCheckSum() {
return checkSum;
}
/**
* @return the pre-calculated hash code
*/
public int getHashCode() {
return hashCode;
}
/**
* @see Object#hashCode()
*/
public int hashCode() {
return getHashCode();
}
/**
* Sets the number that helps keep the uniqueness of this key.
*
* @param newCheckSum
* the new number
*/
public void setCheckSum(long newCheckSum) {
checkSum = newCheckSum;
}
/**
* Sets the pre-calculated hash code.
*
* @param newHashCode
* the new hash code
*/
public void setHashCode(int newHashCode) {
hashCode = newHashCode;
}
/**
* @see Object#toString()
*/
public String toString() {
return getHashCode() + "|" + getCheckSum();
}
}
/**
*
*This class is a HashCode Calculator
*
* @author Elicer Zheng
*/
public final class HashCodeCalculator {
private static final int INITIAL_HASH = 17;
private static final int MULTIPLIER = 37;
private long checkSum;
/**
* Counts the number of times <code>{@link #append(int)}</code> is executed.
* It is also used to build <code>{@link #checkSum}</code> and
* <code>{@link #hashCode}</code>.
*/
private int count;
/**
* Hash code to build;
*/
private int hashCode;
/**
* Constructor.
*/
public HashCodeCalculator() {
super();
hashCode = INITIAL_HASH;
}
/**
* Recalculates <code>{@link #checkSum}</code> and
* <code>{@link #hashCode}</code> using the specified value.
*
* @param value
* the specified value.
*/
public void append(int value) {
count++;
int valueToAppend = count * value;
hashCode = MULTIPLIER * hashCode + (valueToAppend ^ (valueToAppend >>> 16));
checkSum += valueToAppend;
}
/**
* @return the number that ensures that the combination hashCode/checSum is
* unique
*/
public long getCheckSum() {
return checkSum;
}
/**
* @return the calculated hash code
*/
public int getHashCode() {
return hashCode;
}
}
import java.util.HashSet;
import java.util.Set;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* <p>
* Miscellaneous object utility methods.
* </p>
*
* @author Elicer Zheng
*/
public abstract class ObjectsUtility {
private static final String ARRAY_ELEMENT_SEPARATOR = ", ";
private static final String ARRAY_END = "}";
private static final String ARRAY_START = "{";
private static final String EMPTY_ARRAY = "{}";
private static final int INITIAL_HASH = 7;
private static final int MULTIPLIER = 31;
private static final String NULL_ARRAY = "null";
private static final Set primitivesAndWrappers;
static {
primitivesAndWrappers = new HashSet();
primitivesAndWrappers.add(boolean.class);
primitivesAndWrappers.add(Boolean.class);
primitivesAndWrappers.add(byte.class);
primitivesAndWrappers.add(Byte.class);
primitivesAndWrappers.add(char.class);
primitivesAndWrappers.add(Character.class);
primitivesAndWrappers.add(double.class);
primitivesAndWrappers.add(Double.class);
primitivesAndWrappers.add(float.class);
primitivesAndWrappers.add(Float.class);
primitivesAndWrappers.add(int.class);
primitivesAndWrappers.add(Integer.class);
primitivesAndWrappers.add(long.class);
primitivesAndWrappers.add(Long.class);
primitivesAndWrappers.add(short.class);
primitivesAndWrappers.add(Short.class);
}
/**
* Returns the same value as <code>{@link Boolean#hashCode()}</code>.
*
* @param bool
* the given <code>boolean</code>.
* @return the hash code for the given <code>boolean</code>.
* @see Boolean#hashCode()
*/
public static int hashCode(boolean bool) {
return bool ? 1231 : 1237;
}
/**
* Returns the same value as <code>{@link Double#hashCode()}</code>.
*
* @param dbl
* the given <code>double</code>.
* @return the hash code for the given <code>double</code>.
* @see Double#hashCode()
*/
public static int hashCode(double dbl) {
long bits = Double.doubleToLongBits(dbl);
return hashCode(bits);
}
/**
* Returns the same value as <code>{@link Float#hashCode()}</code>.
*
* @param flt
* the given <code>float</code>.
* @return the hash code for the given <code>float</code>.
* @see Float#hashCode()
*/
public static int hashCode(float flt) {
return Float.floatToIntBits(flt);
}
/**
* Returns the same value as <code>{@link Long#hashCode()}</code>.
*
* @param lng
* the given <code>long</code>.
* @return the hash code for the given <code>long</code>.
* @see Long#hashCode()
*/
public static int hashCode(long lng) {
return (int) (lng ^ (lng >>> 32));
}
/**
* <p>
* Returns a <code>StringBuffer</code> containing:
* <ol>
* <li>the class name of the given object</li>
* <li>the character '@'</li>
* <li>the hex string for the object's identity hash code</li>
* </ol>
* </p>
* <p>
* This method will return an empty <code>StringBuffer</code> if the given
* object is <code>null</code>.
* </p>
*
* @param obj
* the given object.
* @return a <code>StringBuffer</code> containing identity information of
* the given object.
*/
public static StringBuffer identityToString(Object obj) {
StringBuffer buffer = new StringBuffer();
if (obj != null) {
buffer.append(obj.getClass().getName());
buffer.append("@");
buffer.append(ObjectUtils.getIdentityHexString(obj));
}
return buffer;
}
/**
* Returns <code>true</code> if the given object is an array of primitives.
*
* @param array
* the given object to check.
* @return <code>true</code> if the given object is an array of primitives.
*/
public static boolean isArrayOfPrimitives(Object array) {
boolean primitiveArray = false;
if (array != null) {
Class clazz = array.getClass();
primitiveArray = clazz.isArray()
&& clazz.getComponentType().isPrimitive();
}
return primitiveArray;
}
/**
* Returns <code>true</code> if the given class is any of the following:
* <ul>
* <li><code>boolean</code></li>
* <li>Boolean</li>
* <li><code>byte</code></li>
* <li>Byte</li>
* <li><code>char</code></li>
* <li>Character</li>
* <li><code>double</code></li>
* <li>Double</li>
* <li><code>float</code></li>
* <li>Float</li>
* <li><code>int</code></li>
* <li>Integer</li>
* <li><code>long</code></li>
* <li>Long</li>
* <li><code>short</code></li>
* <li>Short</li>
* </ul>
*
* @param clazz
* the given class.
* @return <code>true</code> if the given class represents a primitive or a
* wrapper, <code>false</code> otherwise.
*/
public static boolean isPrimitiveOrWrapper(Class clazz) {
return primitivesAndWrappers.contains(clazz);
}
/**
* <p>
* Returns a hash code based on the contents of the specified array. For any
* two <code>boolean</code> arrays <code>a</code> and <code>b</code>
* such that <code>Arrays.equals(a, b)</code>, it is also the case that
* <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
* </p>
*
* <p>
* If <code>array</code> is <code>null</code>, this method returns 0.
* </p>
*
* @param array
* the array whose hash value to compute.
* @return a content-based hash code for <code>array</code>.
* @see #hashCode(boolean)
*/
public static int nullSafeHashCode(boolean[] array) {
if (array == null)
return 0;
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + hashCode(array[i]);
}
return hash;
}
/**
* <p>
* Returns a hash code based on the contents of the specified array. For any
* two <code>byte</code> arrays <code>a</code> and <code>b</code> such
* that <code>Arrays.equals(a, b)</code>, it is also the case that
* <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
* </p>
*
* <p>
* If <code>array</code> is <code>null</code>, this method returns 0.
* </p>
*
* @param array
* the array whose hash value to compute.
* @return a content-based hash code for <code>array</code>.
*/
public static int nullSafeHashCode(byte[] array) {
if (array == null)
return 0;
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + array[i];
}
return hash;
}
/**
* <p>
* Returns a hash code based on the contents of the specified array. For any
* two <code>char</code> arrays <code>a</code> and <code>b</code> such
* that <code>Arrays.equals(a, b)</code>, it is also the case that
* <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
* </p>
*
* <p>
* If <code>array</code> is <code>null</code>, this method returns 0.
* </p>
*
* @param array
* the array whose hash value to compute.
* @return a content-based hash code for <code>array</code>.
*/
public static int nullSafeHashCode(char[] array) {
if (array == null)
return 0;
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + array[i];
}
return hash;
}
/**
* <p>
* Returns a hash code based on the contents of the specified array. For any
* two <code>double</code> arrays <code>a</code> and <code>b</code> such
* that <code>Arrays.equals(a, b)</code>, it is also the case that
* <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
* </p>
*
* <p>
* If <code>array</code> is <code>null</code>, this method returns 0.
* </p>
*
* @param array
* the array whose hash value to compute.
* @return a content-based hash code for <code>array</code>.
* @see #hashCode(double)
*/
public static int nullSafeHashCode(double[] array) {
if (array == null)
return 0;
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + hashCode(array[i]);
}
return hash;
}
/**
* <p>
* Returns a hash code based on the contents of the specified array. For any
* two <code>float</code> arrays <code>a</code> and <code>b</code> such
* that <code>Arrays.equals(a, b)</code>, it is also the case that
* <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
* </p>
*
* <p>
* If <code>array</code> is <code>null</code>, this method returns 0.
* </p>
*
* @param array
* the array whose hash value to compute.
* @return a content-based hash code for <code>array</code>.
* @see #hashCode(float)
*/
public static int nullSafeHashCode(float[] array) {
if (array == null)
return 0;
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + hashCode(array[i]);
}
return hash;
}
/**
* <p>
* Returns a hash code based on the contents of the specified array. For any
* two <code>int</code> arrays <code>a</code> and <code>b</code> such
* that <code>Arrays.equals(a, b)</code>, it is also the case that
* <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
* </p>
*
* <p>
* If <code>array</code> is <code>null</code>, this method returns 0.
* </p>
*
* @param array
* the array whose hash value to compute.
* @return a content-based hash code for <code>array</code>.
*/
public static int nullSafeHashCode(int[] array) {
if (array == null)
return 0;
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + array[i];
}
return hash;
}
/**
* <p>
* Returns a hash code based on the contents of the specified array. For any
* two <code>long</code> arrays <code>a</code> and <code>b</code> such
* that <code>Arrays.equals(a, b)</code>, it is also the case that
* <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
* </p>
*
* <p>
* If <code>array</code> is <code>null</code>, this method returns 0.
* </p>
*
* @param array
* the array whose hash value to compute.
* @return a content-based hash code for <code>array</code>.
* @see #hashCode(long)
*/
public static int nullSafeHashCode(long[] array) {
if (array == null)
return 0;
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + hashCode(array[i]);
}
return hash;
}
/**
* <p>
* Returns the value of <code>{@link Object#hashCode()}</code>. If the
* object is an array, this method will delegate to any of the
* <code>nullSafeHashCode</code> methods for arrays in this class.
* </p>
*
* <p>
* If the object is <code>null</code>, this method returns 0.
* </p>
*
* @param obj
* the object whose hash value to compute.
* @return the hash code of the given object.
* @see #nullSafeHashCode(boolean[])
* @see #nullSafeHashCode(byte[])
* @see #nullSafeHashCode(char[])
* @see #nullSafeHashCode(double[])
* @see #nullSafeHashCode(float[])
* @see #nullSafeHashCode(int[])
* @see #nullSafeHashCode(long[])
* @see #nullSafeHashCode(Object[])
* @see #nullSafeHashCode(short[])
*/
public static int nullSafeHashCode(Object obj) {
if (obj == null)
return 0;
if (obj instanceof boolean[]) {
return nullSafeHashCode((boolean[]) obj);
}
if (obj instanceof byte[]) {
return nullSafeHashCode((byte[]) obj);
}
if (obj instanceof char[]) {
return nullSafeHashCode((char[]) obj);
}
if (obj instanceof double[]) {
return nullSafeHashCode((double[]) obj);
}
if (obj instanceof float[]) {
return nullSafeHashCode((float[]) obj);
}
if (obj instanceof int[]) {
return nullSafeHashCode((int[]) obj);
}
if (obj instanceof long[]) {
return nullSafeHashCode((long[]) obj);
}
if (obj instanceof short[]) {
return nullSafeHashCode((short[]) obj);
}
if (obj instanceof Object[]) {
return nullSafeHashCode((Object[]) obj);
}
return obj.hashCode();
}
/**
* <p>
* Returns a hash code based on the contents of the specified array. For any
* two arrays <code>a</code> and <code>b</code> such that
* <code>Arrays.equals(a, b)</code>, it is also the case that
* <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
* </p>
* <p>
* The value returned by this method is equal to the value that would be
* returned by <code>Arrays.asList(a).hashCode()</code>, unless
* <code>array</code> is <code>null</code>, in which case <code>0</code>
* is returned.
* </p>
*
* @param array
* the array whose content-based hash code to compute.
* @return a content-based hash code for <code>array</code>.
*/
public static int nullSafeHashCode(Object[] array) {
if (array == null)
return 0;
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + nullSafeHashCode(array[i]);
}
return hash;
}
/**
* <p>
* Returns a hash code based on the contents of the specified array. For any
* two <code>short</code> arrays <code>a</code> and <code>b</code> such
* that <code>Arrays.equals(a, b)</code>, it is also the case that
* <code>Arrays.hashCode(a) == Arrays.hashCode(b)</code>.
* </p>
*
* <p>
* If <code>array</code> is <code>null</code>, this method returns 0.
*
* @param array
* the array whose hash value to compute
* @return a content-based hash code for <code>array</code>
*/
public static int nullSafeHashCode(short[] array) {
if (array == null)
return 0;
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + array[i];
}
return hash;
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(boolean[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append(array[i]);
}
buffer.append(ARRAY_END);
return buffer.toString();
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(byte[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append(array[i]);
}
buffer.append(ARRAY_END);
return buffer.toString();
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(char[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append("'" + array[i] + "'");
}
buffer.append(ARRAY_END);
return buffer.toString();
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(double[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append(array[i]);
}
buffer.append(ARRAY_END);
return buffer.toString();
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(float[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append(array[i]);
}
buffer.append(ARRAY_END);
return buffer.toString();
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(int[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append(array[i]);
}
buffer.append(ARRAY_END);
return buffer.toString();
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(long[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append(array[i]);
}
buffer.append(ARRAY_END);
return buffer.toString();
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(Object[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append(StringUtils.quoteIfString(array[i]));
}
buffer.append(ARRAY_END);
return buffer.toString();
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(short[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append(array[i]);
}
buffer.append(ARRAY_END);
return buffer.toString();
}
/**
* Returns a string representation of the contents of the specified array. The
* string representation consists of a list of the array's elements, enclosed
* in curly braces (<code>"{}"</code>). Adjacent elements are separated by
* the characters <code>", "</code> (a comma followed by a space). Returns
* <code>"null"</code> if <code>array</code> is <code>null</code>.
*
* @param array
* the array whose string representation to return.
* @return a string representation of <code>array</code>.
*/
public static String nullSafeToString(String[] array) {
if (array == null)
return NULL_ARRAY;
int length = array.length;
if (length == 0)
return EMPTY_ARRAY;
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < length; i++) {
if (i == 0)
buffer.append(ARRAY_START);
else
buffer.append(ARRAY_ELEMENT_SEPARATOR);
buffer.append(StringUtils.quote(array[i]));
}
buffer.append(ARRAY_END);
return buffer.toString();
}
}
import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.lang.reflect.Method;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import org.springframework.util.ReflectionUtils;
/**
* <p>
* Reflection-related utility methods,used to build the hash code for the input parameter.
* </p>
*
*
* @author Elicer Zheng
*/
public abstract class ReflectionsUtility {
private static final int INITIAL_HASH = 7;
private static final int MULTIPLIER = 31;
/**
* <p>
* This method uses reflection to build a valid hash code.
* </p>
*
* <p>
* It uses <code>AccessibleObject.setAccessible</code> to gain access to
* private fields. This means that it will throw a security exception if run
* under a security manager, if the permissions are not set up correctly. It
* is also not as efficient as testing explicitly.
* </p>
*
* <p>
* Transient members will not be used, as they are likely derived fields,
* and not part of the value of the <code>Object</code>.
* </p>
*
* <p>
* Static fields will not be tested. Superclass fields will be included.
* </p>
*
* @param obj
* the object to create a <code>hashCode</code> for
* @return the generated hash code, or zero if the given object is
* <code>null</code>
*/
public static int reflectionHashCode(Object obj) {
if (obj == null)
return 0;
Class targetClass = obj.getClass();
if (ObjectsUtility.isArrayOfPrimitives(obj)
|| ObjectsUtility.isPrimitiveOrWrapper(targetClass)) {
return ObjectsUtility.nullSafeHashCode(obj);
}
if (targetClass.isArray()) {
return reflectionHashCode((Object[]) obj);
}
if (obj instanceof Collection) {
return reflectionHashCode((Collection) obj);
}
if (obj instanceof Map) {
return reflectionHashCode((Map) obj);
}
// determine whether the object's class declares hashCode() or has a
// superClass other than java.lang.Object that declares hashCode()
Class clazz = (obj instanceof Class)? (Class) obj : obj.getClass();
Method hashCodeMethod = ReflectionUtils.findMethod(clazz,
"hashCode", new Class[0]);
if (hashCodeMethod != null) {
return obj.hashCode();
}
// could not find a hashCode other than the one declared by java.lang.Object
int hash = INITIAL_HASH;
try {
while (targetClass != null) {
Field[] fields = targetClass.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
for (int i = 0; i < fields.length; i++) {
Field field = fields[i];
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
hash = MULTIPLIER * hash + reflectionHashCode(field.get(obj));
}
}
targetClass = targetClass.getSuperclass();
}
} catch (IllegalAccessException exception) {
// ///CLOVER:OFF
ReflectionUtils.handleReflectionException(exception);
// ///CLOVER:ON
}
return hash;
}
private static int reflectionHashCode(Collection collection) {
int hash = INITIAL_HASH;
for (Iterator i = collection.iterator(); i.hasNext();) {
hash = MULTIPLIER * hash + reflectionHashCode(i.next());
}
return hash;
}
private static int reflectionHashCode(Map map) {
int hash = INITIAL_HASH;
for (Iterator i = map.entrySet().iterator(); i.hasNext();) {
Map.Entry entry = (Map.Entry) i.next();
hash = MULTIPLIER * hash + reflectionHashCode(entry);
}
return hash;
}
private static int reflectionHashCode(Map.Entry entry) {
int hash = INITIAL_HASH;
hash = MULTIPLIER * hash + reflectionHashCode(entry.getKey());
hash = MULTIPLIER * hash + reflectionHashCode(entry.getValue());
return hash;
}
private static int reflectionHashCode(Object[] array) {
int hash = INITIAL_HASH;
int arraySize = array.length;
for (int i = 0; i < arraySize; i++) {
hash = MULTIPLIER * hash + reflectionHashCode(array[i]);
}
return hash;
}
}