AnnotationAwareOrderComparator uses DecoratingProxy interface for target class introspection
Issue: SPR-13884
This commit is contained in:
parent
9ac9135c24
commit
6e3fac85f3
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue