/*
* Copyright 2002-2017 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.aop.framework;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import org.springframework.aop.SpringProxy;
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.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ObjectUtils;
Utility methods for AOP proxy factories.
Mainly for internal use within the AOP framework.
See AopUtils
for a collection of generic AOP utility methods which do not depend on AOP framework internals.
Author: Rod Johnson, Juergen Hoeller See Also:
/**
* Utility methods for AOP proxy factories.
* Mainly for internal use within the AOP framework.
*
* <p>See {@link org.springframework.aop.support.AopUtils} for a collection of
* generic AOP utility methods which do not depend on AOP framework internals.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @see org.springframework.aop.support.AopUtils
*/
public abstract class AopProxyUtils {
Obtain the singleton target object behind the given proxy, if any.
Params: - candidate – the (potential) proxy to check
See Also: Returns: the singleton target object managed in a SingletonTargetSource
, or null
in any other case (not a proxy, not an existing singleton target) Since: 4.3.8
/**
* Obtain the singleton target object behind the given proxy, if any.
* @param candidate the (potential) proxy to check
* @return the singleton target object managed in a {@link SingletonTargetSource},
* or {@code null} in any other case (not a proxy, not an existing singleton target)
* @since 4.3.8
* @see Advised#getTargetSource()
* @see SingletonTargetSource#getTarget()
*/
@Nullable
public static Object getSingletonTarget(Object candidate) {
if (candidate instanceof Advised) {
TargetSource targetSource = ((Advised) candidate).getTargetSource();
if (targetSource instanceof SingletonTargetSource) {
return ((SingletonTargetSource) targetSource).getTarget();
}
}
return null;
}
Determine the ultimate target class of the given bean instance, traversing
not only a top-level proxy but any number of nested proxies as well —
as long as possible without side effects, that is, just for singleton targets.
Params: - candidate – the instance to check (might be an AOP proxy)
See Also: Returns: the ultimate target class (or the plain class of the given object as fallback; never null
)
/**
* Determine the ultimate target class of the given bean instance, traversing
* not only a top-level proxy but any number of nested proxies as well —
* as long as possible without side effects, that is, just for singleton targets.
* @param candidate the instance to check (might be an AOP proxy)
* @return the ultimate target class (or the plain class of the given
* object as fallback; never {@code null})
* @see org.springframework.aop.TargetClassAware#getTargetClass()
* @see Advised#getTargetSource()
*/
public static Class<?> ultimateTargetClass(Object candidate) {
Assert.notNull(candidate, "Candidate object must not be null");
Object current = candidate;
Class<?> result = null;
while (current instanceof TargetClassAware) {
result = ((TargetClassAware) current).getTargetClass();
current = getSingletonTarget(current);
}
if (result == null) {
result = (AopUtils.isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass());
}
return result;
}
Determine the complete set of interfaces to proxy for the given AOP configuration.
This will always add the Advised
interface unless the AdvisedSupport's "opaque"
flag is on. Always adds the SpringProxy
marker interface.
Params: - advised – the proxy config
See Also: Returns: the complete set of interfaces to proxy
/**
* 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
* @return the complete set of interfaces to proxy
* @see SpringProxy
* @see Advised
*/
public static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised) {
return completeProxiedInterfaces(advised, false);
}
Determine the complete set of interfaces to proxy for the given AOP configuration.
This will always add the Advised
interface unless the AdvisedSupport's "opaque"
flag is on. Always adds the SpringProxy
marker interface.
Params: - advised – the proxy config
- decoratingProxy – whether to expose the
DecoratingProxy
interface
See Also: Returns: the complete set of interfaces to proxy Since: 4.3
/**
* 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.
Class<?> targetClass = advised.getTargetClass();
if (targetClass != null) {
if (targetClass.isInterface()) {
advised.setInterfaces(targetClass);
}
else if (Proxy.isProxyClass(targetClass)) {
advised.setInterfaces(targetClass.getInterfaces());
}
specifiedInterfaces = advised.getProxiedInterfaces();
}
}
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++;
}
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[index] = SpringProxy.class;
index++;
}
if (addAdvised) {
proxiedInterfaces[index] = Advised.class;
index++;
}
if (addDecoratingProxy) {
proxiedInterfaces[index] = DecoratingProxy.class;
}
return proxiedInterfaces;
}
Extract the user-specified interfaces that the given proxy implements,
i.e. all non-Advised interfaces that the proxy implements.
Params: - proxy – the proxy to analyze (usually a JDK dynamic proxy)
See Also: Returns: all user-specified interfaces that the proxy implements, in the original order (never null
or empty)
/**
* Extract the user-specified interfaces that the given proxy implements,
* i.e. all non-Advised interfaces that the proxy implements.
* @param proxy the proxy to analyze (usually a JDK dynamic proxy)
* @return all user-specified interfaces that the proxy implements,
* in the original order (never {@code null} or empty)
* @see Advised
*/
public static Class<?>[] proxiedUserInterfaces(Object proxy) {
Class<?>[] proxyInterfaces = proxy.getClass().getInterfaces();
int nonUserIfcCount = 0;
if (proxy instanceof SpringProxy) {
nonUserIfcCount++;
}
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");
return userInterfaces;
}
Check equality of the proxies behind the given AdvisedSupport objects.
Not the same as equality of the AdvisedSupport objects:
rather, equality of interfaces, advisors and target sources.
/**
* Check equality of the proxies behind the given AdvisedSupport objects.
* Not the same as equality of the AdvisedSupport objects:
* rather, equality of interfaces, advisors and target sources.
*/
public static boolean equalsInProxy(AdvisedSupport a, AdvisedSupport b) {
return (a == b ||
(equalsProxiedInterfaces(a, b) && equalsAdvisors(a, b) && a.getTargetSource().equals(b.getTargetSource())));
}
Check equality of the proxied interfaces behind the given AdvisedSupport objects.
/**
* Check equality of the proxied interfaces behind the given AdvisedSupport objects.
*/
public static boolean equalsProxiedInterfaces(AdvisedSupport a, AdvisedSupport b) {
return Arrays.equals(a.getProxiedInterfaces(), b.getProxiedInterfaces());
}
Check equality of the advisors behind the given AdvisedSupport objects.
/**
* Check equality of the advisors behind the given AdvisedSupport objects.
*/
public static boolean equalsAdvisors(AdvisedSupport a, AdvisedSupport b) {
return Arrays.equals(a.getAdvisors(), b.getAdvisors());
}
Adapt the given arguments to the target signature in the given method,
if necessary: in particular, if a given vararg argument array does not
match the array type of the declared vararg parameter in the method.
Params: - method – the target method
- arguments – the given arguments
Returns: a cloned argument array, or the original if no adaptation is needed Since: 4.2.3
/**
* Adapt the given arguments to the target signature in the given method,
* if necessary: in particular, if a given vararg argument array does not
* match the array type of the declared vararg parameter in the method.
* @param method the target method
* @param arguments the given arguments
* @return a cloned argument array, or the original if no adaptation is needed
* @since 4.2.3
*/
static Object[] adaptArgumentsIfNecessary(Method method, @Nullable Object[] arguments) {
if (ObjectUtils.isEmpty(arguments)) {
return new Object[0];
}
if (method.isVarArgs()) {
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length == arguments.length) {
int varargIndex = paramTypes.length - 1;
Class<?> varargType = paramTypes[varargIndex];
if (varargType.isArray()) {
Object varargArray = arguments[varargIndex];
if (varargArray instanceof Object[] && !varargType.isInstance(varargArray)) {
Object[] newArguments = new Object[arguments.length];
System.arraycopy(arguments, 0, newArguments, 0, varargIndex);
Class<?> targetElementType = varargType.getComponentType();
int varargLength = Array.getLength(varargArray);
Object newVarargArray = Array.newInstance(targetElementType, varargLength);
System.arraycopy(varargArray, 0, newVarargArray, 0, varargLength);
newArguments[varargIndex] = newVarargArray;
return newArguments;
}
}
}
}
return arguments;
}
}