16
16
17
17
package org .springframework .aop .framework ;
18
18
19
+ import java .io .IOException ;
20
+ import java .io .ObjectInputStream ;
19
21
import java .io .Serializable ;
20
22
import java .lang .reflect .InvocationHandler ;
21
23
import java .lang .reflect .Method ;
@@ -71,34 +73,16 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
71
73
private static final long serialVersionUID = 5531744639992436476L ;
72
74
73
75
74
- /*
75
- * NOTE: We could avoid the code duplication between this class and the CGLIB
76
- * proxies by refactoring "invoke" into a template method. However, this approach
77
- * adds at least 10% performance overhead versus a copy-paste solution, so we sacrifice
78
- * elegance for performance (we have a good test suite to ensure that the different
79
- * proxies behave the same :-)).
80
- * This way, we can also more easily take advantage of minor optimizations in each class.
81
- */
76
+ private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow" ;
82
77
83
78
/** We use a static Log to avoid serialization issues. */
84
79
private static final Log logger = LogFactory .getLog (JdkDynamicAopProxy .class );
85
80
86
- private static final String COROUTINES_FLOW_CLASS_NAME = "kotlinx.coroutines.flow.Flow" ;
87
-
88
81
/** Config used to configure this proxy. */
89
82
private final AdvisedSupport advised ;
90
83
91
- private final Class <?>[] proxiedInterfaces ;
92
-
93
- /**
94
- * Is the {@link #equals} method defined on the proxied interfaces?
95
- */
96
- private boolean equalsDefined ;
97
-
98
- /**
99
- * Is the {@link #hashCode} method defined on the proxied interfaces?
100
- */
101
- private boolean hashCodeDefined ;
84
+ /** Cached in {@link AdvisedSupport#proxyMetadataCache}. */
85
+ private transient ProxiedInterfacesCache cache ;
102
86
103
87
104
88
/**
@@ -110,8 +94,17 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
110
94
public JdkDynamicAopProxy (AdvisedSupport config ) throws AopConfigException {
111
95
Assert .notNull (config , "AdvisedSupport must not be null" );
112
96
this .advised = config ;
113
- this .proxiedInterfaces = AopProxyUtils .completeProxiedInterfaces (this .advised , true );
114
- findDefinedEqualsAndHashCodeMethods (this .proxiedInterfaces );
97
+
98
+ // Initialize ProxiedInterfacesCache if not cached already
99
+ ProxiedInterfacesCache cache ;
100
+ if (config .proxyMetadataCache instanceof ProxiedInterfacesCache proxiedInterfacesCache ) {
101
+ cache = proxiedInterfacesCache ;
102
+ }
103
+ else {
104
+ cache = new ProxiedInterfacesCache (config );
105
+ config .proxyMetadataCache = cache ;
106
+ }
107
+ this .cache = cache ;
115
108
}
116
109
117
110
@@ -125,13 +118,13 @@ public Object getProxy(@Nullable ClassLoader classLoader) {
125
118
if (logger .isTraceEnabled ()) {
126
119
logger .trace ("Creating JDK dynamic proxy: " + this .advised .getTargetSource ());
127
120
}
128
- return Proxy .newProxyInstance (determineClassLoader (classLoader ), this .proxiedInterfaces , this );
121
+ return Proxy .newProxyInstance (determineClassLoader (classLoader ), this .cache . proxiedInterfaces , this );
129
122
}
130
123
131
124
@ SuppressWarnings ("deprecation" )
132
125
@ Override
133
126
public Class <?> getProxyClass (@ Nullable ClassLoader classLoader ) {
134
- return Proxy .getProxyClass (determineClassLoader (classLoader ), this .proxiedInterfaces );
127
+ return Proxy .getProxyClass (determineClassLoader (classLoader ), this .cache . proxiedInterfaces );
135
128
}
136
129
137
130
/**
@@ -160,28 +153,6 @@ private ClassLoader determineClassLoader(@Nullable ClassLoader classLoader) {
160
153
return classLoader ;
161
154
}
162
155
163
- /**
164
- * Finds any {@link #equals} or {@link #hashCode} method that may be defined
165
- * on the supplied set of interfaces.
166
- * @param proxiedInterfaces the interfaces to introspect
167
- */
168
- private void findDefinedEqualsAndHashCodeMethods (Class <?>[] proxiedInterfaces ) {
169
- for (Class <?> proxiedInterface : proxiedInterfaces ) {
170
- Method [] methods = proxiedInterface .getDeclaredMethods ();
171
- for (Method method : methods ) {
172
- if (AopUtils .isEqualsMethod (method )) {
173
- this .equalsDefined = true ;
174
- }
175
- if (AopUtils .isHashCodeMethod (method )) {
176
- this .hashCodeDefined = true ;
177
- }
178
- if (this .equalsDefined && this .hashCodeDefined ) {
179
- return ;
180
- }
181
- }
182
- }
183
- }
184
-
185
156
186
157
/**
187
158
* Implementation of {@code InvocationHandler.invoke}.
@@ -198,11 +169,11 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
198
169
Object target = null ;
199
170
200
171
try {
201
- if (!this .equalsDefined && AopUtils .isEqualsMethod (method )) {
172
+ if (!this .cache . equalsDefined && AopUtils .isEqualsMethod (method )) {
202
173
// The target does not implement the equals(Object) method itself.
203
174
return equals (args [0 ]);
204
175
}
205
- else if (!this .hashCodeDefined && AopUtils .isHashCodeMethod (method )) {
176
+ else if (!this .cache . hashCodeDefined && AopUtils .isHashCodeMethod (method )) {
206
177
// The target does not implement the hashCode() method itself.
207
178
return hashCode ();
208
179
}
@@ -324,4 +295,53 @@ public int hashCode() {
324
295
return JdkDynamicAopProxy .class .hashCode () * 13 + this .advised .getTargetSource ().hashCode ();
325
296
}
326
297
298
+
299
+ //---------------------------------------------------------------------
300
+ // Serialization support
301
+ //---------------------------------------------------------------------
302
+
303
+ private void readObject (ObjectInputStream ois ) throws IOException , ClassNotFoundException {
304
+ // Rely on default serialization; just initialize state after deserialization.
305
+ ois .defaultReadObject ();
306
+
307
+ // Initialize transient fields.
308
+ this .cache = new ProxiedInterfacesCache (this .advised );
309
+ }
310
+
311
+
312
+ /**
313
+ * Holder for the complete proxied interfaces and derived metadata,
314
+ * to be cached in {@link AdvisedSupport#proxyMetadataCache}.
315
+ * @since 6.1.3
316
+ */
317
+ static final class ProxiedInterfacesCache {
318
+
319
+ Class <?>[] proxiedInterfaces ;
320
+
321
+ boolean equalsDefined ;
322
+
323
+ boolean hashCodeDefined ;
324
+
325
+ ProxiedInterfacesCache (AdvisedSupport config ) {
326
+ this .proxiedInterfaces = AopProxyUtils .completeProxiedInterfaces (config , true );
327
+
328
+ // Find any {@link #equals} or {@link #hashCode} method that may be defined
329
+ //on the supplied set of interfaces.
330
+ for (Class <?> proxiedInterface : this .proxiedInterfaces ) {
331
+ Method [] methods = proxiedInterface .getDeclaredMethods ();
332
+ for (Method method : methods ) {
333
+ if (AopUtils .isEqualsMethod (method )) {
334
+ this .equalsDefined = true ;
335
+ }
336
+ if (AopUtils .isHashCodeMethod (method )) {
337
+ this .hashCodeDefined = true ;
338
+ }
339
+ if (this .equalsDefined && this .hashCodeDefined ) {
340
+ return ;
341
+ }
342
+ }
343
+ }
344
+ }
345
+ }
346
+
327
347
}
0 commit comments