Browse Source

TargetSource.getTarget() is nullable again (for compatibility with MethodInvocation.getThis)

Issue: SPR-15651
pull/1454/merge
Juergen Hoeller 8 years ago
parent
commit
47ec966757
  1. 8
      spring-aop/src/main/java/org/springframework/aop/TargetSource.java
  2. 58
      spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java
  3. 2
      spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java
  4. 5
      spring-aop/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java
  5. 2
      spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java
  6. 17
      spring-aop/src/main/java/org/springframework/aop/target/EmptyTargetSource.java
  7. 11
      spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java
  8. 11
      spring-test/src/main/java/org/springframework/test/util/AopTestUtils.java

8
spring-aop/src/main/java/org/springframework/aop/TargetSource.java

@ -16,6 +16,8 @@ @@ -16,6 +16,8 @@
package org.springframework.aop;
import org.springframework.lang.Nullable;
/**
* A {@code TargetSource} is used to obtain the current "target" of
* an AOP invocation, which will be invoked via reflection if no around
@ -54,14 +56,16 @@ public interface TargetSource extends TargetClassAware { @@ -54,14 +56,16 @@ public interface TargetSource extends TargetClassAware {
/**
* Return a target instance. Invoked immediately before the
* AOP framework calls the "target" of an AOP method invocation.
* @return the target object, which contains the joinpoint
* @return the target object which contains the joinpoint,
* or {@code null} if there is no actual target instance
* @throws Exception if the target object can't be resolved
*/
@Nullable
Object getTarget() throws Exception;
/**
* Release the given target object obtained from the
* {@link #getTarget()} method.
* {@link #getTarget()} method, if any.
* @param target object obtained from a call to {@link #getTarget()}
* @throws Exception if the object can't be released
*/

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

@ -326,12 +326,8 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -326,12 +326,8 @@ class CglibAopProxy implements AopProxy, Serializable {
// TODO: small memory optimization here (can skip creation for methods with no advice)
for (int x = 0; x < methods.length; x++) {
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(methods[x], rootClass);
Object target = this.advised.getTargetSource().getTarget();
Class<?> targetClass = this.advised.getTargetClass();
if (targetClass == null) {
targetClass = target.getClass();
}
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(chain, target, targetClass);
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
this.fixedInterceptorMap.put(methods[x].toString(), x);
}
@ -378,20 +374,22 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -378,20 +374,22 @@ class CglibAopProxy implements AopProxy, Serializable {
* {@code proxy} and also verifies that {@code null} is not returned as a primitive.
*/
@Nullable
private static Object processReturnType(Object proxy, Object target, Method method, @Nullable Object retVal) {
private static Object processReturnType(
Object proxy, @Nullable Object target, Method method, @Nullable Object returnValue) {
// Massage return value if necessary
if (retVal != null && retVal == target &&
if (returnValue != null && returnValue == target &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this". Note that we can't help
// if the target sets a reference to itself in another returned object.
retVal = proxy;
returnValue = proxy;
}
Class<?> returnType = method.getReturnType();
if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
if (returnValue == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
return retVal;
return returnValue;
}
@ -413,7 +411,7 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -413,7 +411,7 @@ class CglibAopProxy implements AopProxy, Serializable {
private final Object target;
public StaticUnadvisedInterceptor(Object target) {
public StaticUnadvisedInterceptor(@Nullable Object target) {
this.target = target;
}
@ -434,7 +432,7 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -434,7 +432,7 @@ class CglibAopProxy implements AopProxy, Serializable {
private final Object target;
public StaticUnadvisedExposedInterceptor(Object target) {
public StaticUnadvisedExposedInterceptor(@Nullable Object target) {
this.target = target;
}
@ -476,7 +474,9 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -476,7 +474,9 @@ class CglibAopProxy implements AopProxy, Serializable {
return processReturnType(proxy, target, method, retVal);
}
finally {
this.targetSource.releaseTarget(target);
if (target != null) {
this.targetSource.releaseTarget(target);
}
}
}
}
@ -505,7 +505,9 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -505,7 +505,9 @@ class CglibAopProxy implements AopProxy, Serializable {
}
finally {
AopContext.setCurrentProxy(oldProxy);
this.targetSource.releaseTarget(target);
if (target != null) {
this.targetSource.releaseTarget(target);
}
}
}
}
@ -612,7 +614,9 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -612,7 +614,9 @@ class CglibAopProxy implements AopProxy, Serializable {
private final Class<?> targetClass;
public FixedChainStaticTargetInterceptor(List<Object> adviceChain, Object target, Class<?> targetClass) {
public FixedChainStaticTargetInterceptor(
List<Object> adviceChain, @Nullable Object target, @Nullable Class<?> targetClass) {
this.adviceChain = adviceChain;
this.target = target;
this.targetClass = targetClass;
@ -649,6 +653,7 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -649,6 +653,7 @@ class CglibAopProxy implements AopProxy, Serializable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
TargetSource targetSource = this.advised.getTargetSource();
try {
if (this.advised.exposeProxy) {
// Make invocation available if necessary.
@ -656,8 +661,8 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -656,8 +661,8 @@ class CglibAopProxy implements AopProxy, Serializable {
setProxyContext = true;
}
// Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
target = getTarget();
Class<?> targetClass = target.getClass();
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
// Check whether we only have one InvokerInterceptor: that is,
@ -678,8 +683,8 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -678,8 +683,8 @@ class CglibAopProxy implements AopProxy, Serializable {
return retVal;
}
finally {
if (target != null) {
releaseTarget(target);
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// Restore old proxy.
@ -702,14 +707,6 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -702,14 +707,6 @@ class CglibAopProxy implements AopProxy, Serializable {
public int hashCode() {
return this.advised.hashCode();
}
protected Object getTarget() throws Exception {
return this.advised.getTargetSource().getTarget();
}
protected void releaseTarget(Object target) throws Exception {
this.advised.getTargetSource().releaseTarget(target);
}
}
@ -722,8 +719,9 @@ class CglibAopProxy implements AopProxy, Serializable { @@ -722,8 +719,9 @@ class CglibAopProxy implements AopProxy, Serializable {
private final boolean publicMethod;
public CglibMethodInvocation(Object proxy, Object target, Method method, Object[] arguments,
Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
public CglibMethodInvocation(Object proxy, @Nullable Object target, Method method,
Object[] arguments, @Nullable Class<?> targetClass,
List<Object> interceptorsAndDynamicMethodMatchers, MethodProxy methodProxy) {
super(proxy, target, method, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
this.methodProxy = methodProxy;

2
spring-aop/src/main/java/org/springframework/aop/framework/JdkDynamicAopProxy.java

@ -191,7 +191,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa @@ -191,7 +191,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
// Get as late as possible to minimize the time we "own" the target,
// in case it comes from a pool.
target = targetSource.getTarget();
Class<?> targetClass = target.getClass();
Class<?> targetClass = (target != null ? target.getClass() : null);
// Get the interception chain for this method.
List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

5
spring-aop/src/main/java/org/springframework/aop/framework/ReflectiveMethodInvocation.java

@ -103,8 +103,8 @@ public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Clonea @@ -103,8 +103,8 @@ public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Clonea
* but would complicate the code. And it would work only for static pointcuts.
*/
protected ReflectiveMethodInvocation(
Object proxy, Object target, Method method, Object[] arguments,
Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
Object proxy, @Nullable Object target, Method method, Object[] arguments,
@Nullable Class<?> targetClass, List<Object> interceptorsAndDynamicMethodMatchers) {
this.proxy = proxy;
this.target = target;
@ -121,6 +121,7 @@ public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Clonea @@ -121,6 +121,7 @@ public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Clonea
}
@Override
@Nullable
public final Object getThis() {
return this.target;
}

2
spring-aop/src/main/java/org/springframework/aop/support/AopUtils.java

@ -329,7 +329,7 @@ public abstract class AopUtils { @@ -329,7 +329,7 @@ public abstract class AopUtils {
* @throws org.springframework.aop.AopInvocationException in case of a reflection error
*/
@Nullable
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
public static Object invokeJoinpointUsingReflection(@Nullable Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.

17
spring-aop/src/main/java/org/springframework/aop/target/EmptyTargetSource.java

@ -32,13 +32,6 @@ import org.springframework.util.ObjectUtils; @@ -32,13 +32,6 @@ import org.springframework.util.ObjectUtils;
*/
public class EmptyTargetSource implements TargetSource, Serializable {
/**
* The canonical (Singleton) instance of this {@link EmptyTargetSource}.
*/
public static final EmptyTargetSource INSTANCE = new EmptyTargetSource(null, true);
private static final Object EMPTY_TARGET = new Object();
/** use serialVersionUID from Spring 1.2 for interoperability */
private static final long serialVersionUID = 3680494563553489691L;
@ -47,6 +40,12 @@ public class EmptyTargetSource implements TargetSource, Serializable { @@ -47,6 +40,12 @@ public class EmptyTargetSource implements TargetSource, Serializable {
// Static factory methods
//---------------------------------------------------------------------
/**
* The canonical (Singleton) instance of this {@link EmptyTargetSource}.
*/
public static final EmptyTargetSource INSTANCE = new EmptyTargetSource(null, true);
/**
* Return an EmptyTargetSource for the given target Class.
* @param targetClass the target Class (may be {@code null})
@ -106,11 +105,11 @@ public class EmptyTargetSource implements TargetSource, Serializable { @@ -106,11 +105,11 @@ public class EmptyTargetSource implements TargetSource, Serializable {
}
/**
* Always returns {@code DUMMY_TARGET}.
* Always returns {@code null}.
*/
@Override
public Object getTarget() {
return EMPTY_TARGET;
return null;
}
/**

11
spring-aop/src/test/java/org/springframework/aop/framework/ProxyFactoryTests.java

@ -35,6 +35,7 @@ import org.springframework.aop.support.DefaultPointcutAdvisor; @@ -35,6 +35,7 @@ import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.annotation.Order;
import org.springframework.lang.Nullable;
import org.springframework.tests.TimeStamped;
import org.springframework.tests.aop.advice.CountingBeforeAdvice;
import org.springframework.tests.aop.interceptor.NopInterceptor;
@ -369,6 +370,16 @@ public class ProxyFactoryTests { @@ -369,6 +370,16 @@ public class ProxyFactoryTests {
assertSame(proxy1, list.get(1));
}
@Test
public void testInterceptorWithoutJoinpoint() {
final TestBean target = new TestBean("tb");
ITestBean proxy = ProxyFactory.getProxy(ITestBean.class, (MethodInterceptor) invocation -> {
assertNull(invocation.getThis());
return invocation.getMethod().invoke(target, invocation.getArguments());
});
assertEquals("tb", proxy.getName());
}
@SuppressWarnings("serial")
private static class TimestampIntroductionInterceptor extends DelegatingIntroductionInterceptor

11
spring-test/src/main/java/org/springframework/test/util/AopTestUtils.java

@ -29,6 +29,7 @@ import org.springframework.util.Assert; @@ -29,6 +29,7 @@ import org.springframework.util.Assert;
* {@link org.springframework.aop.framework.AopProxyUtils AopProxyUtils}.
*
* @author Sam Brannen
* @author Juergen Hoeller
* @since 4.2
* @see org.springframework.aop.support.AopUtils
* @see org.springframework.aop.framework.AopProxyUtils
@ -54,7 +55,10 @@ public abstract class AopTestUtils { @@ -54,7 +55,10 @@ public abstract class AopTestUtils {
Assert.notNull(candidate, "Candidate must not be null");
try {
if (AopUtils.isAopProxy(candidate) && candidate instanceof Advised) {
return (T) ((Advised) candidate).getTargetSource().getTarget();
Object target = ((Advised) candidate).getTargetSource().getTarget();
if (target != null) {
return (T) target;
}
}
}
catch (Throwable ex) {
@ -83,7 +87,10 @@ public abstract class AopTestUtils { @@ -83,7 +87,10 @@ public abstract class AopTestUtils {
Assert.notNull(candidate, "Candidate must not be null");
try {
if (AopUtils.isAopProxy(candidate) && candidate instanceof Advised) {
return (T) getUltimateTargetObject(((Advised) candidate).getTargetSource().getTarget());
Object target = ((Advised) candidate).getTargetSource().getTarget();
if (target != null) {
return (T) getUltimateTargetObject(target);
}
}
}
catch (Throwable ex) {

Loading…
Cancel
Save