├── README.md
├── src
└── main
│ └── java
│ └── com
│ └── github
│ └── carlosraphael
│ └── javabeanutil
│ ├── javabean
│ ├── JavaBean.java
│ └── NestedJavaBean.java
│ ├── JavaBeanUtilBenchmark.java
│ └── JavaBeanUtil.java
├── LICENSE
└── pom.xml
/README.md:
--------------------------------------------------------------------------------
1 | # javabeanutil-benchmark
2 | https://medium.com/free-code-camp/a-faster-alternative-to-java-reflection-db6b1e48c33e
3 |
--------------------------------------------------------------------------------
/src/main/java/com/github/carlosraphael/javabeanutil/javabean/JavaBean.java:
--------------------------------------------------------------------------------
1 | package com.github.carlosraphael.javabeanutil.javabean;
2 |
3 | import lombok.Builder;
4 | import lombok.Value;
5 |
6 | /**
7 | * @author carlos.raphael.lopes@gmail.com
8 | */
9 | @Value @Builder
10 | public class JavaBean {
11 |
12 | private final String fieldA;
13 | private final NestedJavaBean nestedJavaBean;
14 | }
15 |
--------------------------------------------------------------------------------
/src/main/java/com/github/carlosraphael/javabeanutil/javabean/NestedJavaBean.java:
--------------------------------------------------------------------------------
1 | package com.github.carlosraphael.javabeanutil.javabean;
2 |
3 | import lombok.Builder;
4 | import lombok.Value;
5 |
6 | /**
7 | * @author carlos.raphael.lopes@gmail.com
8 | */
9 | @Value @Builder
10 | public class NestedJavaBean {
11 |
12 | private final String fieldA;
13 | private final NestedJavaBean nestedJavaBean;
14 | }
15 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Carlos
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/main/java/com/github/carlosraphael/javabeanutil/JavaBeanUtilBenchmark.java:
--------------------------------------------------------------------------------
1 | package com.github.carlosraphael.javabeanutil;
2 |
3 | import com.github.carlosraphael.javabeanutil.javabean.JavaBean;
4 | import com.github.carlosraphael.javabeanutil.javabean.NestedJavaBean;
5 | import jodd.bean.BeanUtil;
6 | import org.apache.commons.beanutils.PropertyUtils;
7 | import org.openjdk.jmh.Main;
8 | import org.openjdk.jmh.annotations.*;
9 | import org.openjdk.jmh.runner.RunnerException;
10 |
11 | import java.io.IOException;
12 | import java.util.concurrent.TimeUnit;
13 |
14 | @Fork(3)
15 | @Warmup(iterations = 5, time = 3)
16 | @Measurement(iterations = 5, time = 1)
17 | @BenchmarkMode(Mode.AverageTime)
18 | @OutputTimeUnit(TimeUnit.NANOSECONDS)
19 | @State(Scope.Thread)
20 | public class JavaBeanUtilBenchmark {
21 |
22 | @Param({
23 | "fieldA",
24 | "nestedJavaBean.fieldA",
25 | "nestedJavaBean.nestedJavaBean.fieldA",
26 | "nestedJavaBean.nestedJavaBean.nestedJavaBean.fieldA"
27 | })
28 | String fieldName;
29 | JavaBean javaBean;
30 |
31 | @Setup
32 | public void setup() {
33 | NestedJavaBean nestedJavaBean3 = NestedJavaBean.builder().fieldA("nested-3").build();
34 | NestedJavaBean nestedJavaBean2 = NestedJavaBean.builder().fieldA("nested-2").nestedJavaBean(nestedJavaBean3).build();
35 | NestedJavaBean nestedJavaBean1 = NestedJavaBean.builder().fieldA("nested-1").nestedJavaBean(nestedJavaBean2).build();
36 | javaBean = JavaBean.builder().fieldA("fieldA").nestedJavaBean(nestedJavaBean1).build();
37 | }
38 |
39 | @Benchmark
40 | public Object invokeDynamic() {
41 | return JavaBeanUtil.getFieldValue(javaBean, fieldName);
42 | }
43 |
44 | /**
45 | * Reference: http://commons.apache.org/proper/commons-beanutils/
46 | */
47 | @Benchmark
48 | public Object apacheBeanUtils() throws Exception {
49 | return PropertyUtils.getNestedProperty(javaBean, fieldName);
50 | }
51 |
52 | /**
53 | * Reference: https://jodd.org/beanutil/
54 | */
55 | @Benchmark
56 | public Object joddBean() {
57 | return BeanUtil.declared.getProperty(javaBean, fieldName);
58 | }
59 |
60 | public static void main(String... args) throws IOException, RunnerException {
61 | Main.main(args);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
5 | 4.0.0
6 |
7 | com.github.carlosraphael.javabeanutil
8 | javabeanutil-benchmark
9 | 1.0-SNAPSHOT
10 |
11 | javabeanutil-benchmark
12 | https://github.com/carlosraphael/javabeanutil-benchmark
13 |
14 |
15 | UTF-8
16 | 8
17 |
18 |
19 |
20 |
21 | io.vavr
22 | vavr
23 | 0.10.0
24 |
25 |
26 | org.apache.commons
27 | commons-lang3
28 | RELEASE
29 |
30 |
31 | commons-beanutils
32 | commons-beanutils-core
33 | RELEASE
34 |
35 |
36 | org.jodd
37 | jodd-bean
38 | RELEASE
39 |
40 |
41 | org.openjdk.jmh
42 | jmh-core
43 | RELEASE
44 |
45 |
46 | org.openjdk.jmh
47 | jmh-generator-annprocess
48 | RELEASE
49 |
50 |
51 | org.projectlombok
52 | lombok
53 | true
54 | RELEASE
55 |
56 |
57 | junit
58 | junit
59 | RELEASE
60 | test
61 |
62 |
63 |
64 |
65 | javaBeanUtilsBenchmark
66 |
67 |
68 | org.apache.maven.plugins
69 | maven-compiler-plugin
70 | 3.8.0
71 |
72 | ${jdk.version}
73 | ${jdk.version}
74 |
75 |
76 |
77 | org.apache.maven.plugins
78 | maven-shade-plugin
79 | 3.2.1
80 |
81 |
82 | package
83 |
84 | shade
85 |
86 |
87 |
88 |
90 | com.github.carlosraphael.javabeanutil.JavaBeanUtilBenchmark
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
--------------------------------------------------------------------------------
/src/main/java/com/github/carlosraphael/javabeanutil/JavaBeanUtil.java:
--------------------------------------------------------------------------------
1 | package com.github.carlosraphael.javabeanutil;
2 |
3 | import io.vavr.Tuple;
4 | import io.vavr.Tuple2;
5 | import org.apache.commons.lang3.StringUtils;
6 |
7 | import java.lang.invoke.*;
8 | import java.lang.reflect.Method;
9 | import java.lang.reflect.Modifier;
10 | import java.util.ArrayList;
11 | import java.util.List;
12 | import java.util.Map;
13 | import java.util.concurrent.ConcurrentHashMap;
14 | import java.util.function.Function;
15 | import java.util.regex.Pattern;
16 | import java.util.stream.Stream;
17 |
18 | /**
19 | * This class is meant to demonstrate a quite fast alternative to Java Reflection when reading values from a given
20 | * Java Bean object.
21 | *
22 | * It is built on top of {@link LambdaMetafactory} and {@link MethodHandle} in order to create a {@link CallSite}
23 | * so that "invokedynamic" bytecode instruction is generated to get most out of the JVM . Also, for optimal performance
24 | * it caches the dynamically created getters into a {@link ClassValue}.
25 | *
26 | * Benchmark shows this technique can by far outperform the well-known Apache BeanUtils library and one of its alternatives
27 | * called Jodd BeanUtil.
28 | *
29 | * NOTE: this class does not support array and collection/map fields.
30 | *
31 | * @author carlos.raphael.lopes@gmail.com
32 | */
33 | @SuppressWarnings("unchecked")
34 | public class JavaBeanUtil {
35 |
36 | private static final Pattern FIELD_SEPARATOR = Pattern.compile("\\.");
37 | private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
38 | private static final ClassValue