parent
77b7e49fb2
commit
566ff54782
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
|
|
@ -74,8 +74,8 @@ public abstract class ClassUtils {
|
|||
/** The path separator character: {@code '/'}. */
|
||||
private static final char PATH_SEPARATOR = '/';
|
||||
|
||||
/** The inner class separator character: {@code '$'}. */
|
||||
private static final char INNER_CLASS_SEPARATOR = '$';
|
||||
/** The nested class separator character: {@code '$'}. */
|
||||
private static final char NESTED_CLASS_SEPARATOR = '$';
|
||||
|
||||
/** The CGLIB class separator: {@code "$$"}. */
|
||||
public static final String CGLIB_CLASS_SEPARATOR = "$$";
|
||||
|
|
@ -232,7 +232,7 @@ public abstract class ClassUtils {
|
|||
/**
|
||||
* Replacement for {@code Class.forName()} that also returns Class instances
|
||||
* for primitives (e.g. "int") and array class names (e.g. "String[]").
|
||||
* Furthermore, it is also capable of resolving inner class names in Java source
|
||||
* Furthermore, it is also capable of resolving nested class names in Java source
|
||||
* style (e.g. "java.lang.Thread.State" instead of "java.lang.Thread$State").
|
||||
* @param name the name of the Class
|
||||
* @param classLoader the class loader to use
|
||||
|
|
@ -286,10 +286,10 @@ public abstract class ClassUtils {
|
|||
catch (ClassNotFoundException ex) {
|
||||
int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
|
||||
if (lastDotIndex != -1) {
|
||||
String innerClassName =
|
||||
name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
|
||||
String nestedClassName =
|
||||
name.substring(0, lastDotIndex) + NESTED_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
|
||||
try {
|
||||
return Class.forName(innerClassName, false, clToUse);
|
||||
return Class.forName(nestedClassName, false, clToUse);
|
||||
}
|
||||
catch (ClassNotFoundException ex2) {
|
||||
// Swallow - let original exception get through
|
||||
|
|
@ -953,7 +953,7 @@ public abstract class ClassUtils {
|
|||
nameEndIndex = className.length();
|
||||
}
|
||||
String shortName = className.substring(lastDotIndex + 1, nameEndIndex);
|
||||
shortName = shortName.replace(INNER_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
|
||||
shortName = shortName.replace(NESTED_CLASS_SEPARATOR, PACKAGE_SEPARATOR);
|
||||
return shortName;
|
||||
}
|
||||
|
||||
|
|
@ -968,7 +968,7 @@ public abstract class ClassUtils {
|
|||
|
||||
/**
|
||||
* Return the short string name of a Java class in uncapitalized JavaBeans
|
||||
* property format. Strips the outer class name in case of an inner class.
|
||||
* property format. Strips the outer class name in case of a nested class.
|
||||
* @param clazz the class
|
||||
* @return the short name rendered in a standard JavaBeans property format
|
||||
* @see java.beans.Introspector#decapitalize(String)
|
||||
|
|
|
|||
|
|
@ -75,7 +75,4 @@ public class TestObject implements ITestObject, ITestInterface, Comparable<Objec
|
|||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
public static class NestedObject {
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2020 the original author or authors.
|
||||
* Copyright 2002-2021 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.
|
||||
|
|
@ -33,6 +33,7 @@ import java.util.List;
|
|||
import java.util.Set;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Nested;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
|
|
@ -59,14 +60,6 @@ class ClassUtilsTests {
|
|||
private final ClassLoader classLoader = getClass().getClassLoader();
|
||||
|
||||
|
||||
@BeforeEach
|
||||
void clearStatics() {
|
||||
InnerClass.noArgCalled = false;
|
||||
InnerClass.argCalled = false;
|
||||
InnerClass.overloadedCalled = false;
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
void isPresent() {
|
||||
assertThat(ClassUtils.isPresent("java.lang.String", classLoader)).isTrue();
|
||||
|
|
@ -86,8 +79,12 @@ class ClassUtilsTests {
|
|||
assertThat(ClassUtils.forName("org.springframework.tests.sample.objects.TestObject[][]", classLoader)).isEqualTo(TestObject[][].class);
|
||||
assertThat(ClassUtils.forName(TestObject[][].class.getName(), classLoader)).isEqualTo(TestObject[][].class);
|
||||
assertThat(ClassUtils.forName("[[[S", classLoader)).isEqualTo(short[][][].class);
|
||||
assertThat(ClassUtils.forName("org.springframework.tests.sample.objects.TestObject$NestedObject", classLoader)).isEqualTo(TestObject.NestedObject.class);
|
||||
assertThat(ClassUtils.forName("org.springframework.tests.sample.objects.TestObject.NestedObject", classLoader)).isEqualTo(TestObject.NestedObject.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
void forNameWithNestedType() throws ClassNotFoundException {
|
||||
assertThat(ClassUtils.forName("org.springframework.util.ClassUtilsTests$NestedClass", classLoader)).isEqualTo(NestedClass.class);
|
||||
assertThat(ClassUtils.forName("org.springframework.util.ClassUtilsTests.NestedClass", classLoader)).isEqualTo(NestedClass.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -145,11 +142,11 @@ class ClassUtilsTests {
|
|||
assertThat(ClassUtils.isCacheSafe(String.class, childLoader1)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(String.class, childLoader2)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(String.class, childLoader3)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(InnerClass.class, null)).isFalse();
|
||||
assertThat(ClassUtils.isCacheSafe(InnerClass.class, classLoader)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(InnerClass.class, childLoader1)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(InnerClass.class, childLoader2)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(InnerClass.class, childLoader3)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(NestedClass.class, null)).isFalse();
|
||||
assertThat(ClassUtils.isCacheSafe(NestedClass.class, classLoader)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(NestedClass.class, childLoader1)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(NestedClass.class, childLoader2)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(NestedClass.class, childLoader3)).isTrue();
|
||||
assertThat(ClassUtils.isCacheSafe(composite, null)).isFalse();
|
||||
assertThat(ClassUtils.isCacheSafe(composite, classLoader)).isFalse();
|
||||
assertThat(ClassUtils.isCacheSafe(composite, childLoader1)).isTrue();
|
||||
|
|
@ -211,9 +208,9 @@ class ClassUtilsTests {
|
|||
}
|
||||
|
||||
@Test
|
||||
void getShortNameForInnerClass() {
|
||||
String className = ClassUtils.getShortName(InnerClass.class);
|
||||
assertThat(className).as("Class name did not match").isEqualTo("ClassUtilsTests.InnerClass");
|
||||
void getShortNameForNestedClass() {
|
||||
String className = ClassUtils.getShortName(NestedClass.class);
|
||||
assertThat(className).as("Class name did not match").isEqualTo("ClassUtilsTests.NestedClass");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -301,27 +298,6 @@ class ClassUtilsTests {
|
|||
assertThat(ClassUtils.hasAtLeastOneMethodWithName(TestObject.class, "setAge")).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void noArgsStaticMethod() throws IllegalAccessException, InvocationTargetException {
|
||||
Method method = ClassUtils.getStaticMethod(InnerClass.class, "staticMethod");
|
||||
method.invoke(null, (Object[]) null);
|
||||
assertThat(InnerClass.noArgCalled).as("no argument method was not invoked.").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void argsStaticMethod() throws IllegalAccessException, InvocationTargetException {
|
||||
Method method = ClassUtils.getStaticMethod(InnerClass.class, "argStaticMethod", String.class);
|
||||
method.invoke(null, "test");
|
||||
assertThat(InnerClass.argCalled).as("argument method was not invoked.").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void overloadedStaticMethod() throws IllegalAccessException, InvocationTargetException {
|
||||
Method method = ClassUtils.getStaticMethod(InnerClass.class, "staticMethod", String.class);
|
||||
method.invoke(null, "test");
|
||||
assertThat(InnerClass.overloadedCalled).as("argument method was not invoked.").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void isAssignable() {
|
||||
assertThat(ClassUtils.isAssignable(Object.class, Object.class)).isTrue();
|
||||
|
|
@ -433,6 +409,40 @@ class ClassUtilsTests {
|
|||
}
|
||||
|
||||
|
||||
@Nested
|
||||
class GetStaticMethodTests {
|
||||
|
||||
@BeforeEach
|
||||
void clearStatics() {
|
||||
NestedClass.noArgCalled = false;
|
||||
NestedClass.argCalled = false;
|
||||
NestedClass.overloadedCalled = false;
|
||||
}
|
||||
|
||||
@Test
|
||||
void noArgsStaticMethod() throws IllegalAccessException, InvocationTargetException {
|
||||
Method method = ClassUtils.getStaticMethod(NestedClass.class, "staticMethod");
|
||||
method.invoke(null, (Object[]) null);
|
||||
assertThat(NestedClass.noArgCalled).as("no argument method was not invoked.").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void argsStaticMethod() throws IllegalAccessException, InvocationTargetException {
|
||||
Method method = ClassUtils.getStaticMethod(NestedClass.class, "argStaticMethod", String.class);
|
||||
method.invoke(null, "test");
|
||||
assertThat(NestedClass.argCalled).as("argument method was not invoked.").isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void overloadedStaticMethod() throws IllegalAccessException, InvocationTargetException {
|
||||
Method method = ClassUtils.getStaticMethod(NestedClass.class, "staticMethod", String.class);
|
||||
method.invoke(null, "test");
|
||||
assertThat(NestedClass.overloadedCalled).as("argument method was not invoked.").isTrue();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@ValueSource(classes = { Boolean.class, Character.class, Byte.class, Short.class,
|
||||
|
|
@ -447,7 +457,7 @@ class ClassUtilsTests {
|
|||
@interface PrimitiveTypes {
|
||||
}
|
||||
|
||||
public static class InnerClass {
|
||||
public static class NestedClass {
|
||||
|
||||
static boolean noArgCalled;
|
||||
static boolean argCalled;
|
||||
|
|
|
|||
|
|
@ -639,15 +639,16 @@ You can use the `Class` property in one of two ways:
|
|||
from the invocation of the `static` factory method may be the same class or another
|
||||
class entirely.
|
||||
|
||||
.Nested class names
|
||||
****
|
||||
.Inner class names
|
||||
If you want to configure a bean definition for a `static` nested class, you may use
|
||||
either binary name of the nested class or separate it with dot.
|
||||
If you want to configure a bean definition for a nested class, you may use either the
|
||||
binary name or the source name of the nested class.
|
||||
|
||||
For example, if you have a class called `SomeThing` in the `com.example` package, and this
|
||||
`SomeThing` class has a `static` nested class called `OtherThing`, they can be separated
|
||||
by a dollar (`$`) or dot (`.`). So the value of the `class` attribute on a bean definition
|
||||
would be `com.example.SomeThing$OtherThing` or `com.example.SomeThing.OtherThing`.
|
||||
For example, if you have a class called `SomeThing` in the `com.example` package, and
|
||||
this `SomeThing` class has a `static` nested class called `OtherThing`, they can be
|
||||
separated by a dollar sign (`$`) or a dot (`.`). So the value of the `class` attribute in
|
||||
a bean definition would be `com.example.SomeThing$OtherThing` or
|
||||
`com.example.SomeThing.OtherThing`.
|
||||
****
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue