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.
@@ -241,10 +239,11 @@ protected Enhancer createEnhancer() {
241
239
* validates it if not.
242
240
*/
243
241
private void validateClassIfNecessary (Class <?> proxySuperClass , ClassLoader proxyClassLoader ) {
244
- if (logger .isInfoEnabled ()) {
242
+ if (logger .isWarnEnabled ()) {
245
243
synchronized (validatedClasses ) {
246
244
if (!validatedClasses .containsKey (proxySuperClass )) {
247
- doValidateClass (proxySuperClass , proxyClassLoader );
245
+ doValidateClass (proxySuperClass , proxyClassLoader ,
246
+ ClassUtils .getAllInterfacesForClassAsSet (proxySuperClass ));
248
247
validatedClasses .put (proxySuperClass , Boolean .TRUE );
249
248
}
250
249
}
@@ -255,30 +254,35 @@ private void validateClassIfNecessary(Class<?> proxySuperClass, ClassLoader prox
255
254
* Checks for final methods on the given {@code Class}, as well as package-visible
256
255
* methods across ClassLoaders, and writes warnings to the log for each one found.
257
256
*/
258
- private void doValidateClass (Class <?> proxySuperClass , ClassLoader proxyClassLoader ) {
257
+ private void doValidateClass (Class <?> proxySuperClass , ClassLoader proxyClassLoader , Set < Class <?>> ifcs ) {
259
258
if (proxySuperClass != Object .class ) {
260
259
Method [] methods = proxySuperClass .getDeclaredMethods ();
261
260
for (Method method : methods ) {
262
261
int mod = method .getModifiers ();
263
262
if (!Modifier .isStatic (mod )) {
264
263
if (Modifier .isFinal (mod )) {
265
- logger .info ("Unable to proxy method [" + method + "] because it is final: " +
266
- "All calls to this method via a proxy will NOT be routed to the target instance." );
264
+ if (implementsInterface (method , ifcs )) {
265
+ logger .warn ("Unable to proxy interface-implmenting method [" + method + "] because " +
266
+ "it is marked as final: Consider using interface-based proxies instead!" );
267
+ }
268
+ logger .info ("Final method [" + method + "] cannot get proxied via CGLIB: " +
269
+ "Calls to this method will NOT be routed to the target instance and " +
270
+ "might lead to NPEs against uninitialized fields in the proxy instance." );
267
271
}
268
272
else if (!Modifier .isPublic (mod ) && !Modifier .isProtected (mod ) && !Modifier .isPrivate (mod ) &&
269
273
proxyClassLoader != null && proxySuperClass .getClassLoader () != proxyClassLoader ) {
270
- logger .info ("Unable to proxy method [" + method + "] because it is package-visible " +
271
- "across different ClassLoaders: All calls to this method via a proxy will " +
272
- "NOT be routed to the target instance ." );
274
+ logger .info ("Method [" + method + "] is package-visible across different ClassLoaders " +
275
+ "and cannot get proxied via CGLIB: Declare this method as public or protected " +
276
+ "if you need to support invocations through the proxy ." );
273
277
}
274
278
}
275
279
}
276
- doValidateClass (proxySuperClass .getSuperclass (), proxyClassLoader );
280
+ doValidateClass (proxySuperClass .getSuperclass (), proxyClassLoader , ifcs );
277
281
}
278
282
}
279
283
280
284
private Callback [] getCallbacks (Class <?> rootClass ) throws Exception {
281
- // Parameters used for optimisation choices...
285
+ // Parameters used for optimization choices...
282
286
boolean exposeProxy = this .advised .isExposeProxy ();
283
287
boolean isFrozen = this .advised .isFrozen ();
284
288
boolean isStatic = this .advised .getTargetSource ().isStatic ();
@@ -317,14 +321,14 @@ private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
317
321
Callback [] callbacks ;
318
322
319
323
// If the target is a static one and the advice chain is frozen,
320
- // then we can make some optimisations by sending the AOP calls
324
+ // then we can make some optimizations by sending the AOP calls
321
325
// direct to the target using the fixed chain for that method.
322
326
if (isStatic && isFrozen ) {
323
327
Method [] methods = rootClass .getMethods ();
324
328
Callback [] fixedCallbacks = new Callback [methods .length ];
325
329
this .fixedInterceptorMap = new HashMap <String , Integer >(methods .length );
326
330
327
- // TODO: small memory optimisation here (can skip creation for methods with no advice)
331
+ // TODO: small memory optimization here (can skip creation for methods with no advice)
328
332
for (int x = 0 ; x < methods .length ; x ++) {
329
333
List <Object > chain = this .advised .getInterceptorsAndDynamicInterceptionAdvice (methods [x ], rootClass );
330
334
fixedCallbacks [x ] = new FixedChainStaticTargetInterceptor (
@@ -345,6 +349,31 @@ private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
345
349
return callbacks ;
346
350
}
347
351
352
+
353
+ @ Override
354
+ public boolean equals (Object other ) {
355
+ return (this == other || (other instanceof CglibAopProxy &&
356
+ AopProxyUtils .equalsInProxy (this .advised , ((CglibAopProxy ) other ).advised )));
357
+ }
358
+
359
+ @ Override
360
+ public int hashCode () {
361
+ return CglibAopProxy .class .hashCode () * 13 + this .advised .getTargetSource ().hashCode ();
362
+ }
363
+
364
+
365
+ /**
366
+ * Check whether the given method is declared on any of the given interfaces.
367
+ */
368
+ private static boolean implementsInterface (Method method , Set <Class <?>> ifcs ) {
369
+ for (Class <?> ifc : ifcs ) {
370
+ if (ClassUtils .hasMethod (ifc , method .getName (), method .getParameterTypes ())) {
371
+ return true ;
372
+ }
373
+ }
374
+ return false ;
375
+ }
376
+
348
377
/**
349
378
* Process a return value. Wraps a return of {@code this} if necessary to be the
350
379
* {@code proxy} and also verifies that {@code null} is not returned as a primitive.
@@ -366,18 +395,6 @@ private static Object processReturnType(Object proxy, Object target, Method meth
366
395
}
367
396
368
397
369
- @ Override
370
- public boolean equals (Object other ) {
371
- return (this == other || (other instanceof CglibAopProxy &&
372
- AopProxyUtils .equalsInProxy (this .advised , ((CglibAopProxy ) other ).advised )));
373
- }
374
-
375
- @ Override
376
- public int hashCode () {
377
- return CglibAopProxy .class .hashCode () * 13 + this .advised .getTargetSource ().hashCode ();
378
- }
379
-
380
-
381
398
/**
382
399
* Serializable replacement for CGLIB's NoOp interface.
383
400
* Public to allow use elsewhere in the framework.
@@ -823,51 +840,42 @@ public int accept(Method method) {
823
840
// Else use the AOP_PROXY.
824
841
if (isStatic && isFrozen && this .fixedInterceptorMap .containsKey (key )) {
825
842
if (logger .isDebugEnabled ()) {
826
- logger .debug ("Method has advice and optimisations are enabled: " + method );
843
+ logger .debug ("Method has advice and optimizations are enabled: " + method );
827
844
}
828
- // We know that we are optimising so we can use the FixedStaticChainInterceptors.
845
+ // We know that we are optimizing so we can use the FixedStaticChainInterceptors.
829
846
int index = this .fixedInterceptorMap .get (key );
830
847
return (index + this .fixedInterceptorOffset );
831
848
}
832
849
else {
833
850
if (logger .isDebugEnabled ()) {
834
- logger .debug ("Unable to apply any optimisations to advised method: " + method );
851
+ logger .debug ("Unable to apply any optimizations to advised method: " + method );
835
852
}
836
853
return AOP_PROXY ;
837
854
}
838
855
}
839
856
else {
840
- // See if the return type of the method is outside the class hierarchy
841
- // of the target type. If so we know it never needs to have return type
842
- // massage and can use a dispatcher.
843
- // If the proxy is being exposed, then must use the interceptor the
844
- // correct one is already configured. If the target is not static, then
845
- // cannot use a dispatcher because the target cannot be released.
857
+ // See if the return type of the method is outside the class hierarchy of the target type.
858
+ // If so we know it never needs to have return type massage and can use a dispatcher.
859
+ // If the proxy is being exposed, then must use the interceptor the correct one is already
860
+ // configured. If the target is not static, then we cannot use a dispatcher because the
861
+ // target needs to be explicitly released after the invocation.
846
862
if (exposeProxy || !isStatic ) {
847
863
return INVOKE_TARGET ;
848
864
}
849
865
Class <?> returnType = method .getReturnType ();
850
- if (targetClass == returnType ) {
866
+ if (returnType . isAssignableFrom ( targetClass ) ) {
851
867
if (logger .isDebugEnabled ()) {
852
- logger .debug ("Method " + method +
853
- "has return type same as target type ( may return this) - using INVOKE_TARGET" );
868
+ logger .debug ("Method return type is assignable from target type and " +
869
+ "may therefore return ' this' - using INVOKE_TARGET: " + method );
854
870
}
855
871
return INVOKE_TARGET ;
856
872
}
857
- else if (returnType .isPrimitive () || !returnType .isAssignableFrom (targetClass )) {
858
- if (logger .isDebugEnabled ()) {
859
- logger .debug ("Method " + method +
860
- " has return type that ensures this cannot be returned- using DISPATCH_TARGET" );
861
- }
862
- return DISPATCH_TARGET ;
863
- }
864
873
else {
865
874
if (logger .isDebugEnabled ()) {
866
- logger .debug ("Method " + method +
867
- "has return type that is assignable from the target type (may return this) - " +
868
- "using INVOKE_TARGET" );
875
+ logger .debug ("Method return type ensures 'this' cannot be returned - " +
876
+ "using DISPATCH_TARGET: " + method );
869
877
}
870
- return INVOKE_TARGET ;
878
+ return DISPATCH_TARGET ;
871
879
}
872
880
}
873
881
}
0 commit comments