AnnotationAwareOrderComparator uses DecoratingProxy interface for target class introspection

Issue: SPR-13884
This commit is contained in:
Juergen Hoeller 2016-03-18 22:12:10 +01:00
parent 9ac9135c24
commit 6e3fac85f3
8 changed files with 168 additions and 33 deletions

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -26,6 +26,7 @@ import org.springframework.aop.TargetClassAware;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.AopUtils;
import org.springframework.aop.target.SingletonTargetSource;
import org.springframework.core.DecoratingProxy;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
@ -78,11 +79,29 @@ public abstract class AopProxyUtils {
* <p>This will always add the {@link Advised} interface unless the AdvisedSupport's
* {@link AdvisedSupport#setOpaque "opaque"} flag is on. Always adds the
* {@link org.springframework.aop.SpringProxy} marker interface.
* @param advised the proxy config
* @return the complete set of interfaces to proxy
* @see SpringProxy
* @see Advised
* @see org.springframework.aop.SpringProxy
*/
public static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised) {
return completeProxiedInterfaces(advised, false);
}
/**
* Determine the complete set of interfaces to proxy for the given AOP configuration.
* <p>This will always add the {@link Advised} interface unless the AdvisedSupport's
* {@link AdvisedSupport#setOpaque "opaque"} flag is on. Always adds the
* {@link org.springframework.aop.SpringProxy} marker interface.
* @param advised the proxy config
* @param decoratingProxy whether to expose the {@link DecoratingProxy} interface
* @return the complete set of interfaces to proxy
* @since 4.3
* @see SpringProxy
* @see Advised
* @see DecoratingProxy
*/
static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
if (specifiedInterfaces.length == 0) {
// No user-specified interfaces: check whether target class is an interface.
@ -99,6 +118,7 @@ public abstract class AopProxyUtils {
}
boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class);
boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class);
boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class));
int nonUserIfcCount = 0;
if (addSpringProxy) {
nonUserIfcCount++;
@ -106,13 +126,22 @@ public abstract class AopProxyUtils {
if (addAdvised) {
nonUserIfcCount++;
}
if (addDecoratingProxy) {
nonUserIfcCount++;
}
Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount];
System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length);
int index = specifiedInterfaces.length;
if (addSpringProxy) {
proxiedInterfaces[specifiedInterfaces.length] = SpringProxy.class;
proxiedInterfaces[index] = SpringProxy.class;
index++;
}
if (addAdvised) {
proxiedInterfaces[proxiedInterfaces.length - 1] = Advised.class;
proxiedInterfaces[index] = Advised.class;
index++;
}
if (addDecoratingProxy) {
proxiedInterfaces[index] = DecoratingProxy.class;
}
return proxiedInterfaces;
}
@ -134,6 +163,9 @@ public abstract class AopProxyUtils {
if (proxy instanceof Advised) {
nonUserIfcCount++;
}
if (proxy instanceof DecoratingProxy) {
nonUserIfcCount++;
}
Class<?>[] userInterfaces = new Class<?>[proxyInterfaces.length - nonUserIfcCount];
System.arraycopy(proxyInterfaces, 0, userInterfaces, 0, userInterfaces.length);
Assert.notEmpty(userInterfaces, "JDK proxy must implement one or more interfaces");

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -30,6 +30,7 @@ import org.springframework.aop.AopInvocationException;
import org.springframework.aop.RawTargetAccess;
import org.springframework.aop.TargetSource;
import org.springframework.aop.support.AopUtils;
import org.springframework.core.DecoratingProxy;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
@ -116,7 +117,7 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
if (logger.isDebugEnabled()) {
logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
@ -164,11 +165,15 @@ final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializa
// The target does not implement the equals(Object) method itself.
return equals(args[0]);
}
if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
// The target does not implement the hashCode() method itself.
return hashCode();
}
if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
else if (method.getDeclaringClass() == DecoratingProxy.class) {
// There is only getDecoratedClass() declared -> dispatch to proxy config.
return AopProxyUtils.ultimateTargetClass(this.advised);
}
else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
method.getDeclaringClass().isAssignableFrom(Advised.class)) {
// Service invocations on ProxyConfig with the proxy config...
return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2014 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -22,9 +22,9 @@ import org.springframework.aop.TargetSource;
import org.springframework.util.ClassUtils;
/**
* Factory for AOP proxies for programmatic use, rather than via a bean
* factory. This class provides a simple way of obtaining and configuring
* AOP proxies in code.
* Factory for AOP proxies for programmatic use, rather than via declarative
* setup in a bean factory. This class provides a simple way of obtaining
* and configuring AOP proxy instances in custom user code.
*
* @author Rod Johnson
* @author Juergen Hoeller

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,8 +16,6 @@
package org.springframework.aop.framework;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
@ -34,7 +32,7 @@ import static org.junit.Assert.*;
* @author Rod Johnson
* @author Chris Beams
*/
public final class AopProxyUtilsTests {
public class AopProxyUtilsTests {
@Test
public void testCompleteProxiedInterfacesWorksWithNull() {
@ -125,15 +123,10 @@ public final class AopProxyUtilsTests {
assertEquals(Comparable.class, userInterfaces[1]);
}
@Test(expected=IllegalArgumentException.class)
@Test(expected = IllegalArgumentException.class)
public void testProxiedUserInterfacesWithNoInterface() {
Object proxy = Proxy.newProxyInstance(getClass().getClassLoader(), new Class[0],
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return null;
}
});
(proxy1, method, args) -> null);
AopProxyUtils.proxiedUserInterfaces(proxy);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,6 +16,8 @@
package org.springframework.aop.framework;
import java.util.ArrayList;
import java.util.List;
import javax.accessibility.Accessible;
import javax.swing.JFrame;
import javax.swing.RootPaneContainer;
@ -31,6 +33,8 @@ import org.springframework.aop.support.AopUtils;
import org.springframework.aop.support.DefaultIntroductionAdvisor;
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.tests.TimeStamped;
import org.springframework.tests.aop.advice.CountingBeforeAdvice;
import org.springframework.tests.aop.interceptor.NopInterceptor;
@ -49,7 +53,7 @@ import static org.junit.Assert.*;
* @author Chris Beams
* @since 14.05.2003
*/
public final class ProxyFactoryTests {
public class ProxyFactoryTests {
@Test
public void testIndexOfMethods() {
@ -337,6 +341,34 @@ public final class ProxyFactoryTests {
assertTrue(proxy instanceof Accessible);
}
@Test
public void testInterfaceProxiesCanBeOrderedThroughAnnotations() {
Object proxy1 = new ProxyFactory(new A()).getProxy();
Object proxy2 = new ProxyFactory(new B()).getProxy();
List<Object> list = new ArrayList<Object>(2);
list.add(proxy1);
list.add(proxy2);
AnnotationAwareOrderComparator.sort(list);
assertSame(proxy2, list.get(0));
assertSame(proxy1, list.get(1));
}
@Test
public void testTargetClassProxiesCanBeOrderedThroughAnnotations() {
ProxyFactory pf1 = new ProxyFactory(new A());
pf1.setProxyTargetClass(true);
ProxyFactory pf2 = new ProxyFactory(new B());
pf2.setProxyTargetClass(true);
Object proxy1 = pf1.getProxy();
Object proxy2 = pf2.getProxy();
List<Object> list = new ArrayList<Object>(2);
list.add(proxy1);
list.add(proxy2);
AnnotationAwareOrderComparator.sort(list);
assertSame(proxy2, list.get(0));
assertSame(proxy1, list.get(1));
}
@SuppressWarnings("serial")
private static class TimestampIntroductionInterceptor extends DelegatingIntroductionInterceptor
@ -361,4 +393,22 @@ public final class ProxyFactoryTests {
}
}
@Order(2)
public static class A implements Runnable {
@Override
public void run() {
}
}
@Order(1)
public static class B implements Runnable{
@Override
public void run() {
}
}
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -32,10 +32,10 @@ import org.springframework.tests.sample.beans.TestBean;
import static org.junit.Assert.*;
/**
* @since 13.03.2003
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @since 13.03.2003
*/
@SuppressWarnings("serial")
public class JdkDynamicProxyTests extends AbstractAopProxyTests implements Serializable {

View File

@ -0,0 +1,47 @@
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.core;
/**
* Interface to be implemented by decorating proxies, in particular Spring AOP
* proxies but potentially also custom proxies with decorator semantics.
*
* <p>Note that this interface should just be implemented if the decorated class
* is not within the hierarchy of the proxy class to begin with. In particular,
* a "target-class" proxy such as a Spring AOP CGLIB proxy should not implement
* it since any lookup on the target class can simply be performed on the proxy
* class there anyway.
*
* <p>Defined in the core module in order to allow
* #{@link org.springframework.core.annotation.AnnotationAwareOrderComparator}
* (and potential other candidates without spring-aop dependencies) to use it
* for introspection purposes, in particular annotation lookups.
*
* @author Juergen Hoeller
* @since 4.3
*/
public interface DecoratingProxy {
/**
* Return the (ultimate) decorated class behind this proxy.
* <p>In case of an AOP proxy, this will be the ultimate target class,
* not just the immediate target (in case of multiple nested proxies).
* @return the decorated class (never {@code null})
*/
Class<?> getDecoratedClass();
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2015 the original author or authors.
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.List;
import org.springframework.core.OrderComparator;
import org.springframework.core.DecoratingProxy;
/**
* {@code AnnotationAwareOrderComparator} is an extension of
@ -81,10 +82,13 @@ public class AnnotationAwareOrderComparator extends OrderComparator {
}
}
else if (obj != null) {
return OrderUtils.getOrder(obj.getClass());
order = OrderUtils.getOrder(obj.getClass());
if (order == null && obj instanceof DecoratingProxy) {
order = OrderUtils.getOrder(((DecoratingProxy) obj).getDecoratedClass());
}
}
return null;
return order;
}
/**
@ -94,13 +98,17 @@ public class AnnotationAwareOrderComparator extends OrderComparator {
* multiple matches but only one object to be returned.
*/
public Integer getPriority(Object obj) {
Integer priority = null;
if (obj instanceof Class) {
return OrderUtils.getPriority((Class<?>) obj);
priority = OrderUtils.getPriority((Class<?>) obj);
}
else if (obj != null) {
return OrderUtils.getPriority(obj.getClass());
priority = OrderUtils.getPriority(obj.getClass());
if (priority == null && obj instanceof DecoratingProxy) {
priority = OrderUtils.getOrder(((DecoratingProxy) obj).getDecoratedClass());
}
}
return null;
return priority;
}