-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
✨ 🐲 TypeSugar to get generic class #253
- Loading branch information
Showing
11 changed files
with
320 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Submodule docs
updated
2 files
+24 −4 | src/0-wings/0d-qa-devops.md | |
+24 −4 | src/zh/0-wings/0d-qa-devops.md |
Submodule mirana
updated
2 files
+11 −1 | src/main/java/pro/fessional/mirana/lock/ArrayKey.java | |
+3 −3 | src/test/java/pro/fessional/mirana/lock/ArrayKeyTest.java |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
120 changes: 120 additions & 0 deletions
120
wings/silencer/src/main/java/pro/fessional/wings/silencer/enhance/TypeSugar.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
package pro.fessional.wings.silencer.enhance; | ||
|
||
import org.jetbrains.annotations.NotNull; | ||
import org.springframework.core.ResolvableType; | ||
import org.springframework.core.convert.TypeDescriptor; | ||
import pro.fessional.mirana.lock.ArrayKey; | ||
|
||
import java.lang.reflect.Type; | ||
import java.math.BigDecimal; | ||
import java.time.LocalDate; | ||
import java.time.LocalDateTime; | ||
import java.time.LocalTime; | ||
import java.time.OffsetDateTime; | ||
import java.time.ZoneId; | ||
import java.time.ZonedDateTime; | ||
import java.util.concurrent.ConcurrentHashMap; | ||
|
||
/** | ||
* ResolvableType for Class with Generics with cache, | ||
* 20 times faster than new ResolvableType, TypeDescriptor | ||
* | ||
* @author trydofor | ||
* @since 2024-06-09 | ||
*/ | ||
public class TypeSugar { | ||
|
||
public static final TypeDescriptor StringDescriptor = TypeDescriptor.valueOf(String.class); | ||
public static final TypeDescriptor BooleanDescriptor = TypeDescriptor.valueOf(Boolean.class); | ||
public static final TypeDescriptor IntegerDescriptor = TypeDescriptor.valueOf(Integer.class); | ||
public static final TypeDescriptor LongDescriptor = TypeDescriptor.valueOf(Long.class); | ||
public static final TypeDescriptor DoubleDescriptor = TypeDescriptor.valueOf(Double.class); | ||
public static final TypeDescriptor FloatDescriptor = TypeDescriptor.valueOf(Float.class); | ||
public static final TypeDescriptor BigDecimalDescriptor = TypeDescriptor.valueOf(BigDecimal.class); | ||
|
||
public static final TypeDescriptor LocalDateDescriptor = TypeDescriptor.valueOf(LocalDate.class); | ||
public static final TypeDescriptor LocalTimeDescriptor = TypeDescriptor.valueOf(LocalTime.class); | ||
public static final TypeDescriptor LocalDateTimeDescriptor = TypeDescriptor.valueOf(LocalDateTime.class); | ||
public static final TypeDescriptor ZonedDateTimeDescriptor = TypeDescriptor.valueOf(ZonedDateTime.class); | ||
public static final TypeDescriptor OffsetDateTimeDescriptor = TypeDescriptor.valueOf(OffsetDateTime.class); | ||
public static final TypeDescriptor ZoneIdDescriptor = TypeDescriptor.valueOf(ZoneId.class); | ||
|
||
|
||
// | ||
private static final ConcurrentHashMap<ArrayKey, ResolvableType> CacheResolvable = new ConcurrentHashMap<>(); | ||
private static final ConcurrentHashMap<ArrayKey, TypeDescriptor> CacheDescriptor = new ConcurrentHashMap<>(); | ||
|
||
/** | ||
* by cache | ||
*/ | ||
@NotNull | ||
public static TypeDescriptor describe(@NotNull Class<?> clazz, Class<?>... generics) { | ||
ArrayKey key = generics == null || generics.length == 0 | ||
? new ArrayKey(clazz) | ||
: new ArrayKey(clazz, generics); | ||
return CacheDescriptor.computeIfAbsent(key, ignore -> describeNew(clazz, generics)); | ||
} | ||
|
||
/** | ||
* by cache | ||
*/ | ||
@NotNull | ||
public static ResolvableType resolve(@NotNull Class<?> clazz, Class<?>... generics) { | ||
ArrayKey key = generics == null || generics.length == 0 | ||
? new ArrayKey(clazz) | ||
: new ArrayKey(clazz, generics); | ||
return CacheResolvable.computeIfAbsent(key, ignore -> resolveNew(clazz, generics)); | ||
} | ||
|
||
/** | ||
* by cache | ||
*/ | ||
@NotNull | ||
public static Type type(@NotNull Class<?> clazz, Class<?>... generics) { | ||
return resolve(clazz, generics).getType(); | ||
} | ||
|
||
/** | ||
* no cache | ||
*/ | ||
@NotNull | ||
public static TypeDescriptor describeNew(@NotNull Class<?> clazz, Class<?>... generics) { | ||
return new TypeDescriptor(resolveNew(clazz, generics), null, null); | ||
} | ||
|
||
/** | ||
* no cache | ||
*/ | ||
@NotNull | ||
public static ResolvableType resolveNew(@NotNull Class<?> clazz, Class<?>... generics) { | ||
if (generics == null || generics.length == 0) return ResolvableType.forClass(clazz); | ||
|
||
final int rootCnt = clazz.getTypeParameters().length; | ||
final ResolvableType[] rootArg = new ResolvableType[rootCnt]; | ||
|
||
int nextIdx = 0; | ||
for (int ri = 0; ri < rootCnt; ri++) { | ||
Class<?> rt = generics[nextIdx++]; | ||
int rc = rt.getTypeParameters().length; | ||
nextIdx = resolve(rt, rootArg, ri, rc, generics, nextIdx); | ||
} | ||
|
||
return ResolvableType.forClassWithGenerics(clazz, rootArg); | ||
} | ||
|
||
private static int resolve(Class<?> rootClz, ResolvableType[] rootArg, int rootIdx, int paraCnt, Class<?>[] nextClz, int nextIdx) { | ||
if (paraCnt <= 0) { | ||
rootArg[rootIdx] = ResolvableType.forClass(rootClz); | ||
} | ||
else { | ||
final ResolvableType[] args = new ResolvableType[paraCnt]; | ||
for (int i = 0; i < paraCnt; i++) { | ||
Class<?> root = nextClz[nextIdx++]; | ||
int c1 = root.getTypeParameters().length; | ||
nextIdx = resolve(root, args, i, c1, nextClz, nextIdx); | ||
} | ||
rootArg[rootIdx] = ResolvableType.forClassWithGenerics(rootClz, args); | ||
} | ||
return nextIdx; | ||
} | ||
} |
82 changes: 82 additions & 0 deletions
82
wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/TypeSugarMain.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package pro.fessional.wings.silencer.enhance; | ||
|
||
import org.openjdk.jmh.annotations.Benchmark; | ||
import org.openjdk.jmh.annotations.BenchmarkMode; | ||
import org.openjdk.jmh.annotations.Fork; | ||
import org.openjdk.jmh.annotations.Measurement; | ||
import org.openjdk.jmh.annotations.Mode; | ||
import org.openjdk.jmh.annotations.OutputTimeUnit; | ||
import org.openjdk.jmh.annotations.Scope; | ||
import org.openjdk.jmh.annotations.State; | ||
import org.openjdk.jmh.annotations.Warmup; | ||
import org.springframework.core.ResolvableType; | ||
import org.springframework.core.convert.TypeDescriptor; | ||
|
||
import java.io.IOException; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
/** | ||
* <pre> | ||
* Benchmark Mode Cnt Score Error Units | ||
* TypeSugarMain.describeCache thrpt 4 20406.578 ± 4018.606 ops/ms | ||
* TypeSugarMain.describeNew thrpt 4 1240.069 ± 346.185 ops/ms | ||
* TypeSugarMain.describeRaw thrpt 4 1365.260 ± 312.185 ops/ms | ||
* TypeSugarMain.resolveCache thrpt 4 21342.189 ± 5840.158 ops/ms | ||
* TypeSugarMain.resolveNew thrpt 4 1280.143 ± 214.265 ops/ms | ||
* TypeSugarMain.resolveRaw thrpt 4 2061.249 ± 670.345 ops/ms | ||
* </pre> | ||
* | ||
* @author trydofor | ||
* @since 2024-06-09 | ||
*/ | ||
@Fork(2) | ||
@Warmup(iterations = 1) | ||
@Measurement(iterations = 2) | ||
@BenchmarkMode(Mode.Throughput) | ||
@OutputTimeUnit(TimeUnit.MILLISECONDS) | ||
@State(Scope.Benchmark) | ||
public class TypeSugarMain { | ||
|
||
@Benchmark | ||
public void resolveRaw() { | ||
ResolvableType.forClassWithGenerics(Map.class, | ||
ResolvableType.forClassWithGenerics(List.class, Long[].class), | ||
ResolvableType.forClass(String.class) | ||
); | ||
} | ||
|
||
@Benchmark | ||
public void resolveNew() { | ||
TypeSugar.resolveNew(Map.class, List.class, List.class, Long[].class, String.class); | ||
} | ||
|
||
@Benchmark | ||
public void resolveCache() { | ||
TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); | ||
} | ||
|
||
@Benchmark | ||
public void describeRaw() { | ||
TypeDescriptor.map(Map.class, | ||
TypeDescriptor.collection(List.class, TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class))), | ||
TypeDescriptor.valueOf(String.class) | ||
); | ||
} | ||
|
||
@Benchmark | ||
public void describeNew() { | ||
TypeSugar.describeNew(Map.class, List.class, List.class, Long[].class, String.class); | ||
} | ||
|
||
@Benchmark | ||
public void describeCache() { | ||
TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); | ||
} | ||
|
||
|
||
public static void main(String[] args) throws IOException { | ||
org.openjdk.jmh.Main.main(args); | ||
} | ||
} |
81 changes: 81 additions & 0 deletions
81
wings/silencer/src/test/java/pro/fessional/wings/silencer/enhance/TypeSugarTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,81 @@ | ||
package pro.fessional.wings.silencer.enhance; | ||
|
||
import io.qameta.allure.TmsLink; | ||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.Test; | ||
import org.springframework.core.ResolvableType; | ||
import org.springframework.core.convert.TypeDescriptor; | ||
|
||
import java.util.List; | ||
import java.util.Map; | ||
|
||
/** | ||
* @author trydofor | ||
* @since 2024-06-09 | ||
*/ | ||
public class TypeSugarTest { | ||
|
||
@Test | ||
@TmsLink("C11035") | ||
void test() { | ||
|
||
// Map<String, List<Long[]> | ||
var a0 = ResolvableType.forClassWithGenerics(Map.class, | ||
ResolvableType.forClass(String.class), | ||
ResolvableType.forClassWithGenerics(List.class, Long[].class) | ||
); | ||
var a1 = TypeSugar.resolve(Map.class, String.class, List.class, Long[].class); | ||
|
||
var a2 = TypeDescriptor.map(Map.class, | ||
TypeDescriptor.valueOf(String.class), | ||
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class)) | ||
); | ||
var a3 = TypeSugar.describe(Map.class, String.class, List.class, Long[].class); | ||
|
||
Assertions.assertEquals(a0, a1); | ||
Assertions.assertEquals(a2, a3); | ||
|
||
// Map<List<Long[]>,String> | ||
var b0 = ResolvableType.forClassWithGenerics(Map.class, | ||
ResolvableType.forClassWithGenerics(List.class, Long[].class), | ||
ResolvableType.forClass(String.class) | ||
); | ||
var b1 = TypeSugar.resolve(Map.class, List.class, Long[].class, String.class); | ||
|
||
var b2 = TypeDescriptor.map(Map.class, | ||
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class)), | ||
TypeDescriptor.valueOf(String.class) | ||
); | ||
var b3 = TypeSugar.describe(Map.class, List.class, Long[].class, String.class); | ||
|
||
Assertions.assertEquals(b0, b1); | ||
Assertions.assertEquals(b2, b3); | ||
|
||
// Map<List<List<Long[]>>,String> | ||
var c0 = ResolvableType.forClassWithGenerics(Map.class, | ||
ResolvableType.forClassWithGenerics(List.class, | ||
ResolvableType.forClassWithGenerics(List.class, Long[].class) | ||
), | ||
ResolvableType.forClass(String.class) | ||
); | ||
var c1 = TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); | ||
|
||
var c2 = TypeDescriptor.map(Map.class, | ||
TypeDescriptor.collection(List.class, | ||
TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(Long[].class)) | ||
), | ||
TypeDescriptor.valueOf(String.class) | ||
); | ||
var c3 = TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); | ||
|
||
Assertions.assertEquals(c0, c1); | ||
Assertions.assertEquals(c2, c3); | ||
|
||
var d0 = TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); | ||
var d1 = TypeSugar.resolve(Map.class, List.class, List.class, Long[].class, String.class); | ||
var d2 = TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); | ||
var d3 = TypeSugar.describe(Map.class, List.class, List.class, Long[].class, String.class); | ||
Assertions.assertSame(d0, d1); | ||
Assertions.assertSame(d2, d3); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.