CglibAopProxy detects package-visible methods when defined in a different ClassLoader
Issue: SPR-11618
This commit is contained in:
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,9 +450,40 @@ public final class CglibProxyTests extends AbstractAopProxyTests implements Seri
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class HasFinalMethod {
|
public static class NoArgCtorTestBean {
|
||||||
|
|
||||||
public final void foo() {
|
private boolean called = false;
|
||||||
|
|
||||||
|
public NoArgCtorTestBean(String x, int y) {
|
||||||
|
called = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean wasCalled() {
|
||||||
|
return called;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset() {
|
||||||
|
called = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class ProtectedMethodTestBean {
|
||||||
|
|
||||||
|
public String value;
|
||||||
|
|
||||||
|
protected String getString() {
|
||||||
|
return this.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class PackageMethodTestBean {
|
||||||
|
|
||||||
|
public String value;
|
||||||
|
|
||||||
|
String getString() {
|
||||||
|
return this.value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -436,32 +507,6 @@ class CglibTestBean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class NoArgCtorTestBean {
|
|
||||||
|
|
||||||
private boolean called = false;
|
|
||||||
|
|
||||||
public NoArgCtorTestBean(String x, int y) {
|
|
||||||
called = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean wasCalled() {
|
|
||||||
return called;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void reset() {
|
|
||||||
called = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ProtectedMethodTestBean {
|
|
||||||
|
|
||||||
protected String getString() {
|
|
||||||
return "foo";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class UnsupportedInterceptor implements MethodInterceptor {
|
class UnsupportedInterceptor implements MethodInterceptor {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
Reference in New Issue
Block a user