diff --git a/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java b/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java index 9c31ca503f..d62a294a21 100644 --- a/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java +++ b/org.springframework.core/src/main/java/org/springframework/util/ClassUtils.java @@ -39,9 +39,9 @@ import java.util.Set; * framework; consider Jakarta's Commons Lang for a more comprehensive suite * of class utilities. * + * @author Juergen Hoeller * @author Keith Donald * @author Rob Harrop - * @author Juergen Hoeller * @since 1.1 * @see TypeUtils * @see ReflectionUtils @@ -51,8 +51,11 @@ public abstract class ClassUtils { /** Suffix for array class names: "[]" */ public static final String ARRAY_SUFFIX = "[]"; - /** Prefix for internal array class names: "[L" */ - private static final String INTERNAL_ARRAY_PREFIX = "[L"; + /** Prefix for internal array class names: "[" */ + private static final String INTERNAL_ARRAY_PREFIX = "["; + + /** Prefix for internal non-primitive array class names: "[L" */ + private static final String NON_PRIMITIVE_ARRAY_PREFIX = "[L"; /** The package separator character '.' */ private static final char PACKAGE_SEPARATOR = '.'; @@ -85,6 +88,12 @@ public abstract class ClassUtils { */ private static final Map primitiveTypeNameMap = new HashMap(16); + /** + * Map with common "java.lang" class name as key and corresponding Class as value. + * Primarily for efficient deserialization of remote invocations. + */ + private static final Map commonClassCache = new HashMap(32); + static { primitiveWrapperTypeMap.put(Boolean.class, boolean.class); @@ -98,6 +107,7 @@ public abstract class ClassUtils { for (Map.Entry entry : primitiveWrapperTypeMap.entrySet()) { primitiveTypeToWrapperMap.put(entry.getValue(), entry.getKey()); + registerCommonClasses(entry.getKey()); } Set primitiveTypes = new HashSet(16); @@ -108,9 +118,25 @@ public abstract class ClassUtils { for (Class primitiveType : primitiveTypes) { primitiveTypeNameMap.put(primitiveType.getName(), primitiveType); } + + registerCommonClasses(Boolean[].class, Byte[].class, Character[].class, Double[].class, + Float[].class, Integer[].class, Long[].class, Short[].class); + registerCommonClasses(Number.class, Number[].class, String.class, String[].class, + Object.class, Object[].class, Class.class, Class[].class); + registerCommonClasses(Throwable.class, Exception.class, RuntimeException.class, + Error.class, StackTraceElement.class, StackTraceElement[].class); } + /** + * Register the given common classes with the ClassUtils cache. + */ + private static void registerCommonClasses(Class... commonClasses) { + for (Class clazz : commonClasses) { + commonClassCache.put(clazz.getName(), clazz); + } + } + /** * Return the default ClassLoader to use: typically the thread context * ClassLoader, if available; the ClassLoader that loaded the ClassUtils @@ -188,6 +214,9 @@ public abstract class ClassUtils { Assert.notNull(name, "Name must not be null"); Class clazz = resolvePrimitiveClassName(name); + if (clazz == null) { + clazz = commonClassCache.get(name); + } if (clazz != null) { return clazz; } @@ -200,16 +229,16 @@ public abstract class ClassUtils { } // "[Ljava.lang.String;" style arrays - int internalArrayMarker = name.indexOf(INTERNAL_ARRAY_PREFIX); - if (internalArrayMarker != -1 && name.endsWith(";")) { - String elementClassName = null; - if (internalArrayMarker == 0) { - elementClassName = name.substring(INTERNAL_ARRAY_PREFIX.length(), name.length() - 1); - } - else if (name.startsWith("[")) { - elementClassName = name.substring(1); - } - Class elementClass = forName(elementClassName, classLoader); + if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) { + String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1); + Class elementClass = forName(elementName, classLoader); + return Array.newInstance(elementClass, 0).getClass(); + } + + // "[[I" or "[[Ljava.lang.String;" style arrays + if (name.startsWith(INTERNAL_ARRAY_PREFIX)) { + String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length()); + Class elementClass = forName(elementName, classLoader); return Array.newInstance(elementClass, 0).getClass(); } diff --git a/org.springframework.web/src/test/java/org/springframework/beans/ITestBean.java b/org.springframework.web/src/test/java/org/springframework/beans/ITestBean.java index cdf5ef510d..526c3dacfb 100644 --- a/org.springframework.web/src/test/java/org/springframework/beans/ITestBean.java +++ b/org.springframework.web/src/test/java/org/springframework/beans/ITestBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 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. @@ -47,6 +47,22 @@ public interface ITestBean { void setStringArray(String[] stringArray); + Integer[][] getNestedIntegerArray(); + + Integer[] getSomeIntegerArray(); + + void setSomeIntegerArray(Integer[] someIntegerArray); + + void setNestedIntegerArray(Integer[][] nestedIntegerArray); + + int[] getSomeIntArray(); + + void setSomeIntArray(int[] someIntArray); + + int[][] getNestedIntArray(); + + void setNestedIntArray(int[][] someNestedArray); + /** * Throws a given (non-null) exception. */ diff --git a/org.springframework.web/src/test/java/org/springframework/beans/TestBean.java b/org.springframework.web/src/test/java/org/springframework/beans/TestBean.java index 7842bbfeac..4670b8c78b 100644 --- a/org.springframework.web/src/test/java/org/springframework/beans/TestBean.java +++ b/org.springframework.web/src/test/java/org/springframework/beans/TestBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2008 the original author or authors. + * Copyright 2002-2009 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. @@ -66,6 +66,12 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt private Integer[] someIntegerArray; + private Integer[][] nestedIntegerArray; + + private int[] someIntArray; + + private int[][] nestedIntArray; + private Date date = new Date(); private Float myFloat = new Float(0.0); @@ -98,7 +104,6 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt private List pets; - public TestBean() { } @@ -246,6 +251,30 @@ public class TestBean implements BeanNameAware, BeanFactoryAware, ITestBean, IOt this.someIntegerArray = someIntegerArray; } + public Integer[][] getNestedIntegerArray() { + return nestedIntegerArray; + } + + public void setNestedIntegerArray(Integer[][] nestedIntegerArray) { + this.nestedIntegerArray = nestedIntegerArray; + } + + public int[] getSomeIntArray() { + return someIntArray; + } + + public void setSomeIntArray(int[] someIntArray) { + this.someIntArray = someIntArray; + } + + public int[][] getNestedIntArray() { + return nestedIntArray; + } + + public void setNestedIntArray(int[][] nestedIntArray) { + this.nestedIntArray = nestedIntArray; + } + public Date getDate() { return date; } diff --git a/org.springframework.web/src/test/java/org/springframework/remoting/httpinvoker/HttpInvokerTests.java b/org.springframework.web/src/test/java/org/springframework/remoting/httpinvoker/HttpInvokerTests.java index 25a3bd2717..3fe0a439c3 100644 --- a/org.springframework.web/src/test/java/org/springframework/remoting/httpinvoker/HttpInvokerTests.java +++ b/org.springframework.web/src/test/java/org/springframework/remoting/httpinvoker/HttpInvokerTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2007 the original author or authors. + * Copyright 2002-2009 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. @@ -28,7 +28,6 @@ import java.lang.reflect.InvocationTargetException; import java.util.Arrays; import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; - import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @@ -97,6 +96,18 @@ public class HttpInvokerTests extends TestCase { assertEquals(50, proxy.getAge()); proxy.setStringArray(new String[] {"str1", "str2"}); assertTrue(Arrays.equals(new String[] {"str1", "str2"}, proxy.getStringArray())); + proxy.setSomeIntegerArray(new Integer[] {1, 2, 3}); + assertTrue(Arrays.equals(new Integer[] {1, 2, 3}, proxy.getSomeIntegerArray())); + proxy.setNestedIntegerArray(new Integer[][] {{1, 2, 3}, {4, 5, 6}}); + Integer[][] integerArray = proxy.getNestedIntegerArray(); + assertTrue(Arrays.equals(new Integer[] {1, 2, 3}, integerArray[0])); + assertTrue(Arrays.equals(new Integer[] {4, 5, 6}, integerArray[1])); + proxy.setSomeIntArray(new int[] {1, 2, 3}); + assertTrue(Arrays.equals(new int[] {1, 2, 3}, proxy.getSomeIntArray())); + proxy.setNestedIntArray(new int[][] {{1, 2, 3}, {4, 5, 6}}); + int[][] intArray = proxy.getNestedIntArray(); + assertTrue(Arrays.equals(new int[] {1, 2, 3}, intArray[0])); + assertTrue(Arrays.equals(new int[] {4, 5, 6}, intArray[1])); try { proxy.exceptional(new IllegalStateException());