Skip to content

Commit 90309ab

Browse files
committed
CglibAopProxy detects package-visible methods when defined in a different ClassLoader
Issue: SPR-11618
1 parent 7bc2168 commit 90309ab

File tree

2 files changed

+106
-52
lines changed

2 files changed

+106
-52
lines changed

spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ public Object getProxy(ClassLoader classLoader) {
172172
}
173173

174174
// Validate the class, writing log messages as necessary.
175-
validateClassIfNecessary(proxySuperClass);
175+
validateClassIfNecessary(proxySuperClass, classLoader);
176176

177177
// Configure CGLIB Enhancer...
178178
Enhancer enhancer = createEnhancer();
@@ -239,31 +239,40 @@ protected Enhancer createEnhancer() {
239239
* Checks to see whether the supplied {@code Class} has already been validated and
240240
* validates it if not.
241241
*/
242-
private void validateClassIfNecessary(Class<?> proxySuperClass) {
243-
if (logger.isWarnEnabled()) {
242+
private void validateClassIfNecessary(Class<?> proxySuperClass, ClassLoader proxyClassLoader) {
243+
if (logger.isInfoEnabled()) {
244244
synchronized (validatedClasses) {
245245
if (!validatedClasses.containsKey(proxySuperClass)) {
246-
doValidateClass(proxySuperClass);
246+
doValidateClass(proxySuperClass, proxyClassLoader);
247247
validatedClasses.put(proxySuperClass, Boolean.TRUE);
248248
}
249249
}
250250
}
251251
}
252252

253253
/**
254-
* Checks for final methods on the {@code Class} and writes warnings to the log
255-
* for each one found.
254+
* Checks for final methods on the given {@code Class}, as well as package-visible
255+
* methods across ClassLoaders, and writes warnings to the log for each one found.
256256
*/
257-
private void doValidateClass(Class<?> proxySuperClass) {
258-
if (logger.isWarnEnabled()) {
259-
Method[] methods = proxySuperClass.getMethods();
257+
private void doValidateClass(Class<?> proxySuperClass, ClassLoader proxyClassLoader) {
258+
if (!Object.class.equals(proxySuperClass)) {
259+
Method[] methods = proxySuperClass.getDeclaredMethods();
260260
for (Method method : methods) {
261-
if (!Object.class.equals(method.getDeclaringClass()) && !Modifier.isStatic(method.getModifiers()) &&
262-
Modifier.isFinal(method.getModifiers())) {
263-
logger.warn("Unable to proxy method [" + method + "] because it is final: " +
264-
"All calls to this method via a proxy will be routed directly to the proxy.");
261+
int mod = method.getModifiers();
262+
if (!Modifier.isStatic(mod)) {
263+
if (Modifier.isFinal(mod)) {
264+
logger.info("Unable to proxy method [" + method + "] because it is final: " +
265+
"All calls to this method via a proxy will NOT be routed to the target instance.");
266+
}
267+
else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) && !Modifier.isPrivate(mod) &&
268+
proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) {
269+
logger.info("Unable to proxy method [" + method + "] because it is package-visible " +
270+
"across different ClassLoaders: All calls to this method via a proxy will " +
271+
"NOT be routed to the target instance.");
272+
}
265273
}
266274
}
275+
doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader);
267276
}
268277
}
269278

@@ -604,7 +613,7 @@ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy
604613
*/
605614
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
606615

607-
private AdvisedSupport advised;
616+
private final AdvisedSupport advised;
608617

609618
public DynamicAdvisedInterceptor(AdvisedSupport advised) {
610619
this.advised = advised;
@@ -622,8 +631,8 @@ public Object intercept(Object proxy, Method method, Object[] args, MethodProxy
622631
oldProxy = AopContext.setCurrentProxy(proxy);
623632
setProxyContext = true;
624633
}
625-
// May be null Get as late as possible to minimize the time we
626-
// "own" the target, in case it comes from a pool.
634+
// May be null. Get as late as possible to minimize the time we
635+
// "own" the target, in case it comes from a pool...
627636
target = getTarget();
628637
if (target != null) {
629638
targetClass = target.getClass();
@@ -689,13 +698,13 @@ private static class CglibMethodInvocation extends ReflectiveMethodInvocation {
689698

690699
private final MethodProxy methodProxy;
691700

692-
private boolean protectedMethod;
701+
private final boolean publicMethod;
693702

694703
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
695704
Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
696705
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
697706
this.methodProxy = methodProxy;
698-
this.protectedMethod = Modifier.isProtected(method.getModifiers());
707+
this.publicMethod = Modifier.isPublic(method.getModifiers());
699708
}
700709

701710
/**
@@ -704,11 +713,11 @@ public CglibMethodInvocation(Object proxy, Object target, Method method, Object[
704713
*/
705714
@Override
706715
protected Object invokeJoinpoint() throws Throwable {
707-
if (this.protectedMethod) {
708-
return super.invokeJoinpoint();
716+
if (this.publicMethod) {
717+
return this.methodProxy.invoke(this.target, this.arguments);
709718
}
710719
else {
711-
return this.methodProxy.invoke(this.target, this.arguments);
720+
return super.invokeJoinpoint();
712721
}
713722
}
714723
}
@@ -829,8 +838,8 @@ public int accept(Method method) {
829838
// of the target type. If so we know it never needs to have return type
830839
// massage and can use a dispatcher.
831840
// If the proxy is being exposed, then must use the interceptor the
832-
// correct one is already configured. If the target is not static cannot
833-
// use a Dispatcher because the target can not then be released.
841+
// correct one is already configured. If the target is not static, then
842+
// cannot use a dispatcher because the target cannot be released.
834843
if (exposeProxy || !isStatic) {
835844
return INVOKE_TARGET;
836845
}

spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java

Lines changed: 74 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -102,15 +102,55 @@ public void testNoTarget() {
102102
@Test
103103
public void testProtectedMethodInvocation() {
104104
ProtectedMethodTestBean bean = new ProtectedMethodTestBean();
105+
bean.value = "foo";
105106
mockTargetSource.setTarget(bean);
106107

107108
AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
108109
as.setTargetSource(mockTargetSource);
109110
as.addAdvice(new NopInterceptor());
110111
AopProxy aop = new CglibAopProxy(as);
111112

112-
Object proxy = aop.getProxy();
113+
ProtectedMethodTestBean proxy = (ProtectedMethodTestBean) aop.getProxy();
113114
assertTrue(AopUtils.isCglibProxy(proxy));
115+
assertEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
116+
assertEquals("foo", proxy.getString());
117+
}
118+
119+
@Test
120+
public void testPackageMethodInvocation() {
121+
PackageMethodTestBean bean = new PackageMethodTestBean();
122+
bean.value = "foo";
123+
mockTargetSource.setTarget(bean);
124+
125+
AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
126+
as.setTargetSource(mockTargetSource);
127+
as.addAdvice(new NopInterceptor());
128+
AopProxy aop = new CglibAopProxy(as);
129+
130+
PackageMethodTestBean proxy = (PackageMethodTestBean) aop.getProxy();
131+
assertTrue(AopUtils.isCglibProxy(proxy));
132+
assertEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
133+
assertEquals("foo", proxy.getString());
134+
}
135+
136+
@Test
137+
public void testPackageMethodInvocationWithDifferentClassLoader() {
138+
ClassLoader child = new ClassLoader(getClass().getClassLoader()) {
139+
};
140+
141+
PackageMethodTestBean bean = new PackageMethodTestBean();
142+
bean.value = "foo";
143+
mockTargetSource.setTarget(bean);
144+
145+
AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
146+
as.setTargetSource(mockTargetSource);
147+
as.addAdvice(new NopInterceptor());
148+
AopProxy aop = new CglibAopProxy(as);
149+
150+
PackageMethodTestBean proxy = (PackageMethodTestBean) aop.getProxy(child);
151+
assertTrue(AopUtils.isCglibProxy(proxy));
152+
assertNotEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
153+
assertNull(proxy.getString()); // we're stuck in the proxy instance
114154
}
115155

116156
@Test
@@ -410,54 +450,59 @@ public void doTest() throws Exception {
410450
}
411451

412452

413-
public static class HasFinalMethod {
453+
public static class NoArgCtorTestBean {
454+
455+
private boolean called = false;
414456

415-
public final void foo() {
457+
public NoArgCtorTestBean(String x, int y) {
458+
called = true;
459+
}
460+
461+
public boolean wasCalled() {
462+
return called;
463+
}
464+
465+
public void reset() {
466+
called = false;
416467
}
417468
}
418-
}
419469

420470

421-
class CglibTestBean {
471+
public static class ProtectedMethodTestBean {
422472

423-
private String name;
473+
public String value;
424474

425-
public CglibTestBean() {
426-
setName("Some Default");
475+
protected String getString() {
476+
return this.value;
477+
}
427478
}
428479

429-
public void setName(String name) {
430-
this.name = name;
431-
}
432480

433-
public String getName() {
434-
return this.name;
481+
public static class PackageMethodTestBean {
482+
483+
public String value;
484+
485+
String getString() {
486+
return this.value;
487+
}
435488
}
436489
}
437490

438491

439-
class NoArgCtorTestBean {
440-
441-
private boolean called = false;
492+
class CglibTestBean {
442493

443-
public NoArgCtorTestBean(String x, int y) {
444-
called = true;
445-
}
494+
private String name;
446495

447-
public boolean wasCalled() {
448-
return called;
496+
public CglibTestBean() {
497+
setName("Some Default");
449498
}
450499

451-
public void reset() {
452-
called = false;
500+
public void setName(String name) {
501+
this.name = name;
453502
}
454-
}
455-
456503

457-
class ProtectedMethodTestBean {
458-
459-
protected String getString() {
460-
return "foo";
504+
public String getName() {
505+
return this.name;
461506
}
462507
}
463508

0 commit comments

Comments
 (0)