Browse Source

CglibAopProxy detects package-visible methods when defined in a different ClassLoader

Issue: SPR-11618
pull/516/head
Juergen Hoeller 12 years ago
parent
commit
90309ab0b5
  1. 55
      spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java
  2. 103
      spring-context/src/test/java/org/springframework/aop/framework/CglibProxyTests.java

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

@ -172,7 +172,7 @@ class CglibAopProxy implements AopProxy, Serializable {
} }
// Validate the class, writing log messages as necessary. // Validate the class, writing log messages as necessary.
validateClassIfNecessary(proxySuperClass); validateClassIfNecessary(proxySuperClass, classLoader);
// Configure CGLIB Enhancer... // Configure CGLIB Enhancer...
Enhancer enhancer = createEnhancer(); Enhancer enhancer = createEnhancer();
@ -239,11 +239,11 @@ class CglibAopProxy implements AopProxy, Serializable {
* Checks to see whether the supplied {@code Class} has already been validated and * Checks to see whether the supplied {@code Class} has already been validated and
* validates it if not. * validates it if not.
*/ */
private void validateClassIfNecessary(Class<?> proxySuperClass) { private void validateClassIfNecessary(Class<?> proxySuperClass, ClassLoader proxyClassLoader) {
if (logger.isWarnEnabled()) { if (logger.isInfoEnabled()) {
synchronized (validatedClasses) { synchronized (validatedClasses) {
if (!validatedClasses.containsKey(proxySuperClass)) { if (!validatedClasses.containsKey(proxySuperClass)) {
doValidateClass(proxySuperClass); doValidateClass(proxySuperClass, proxyClassLoader);
validatedClasses.put(proxySuperClass, Boolean.TRUE); validatedClasses.put(proxySuperClass, Boolean.TRUE);
} }
} }
@ -251,19 +251,28 @@ class CglibAopProxy implements AopProxy, Serializable {
} }
/** /**
* Checks for final methods on the {@code Class} and writes warnings to the log * Checks for final methods on the given {@code Class}, as well as package-visible
* for each one found. * methods across ClassLoaders, and writes warnings to the log for each one found.
*/ */
private void doValidateClass(Class<?> proxySuperClass) { private void doValidateClass(Class<?> proxySuperClass, ClassLoader proxyClassLoader) {
if (logger.isWarnEnabled()) { if (!Object.class.equals(proxySuperClass)) {
Method[] methods = proxySuperClass.getMethods(); Method[] methods = proxySuperClass.getDeclaredMethods();
for (Method method : methods) { for (Method method : methods) {
if (!Object.class.equals(method.getDeclaringClass()) && !Modifier.isStatic(method.getModifiers()) && int mod = method.getModifiers();
Modifier.isFinal(method.getModifiers())) { if (!Modifier.isStatic(mod)) {
logger.warn("Unable to proxy method [" + method + "] because it is final: " + if (Modifier.isFinal(mod)) {
"All calls to this method via a proxy will be routed directly to the proxy."); logger.info("Unable to proxy method [" + method + "] because it is final: " +
"All calls to this method via a proxy will NOT be routed to the target instance.");
}
else if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod) && !Modifier.isPrivate(mod) &&
proxyClassLoader != null && proxySuperClass.getClassLoader() != proxyClassLoader) {
logger.info("Unable to proxy method [" + method + "] because it is package-visible " +
"across different ClassLoaders: All calls to this method via a proxy will " +
"NOT be routed to the target instance.");
}
} }
} }
doValidateClass(proxySuperClass.getSuperclass(), proxyClassLoader);
} }
} }
@ -604,7 +613,7 @@ class CglibAopProxy implements AopProxy, Serializable {
*/ */
private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable { private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
private AdvisedSupport advised; private final AdvisedSupport advised;
public DynamicAdvisedInterceptor(AdvisedSupport advised) { public DynamicAdvisedInterceptor(AdvisedSupport advised) {
this.advised = advised; this.advised = advised;
@ -622,8 +631,8 @@ class CglibAopProxy implements AopProxy, Serializable {
oldProxy = AopContext.setCurrentProxy(proxy); oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true; setProxyContext = true;
} }
// May be null Get as late as possible to minimize the time we // May be null. Get as late as possible to minimize the time we
// "own" the target, in case it comes from a pool. // "own" the target, in case it comes from a pool...
target = getTarget(); target = getTarget();
if (target != null) { if (target != null) {
targetClass = target.getClass(); targetClass = target.getClass();
@ -689,13 +698,13 @@ class CglibAopProxy implements AopProxy, Serializable {
private final MethodProxy methodProxy; private final MethodProxy methodProxy;
private boolean protectedMethod; private final boolean publicMethod;
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments, public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) { Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers); super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
this.methodProxy = methodProxy; this.methodProxy = methodProxy;
this.protectedMethod = Modifier.isProtected(method.getModifiers()); this.publicMethod = Modifier.isPublic(method.getModifiers());
} }
/** /**
@ -704,11 +713,11 @@ class CglibAopProxy implements AopProxy, Serializable {
*/ */
@Override @Override
protected Object invokeJoinpoint() throws Throwable { protected Object invokeJoinpoint() throws Throwable {
if (this.protectedMethod) { if (this.publicMethod) {
return super.invokeJoinpoint(); return this.methodProxy.invoke(this.target, this.arguments);
} }
else { else {
return this.methodProxy.invoke(this.target, this.arguments); return super.invokeJoinpoint();
} }
} }
} }
@ -829,8 +838,8 @@ class CglibAopProxy implements AopProxy, Serializable {
// of the target type. If so we know it never needs to have return type // of the target type. If so we know it never needs to have return type
// massage and can use a dispatcher. // massage and can use a dispatcher.
// If the proxy is being exposed, then must use the interceptor the // If the proxy is being exposed, then must use the interceptor the
// correct one is already configured. If the target is not static cannot // correct one is already configured. If the target is not static, then
// use a Dispatcher because the target can not then be released. // cannot use a dispatcher because the target cannot be released.
if (exposeProxy || !isStatic) { if (exposeProxy || !isStatic) {
return INVOKE_TARGET; return INVOKE_TARGET;
} }

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

@ -102,6 +102,7 @@ public final class CglibProxyTests extends AbstractAopProxyTests implements Seri
@Test @Test
public void testProtectedMethodInvocation() { public void testProtectedMethodInvocation() {
ProtectedMethodTestBean bean = new ProtectedMethodTestBean(); ProtectedMethodTestBean bean = new ProtectedMethodTestBean();
bean.value = "foo";
mockTargetSource.setTarget(bean); mockTargetSource.setTarget(bean);
AdvisedSupport as = new AdvisedSupport(new Class<?>[]{}); AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
@ -109,8 +110,47 @@ public final class CglibProxyTests extends AbstractAopProxyTests implements Seri
as.addAdvice(new NopInterceptor()); as.addAdvice(new NopInterceptor());
AopProxy aop = new CglibAopProxy(as); AopProxy aop = new CglibAopProxy(as);
Object proxy = aop.getProxy(); ProtectedMethodTestBean proxy = (ProtectedMethodTestBean) aop.getProxy();
assertTrue(AopUtils.isCglibProxy(proxy)); assertTrue(AopUtils.isCglibProxy(proxy));
assertEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
assertEquals("foo", proxy.getString());
}
@Test
public void testPackageMethodInvocation() {
PackageMethodTestBean bean = new PackageMethodTestBean();
bean.value = "foo";
mockTargetSource.setTarget(bean);
AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
as.setTargetSource(mockTargetSource);
as.addAdvice(new NopInterceptor());
AopProxy aop = new CglibAopProxy(as);
PackageMethodTestBean proxy = (PackageMethodTestBean) aop.getProxy();
assertTrue(AopUtils.isCglibProxy(proxy));
assertEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
assertEquals("foo", proxy.getString());
}
@Test
public void testPackageMethodInvocationWithDifferentClassLoader() {
ClassLoader child = new ClassLoader(getClass().getClassLoader()) {
};
PackageMethodTestBean bean = new PackageMethodTestBean();
bean.value = "foo";
mockTargetSource.setTarget(bean);
AdvisedSupport as = new AdvisedSupport(new Class<?>[]{});
as.setTargetSource(mockTargetSource);
as.addAdvice(new NopInterceptor());
AopProxy aop = new CglibAopProxy(as);
PackageMethodTestBean proxy = (PackageMethodTestBean) aop.getProxy(child);
assertTrue(AopUtils.isCglibProxy(proxy));
assertNotEquals(proxy.getClass().getClassLoader(), bean.getClass().getClassLoader());
assertNull(proxy.getString()); // we're stuck in the proxy instance
} }
@Test @Test
@ -410,54 +450,59 @@ public final class CglibProxyTests extends AbstractAopProxyTests implements Seri
} }
public static class HasFinalMethod { public static class NoArgCtorTestBean {
private boolean called = false;
public final void foo() { public NoArgCtorTestBean(String x, int y) {
called = true;
}
public boolean wasCalled() {
return called;
}
public void reset() {
called = false;
} }
} }
}
class CglibTestBean { public static class ProtectedMethodTestBean {
private String name; public String value;
public CglibTestBean() { protected String getString() {
setName("Some Default"); return this.value;
}
} }
public void setName(String name) {
this.name = name;
}
public String getName() { public static class PackageMethodTestBean {
return this.name;
public String value;
String getString() {
return this.value;
}
} }
} }
class NoArgCtorTestBean { class CglibTestBean {
private boolean called = false;
public NoArgCtorTestBean(String x, int y) { private String name;
called = true;
}
public boolean wasCalled() { public CglibTestBean() {
return called; setName("Some Default");
} }
public void reset() { public void setName(String name) {
called = false; this.name = name;
} }
}
class ProtectedMethodTestBean { public String getName() {
return this.name;
protected String getString() {
return "foo";
} }
} }

Loading…
Cancel
Save