44import net .bytebuddy .dynamic .loading .ClassLoadingStrategy ;
55
66import java .lang .reflect .Method ;
7- import java .util .Collections ;
8- import java .util .LinkedList ;
97import java .util .List ;
8+ import java .util .Map ;
9+ import java .util .concurrent .ConcurrentHashMap ;
1010
1111import static com .remondis .resample .ReflectionUtil .defaultValue ;
1212import static com .remondis .resample .ReflectionUtil .invokeMethodProxySafe ;
1313import static com .remondis .resample .ReflectionUtil .toPropertyName ;
14+ import static java .lang .ClassLoader .getSystemClassLoader ;
15+ import static java .util .Objects .isNull ;
1416import static net .bytebuddy .implementation .InvocationHandlerAdapter .of ;
1517import static net .bytebuddy .matcher .ElementMatchers .isDeclaredBy ;
1618import static net .bytebuddy .matcher .ElementMatchers .isGetter ;
2426 */
2527class InvocationSensor <T > {
2628
27- private T proxyObject ;
29+ static Map < Class <?>, InterceptionHandler <?>> interceptionHandlerCache = new ConcurrentHashMap <>() ;
2830
29- private List < String > propertyNames = new LinkedList <>() ;
31+ private InterceptionHandler < T > interceptionHandler ;
3032
3133 InvocationSensor (Class <T > superType ) {
32- Class <? extends T > proxyClass = new ByteBuddy ().subclass (superType )
33- .method (isGetter ())
34- .intercept (of ((proxy , method , args ) -> markPropertyAsCalled (method )))
35- .method (isDeclaredBy (Object .class ))
36- .intercept (of ((proxy , method , args ) -> invokeMethodProxySafe (method , this , args )))
37- .method (not (isGetter ()).and (not (isDeclaredBy (Object .class ))))
38- .intercept (of ((proxy , method , args ) -> {
39- throw ReflectionException .notAGetter (method );
40- }))
41- .make ()
42- .load (getClass ().getClassLoader (), ClassLoadingStrategy .Default .INJECTION )
43- .getLoaded ();
44-
45- proxyObject = superType .cast (ReflectionUtil .newInstance (proxyClass ));
34+ ClassLoader classLoader ;
35+ if (isNull (superType ) || isNull (superType .getClassLoader ())) {
36+ classLoader = getSystemClassLoader ();
37+ } else {
38+ classLoader = superType .getClassLoader ();
39+ }
40+ if (interceptionHandlerCache .containsKey (superType )) {
41+ this .interceptionHandler = (InterceptionHandler <T >) interceptionHandlerCache .get (superType );
42+ } else {
43+ Class <? extends T > proxyClass = new ByteBuddy ().subclass (superType )
44+ .method (isGetter ())
45+ .intercept (of ((proxy , method , args ) -> markPropertyAsCalled (method )))
46+ .method (isDeclaredBy (Object .class ))
47+ .intercept (of ((proxy , method , args ) -> invokeMethodProxySafe (method , this , args )))
48+ .method (not (isGetter ()).and (not (isDeclaredBy (Object .class ))))
49+ .intercept (of ((proxy , method , args ) -> {
50+ throw ReflectionException .notAGetter (method );
51+ }))
52+ .make ()
53+ .load (classLoader , ClassLoadingStrategy .Default .INJECTION )
54+ .getLoaded ();
55+ this .interceptionHandler = new InterceptionHandler <>();
56+ this .interceptionHandler .setProxyObject (superType .cast (ReflectionUtil .newInstance (proxyClass )));
57+ interceptionHandlerCache .put (superType , this .interceptionHandler );
58+ }
4659 }
4760
4861 private Object markPropertyAsCalled (Method method ) {
4962 String propertyName = toPropertyName (method );
50- propertyNames .add (propertyName );
63+ interceptionHandler .getThreadLocalPropertyNames ()
64+ .add (propertyName );
5165 return nullOrDefaultValue (method .getReturnType ());
5266 }
5367
@@ -57,7 +71,7 @@ private Object markPropertyAsCalled(Method method) {
5771 * @return The proxy.
5872 */
5973 T getSensor () {
60- return proxyObject ;
74+ return interceptionHandler . getProxyObject () ;
6175 }
6276
6377 /**
@@ -66,24 +80,21 @@ T getSensor() {
6680 * @return Returns the tracked property names.
6781 */
6882 List <String > getTrackedPropertyNames () {
69- return Collections . unmodifiableList ( propertyNames );
83+ return interceptionHandler . getTrackedPropertyNames ( );
7084 }
7185
7286 /**
7387 * Checks if there were any properties accessed by get calls.
7488 *
75- * @return Returns <code>true</code> if there were at least one interaction with
76- * a property. Otherwise <code>false</code> is returned.
89+ * @return Returns <code>true</code> if there were at least one interaction with a property. Otherwise
90+ * <code>false</code> is returned.
7791 */
7892 boolean hasTrackedProperties () {
79- return ! propertyNames . isEmpty ();
93+ return interceptionHandler . hasTrackedProperties ();
8094 }
8195
82- /**
83- * Resets all tracked information.
84- */
8596 void reset () {
86- propertyNames . clear ();
97+ interceptionHandler . reset ();
8798 }
8899
89100 private static Object nullOrDefaultValue (Class <?> returnType ) {
0 commit comments