23
23
import java .util .HashMap ;
24
24
import java .util .List ;
25
25
import java .util .Map ;
26
+ import java .util .Set ;
26
27
import java .util .WeakHashMap ;
27
28
28
29
import org .aopalliance .aop .Advice ;
56
57
/**
57
58
* CGLIB-based {@link AopProxy} implementation for the Spring AOP framework.
58
59
*
59
- * <p>Formerly named {@code Cglib2AopProxy}, as of Spring 3.2, this class depends on
60
- * Spring's own internally repackaged version of CGLIB 3.</i>.
61
- *
62
60
* <p>Objects of this type should be obtained through proxy factories,
63
61
* configured by an {@link AdvisedSupport} object. This class is internal
64
62
* to Spring's AOP framework and need not be used directly by client code.
@@ -235,10 +233,11 @@ protected Enhancer createEnhancer() {
235
233
* validates it if not.
236
234
*/
237
235
private void validateClassIfNecessary (Class <?> proxySuperClass , ClassLoader proxyClassLoader ) {
238
- if (logger .isInfoEnabled ()) {
236
+ if (logger .isWarnEnabled ()) {
239
237
synchronized (validatedClasses ) {
240
238
if (!validatedClasses .containsKey (proxySuperClass )) {
241
- doValidateClass (proxySuperClass , proxyClassLoader );
239
+ doValidateClass (proxySuperClass , proxyClassLoader ,
240
+ ClassUtils .getAllInterfacesForClassAsSet (proxySuperClass ));
242
241
validatedClasses .put (proxySuperClass , Boolean .TRUE );
243
242
}
244
243
}
@@ -249,30 +248,35 @@ private void validateClassIfNecessary(Class<?> proxySuperClass, ClassLoader prox
249
248
* Checks for final methods on the given {@code Class}, as well as package-visible
250
249
* methods across ClassLoaders, and writes warnings to the log for each one found.
251
250
*/
252
- private void doValidateClass (Class <?> proxySuperClass , ClassLoader proxyClassLoader ) {
251
+ private void doValidateClass (Class <?> proxySuperClass , ClassLoader proxyClassLoader , Set < Class <?>> ifcs ) {
253
252
if (proxySuperClass != Object .class ) {
254
253
Method [] methods = proxySuperClass .getDeclaredMethods ();
255
254
for (Method method : methods ) {
256
255
int mod = method .getModifiers ();
257
256
if (!Modifier .isStatic (mod )) {
258
257
if (Modifier .isFinal (mod )) {
259
- logger .info ("Unable to proxy method [" + method + "] because it is final: " +
260
- "All calls to this method via a proxy will NOT be routed to the target instance." );
258
+ if (implementsInterface (method , ifcs )) {
259
+ logger .warn ("Unable to proxy interface-implmenting method [" + method + "] because " +
260
+ "it is marked as final: Consider using interface-based proxies instead!" );
261
+ }
262
+ logger .info ("Final method [" + method + "] cannot get proxied via CGLIB: " +
263
+ "Calls to this method will NOT be routed to the target instance and " +
264
+ "might lead to NPEs against uninitialized fields in the proxy instance." );
261
265
}
262
266
else if (!Modifier .isPublic (mod ) && !Modifier .isProtected (mod ) && !Modifier .isPrivate (mod ) &&
263
267
proxyClassLoader != null && proxySuperClass .getClassLoader () != proxyClassLoader ) {
264
- logger .info ("Unable to proxy method [" + method + "] because it is package-visible " +
265
- "across different ClassLoaders: All calls to this method via a proxy will " +
266
- "NOT be routed to the target instance ." );
268
+ logger .info ("Method [" + method + "] is package-visible across different ClassLoaders " +
269
+ "and cannot get proxied via CGLIB: Declare this method as public or protected " +
270
+ "if you need to support invocations through the proxy ." );
267
271
}
268
272
}
269
273
}
270
- doValidateClass (proxySuperClass .getSuperclass (), proxyClassLoader );
274
+ doValidateClass (proxySuperClass .getSuperclass (), proxyClassLoader , ifcs );
271
275
}
272
276
}
273
277
274
278
private Callback [] getCallbacks (Class <?> rootClass ) throws Exception {
275
- // Parameters used for optimisation choices...
279
+ // Parameters used for optimization choices...
276
280
boolean exposeProxy = this .advised .isExposeProxy ();
277
281
boolean isFrozen = this .advised .isFrozen ();
278
282
boolean isStatic = this .advised .getTargetSource ().isStatic ();
@@ -311,14 +315,14 @@ private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
311
315
Callback [] callbacks ;
312
316
313
317
// If the target is a static one and the advice chain is frozen,
314
- // then we can make some optimisations by sending the AOP calls
318
+ // then we can make some optimizations by sending the AOP calls
315
319
// direct to the target using the fixed chain for that method.
316
320
if (isStatic && isFrozen ) {
317
321
Method [] methods = rootClass .getMethods ();
318
322
Callback [] fixedCallbacks = new Callback [methods .length ];
319
323
this .fixedInterceptorMap = new HashMap <>(methods .length );
320
324
321
- // TODO: small memory optimisation here (can skip creation for methods with no advice)
325
+ // TODO: small memory optimization here (can skip creation for methods with no advice)
322
326
for (int x = 0 ; x < methods .length ; x ++) {
323
327
List <Object > chain = this .advised .getInterceptorsAndDynamicInterceptionAdvice (methods [x ], rootClass );
324
328
fixedCallbacks [x ] = new FixedChainStaticTargetInterceptor (
@@ -339,6 +343,31 @@ private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
339
343
return callbacks ;
340
344
}
341
345
346
+
347
+ @ Override
348
+ public boolean equals (Object other ) {
349
+ return (this == other || (other instanceof CglibAopProxy &&
350
+ AopProxyUtils .equalsInProxy (this .advised , ((CglibAopProxy ) other ).advised )));
351
+ }
352
+
353
+ @ Override
354
+ public int hashCode () {
355
+ return CglibAopProxy .class .hashCode () * 13 + this .advised .getTargetSource ().hashCode ();
356
+ }
357
+
358
+
359
+ /**
360
+ * Check whether the given method is declared on any of the given interfaces.
361
+ */
362
+ private static boolean implementsInterface (Method method , Set <Class <?>> ifcs ) {
363
+ for (Class <?> ifc : ifcs ) {
364
+ if (ClassUtils .hasMethod (ifc , method .getName (), method .getParameterTypes ())) {
365
+ return true ;
366
+ }
367
+ }
368
+ return false ;
369
+ }
370
+
342
371
/**
343
372
* Process a return value. Wraps a return of {@code this} if necessary to be the
344
373
* {@code proxy} and also verifies that {@code null} is not returned as a primitive.
@@ -360,18 +389,6 @@ private static Object processReturnType(Object proxy, Object target, Method meth
360
389
}
361
390
362
391
363
- @ Override
364
- public boolean equals (Object other ) {
365
- return (this == other || (other instanceof CglibAopProxy &&
366
- AopProxyUtils .equalsInProxy (this .advised , ((CglibAopProxy ) other ).advised )));
367
- }
368
-
369
- @ Override
370
- public int hashCode () {
371
- return CglibAopProxy .class .hashCode () * 13 + this .advised .getTargetSource ().hashCode ();
372
- }
373
-
374
-
375
392
/**
376
393
* Serializable replacement for CGLIB's NoOp interface.
377
394
* Public to allow use elsewhere in the framework.
@@ -817,51 +834,42 @@ public int accept(Method method) {
817
834
// Else use the AOP_PROXY.
818
835
if (isStatic && isFrozen && this .fixedInterceptorMap .containsKey (key )) {
819
836
if (logger .isDebugEnabled ()) {
820
- logger .debug ("Method has advice and optimisations are enabled: " + method );
837
+ logger .debug ("Method has advice and optimizations are enabled: " + method );
821
838
}
822
- // We know that we are optimising so we can use the FixedStaticChainInterceptors.
839
+ // We know that we are optimizing so we can use the FixedStaticChainInterceptors.
823
840
int index = this .fixedInterceptorMap .get (key );
824
841
return (index + this .fixedInterceptorOffset );
825
842
}
826
843
else {
827
844
if (logger .isDebugEnabled ()) {
828
- logger .debug ("Unable to apply any optimisations to advised method: " + method );
845
+ logger .debug ("Unable to apply any optimizations to advised method: " + method );
829
846
}
830
847
return AOP_PROXY ;
831
848
}
832
849
}
833
850
else {
834
- // See if the return type of the method is outside the class hierarchy
835
- // of the target type. If so we know it never needs to have return type
836
- // massage and can use a dispatcher.
837
- // If the proxy is being exposed, then must use the interceptor the
838
- // correct one is already configured. If the target is not static, then
839
- // cannot use a dispatcher because the target cannot be released.
851
+ // See if the return type of the method is outside the class hierarchy of the target type.
852
+ // If so we know it never needs to have return type massage and can use a dispatcher.
853
+ // If the proxy is being exposed, then must use the interceptor the correct one is already
854
+ // configured. If the target is not static, then we cannot use a dispatcher because the
855
+ // target needs to be explicitly released after the invocation.
840
856
if (exposeProxy || !isStatic ) {
841
857
return INVOKE_TARGET ;
842
858
}
843
859
Class <?> returnType = method .getReturnType ();
844
- if (targetClass == returnType ) {
860
+ if (returnType . isAssignableFrom ( targetClass ) ) {
845
861
if (logger .isDebugEnabled ()) {
846
- logger .debug ("Method " + method +
847
- "has return type same as target type ( may return this) - using INVOKE_TARGET" );
862
+ logger .debug ("Method return type is assignable from target type and " +
863
+ "may therefore return ' this' - using INVOKE_TARGET: " + method );
848
864
}
849
865
return INVOKE_TARGET ;
850
866
}
851
- else if (returnType .isPrimitive () || !returnType .isAssignableFrom (targetClass )) {
852
- if (logger .isDebugEnabled ()) {
853
- logger .debug ("Method " + method +
854
- " has return type that ensures this cannot be returned- using DISPATCH_TARGET" );
855
- }
856
- return DISPATCH_TARGET ;
857
- }
858
867
else {
859
868
if (logger .isDebugEnabled ()) {
860
- logger .debug ("Method " + method +
861
- "has return type that is assignable from the target type (may return this) - " +
862
- "using INVOKE_TARGET" );
869
+ logger .debug ("Method return type ensures 'this' cannot be returned - " +
870
+ "using DISPATCH_TARGET: " + method );
863
871
}
864
- return INVOKE_TARGET ;
872
+ return DISPATCH_TARGET ;
865
873
}
866
874
}
867
875
}
0 commit comments