001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.aop.framework; 018 019import java.lang.reflect.Array; 020import java.lang.reflect.Method; 021import java.lang.reflect.Proxy; 022import java.util.Arrays; 023 024import org.springframework.aop.SpringProxy; 025import org.springframework.aop.TargetClassAware; 026import org.springframework.aop.TargetSource; 027import org.springframework.aop.support.AopUtils; 028import org.springframework.aop.target.SingletonTargetSource; 029import org.springframework.core.DecoratingProxy; 030import org.springframework.lang.Nullable; 031import org.springframework.util.Assert; 032import org.springframework.util.ObjectUtils; 033 034/** 035 * Utility methods for AOP proxy factories. 036 * Mainly for internal use within the AOP framework. 037 * 038 * <p>See {@link org.springframework.aop.support.AopUtils} for a collection of 039 * generic AOP utility methods which do not depend on AOP framework internals. 040 * 041 * @author Rod Johnson 042 * @author Juergen Hoeller 043 * @see org.springframework.aop.support.AopUtils 044 */ 045public abstract class AopProxyUtils { 046 047 /** 048 * Obtain the singleton target object behind the given proxy, if any. 049 * @param candidate the (potential) proxy to check 050 * @return the singleton target object managed in a {@link SingletonTargetSource}, 051 * or {@code null} in any other case (not a proxy, not an existing singleton target) 052 * @since 4.3.8 053 * @see Advised#getTargetSource() 054 * @see SingletonTargetSource#getTarget() 055 */ 056 @Nullable 057 public static Object getSingletonTarget(Object candidate) { 058 if (candidate instanceof Advised) { 059 TargetSource targetSource = ((Advised) candidate).getTargetSource(); 060 if (targetSource instanceof SingletonTargetSource) { 061 return ((SingletonTargetSource) targetSource).getTarget(); 062 } 063 } 064 return null; 065 } 066 067 /** 068 * Determine the ultimate target class of the given bean instance, traversing 069 * not only a top-level proxy but any number of nested proxies as well — 070 * as long as possible without side effects, that is, just for singleton targets. 071 * @param candidate the instance to check (might be an AOP proxy) 072 * @return the ultimate target class (or the plain class of the given 073 * object as fallback; never {@code null}) 074 * @see org.springframework.aop.TargetClassAware#getTargetClass() 075 * @see Advised#getTargetSource() 076 */ 077 public static Class<?> ultimateTargetClass(Object candidate) { 078 Assert.notNull(candidate, "Candidate object must not be null"); 079 Object current = candidate; 080 Class<?> result = null; 081 while (current instanceof TargetClassAware) { 082 result = ((TargetClassAware) current).getTargetClass(); 083 current = getSingletonTarget(current); 084 } 085 if (result == null) { 086 result = (AopUtils.isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass()); 087 } 088 return result; 089 } 090 091 /** 092 * Determine the complete set of interfaces to proxy for the given AOP configuration. 093 * <p>This will always add the {@link Advised} interface unless the AdvisedSupport's 094 * {@link AdvisedSupport#setOpaque "opaque"} flag is on. Always adds the 095 * {@link org.springframework.aop.SpringProxy} marker interface. 096 * @param advised the proxy config 097 * @return the complete set of interfaces to proxy 098 * @see SpringProxy 099 * @see Advised 100 */ 101 public static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised) { 102 return completeProxiedInterfaces(advised, false); 103 } 104 105 /** 106 * Determine the complete set of interfaces to proxy for the given AOP configuration. 107 * <p>This will always add the {@link Advised} interface unless the AdvisedSupport's 108 * {@link AdvisedSupport#setOpaque "opaque"} flag is on. Always adds the 109 * {@link org.springframework.aop.SpringProxy} marker interface. 110 * @param advised the proxy config 111 * @param decoratingProxy whether to expose the {@link DecoratingProxy} interface 112 * @return the complete set of interfaces to proxy 113 * @since 4.3 114 * @see SpringProxy 115 * @see Advised 116 * @see DecoratingProxy 117 */ 118 static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) { 119 Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces(); 120 if (specifiedInterfaces.length == 0) { 121 // No user-specified interfaces: check whether target class is an interface. 122 Class<?> targetClass = advised.getTargetClass(); 123 if (targetClass != null) { 124 if (targetClass.isInterface()) { 125 advised.setInterfaces(targetClass); 126 } 127 else if (Proxy.isProxyClass(targetClass)) { 128 advised.setInterfaces(targetClass.getInterfaces()); 129 } 130 specifiedInterfaces = advised.getProxiedInterfaces(); 131 } 132 } 133 boolean addSpringProxy = !advised.isInterfaceProxied(SpringProxy.class); 134 boolean addAdvised = !advised.isOpaque() && !advised.isInterfaceProxied(Advised.class); 135 boolean addDecoratingProxy = (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class)); 136 int nonUserIfcCount = 0; 137 if (addSpringProxy) { 138 nonUserIfcCount++; 139 } 140 if (addAdvised) { 141 nonUserIfcCount++; 142 } 143 if (addDecoratingProxy) { 144 nonUserIfcCount++; 145 } 146 Class<?>[] proxiedInterfaces = new Class<?>[specifiedInterfaces.length + nonUserIfcCount]; 147 System.arraycopy(specifiedInterfaces, 0, proxiedInterfaces, 0, specifiedInterfaces.length); 148 int index = specifiedInterfaces.length; 149 if (addSpringProxy) { 150 proxiedInterfaces[index] = SpringProxy.class; 151 index++; 152 } 153 if (addAdvised) { 154 proxiedInterfaces[index] = Advised.class; 155 index++; 156 } 157 if (addDecoratingProxy) { 158 proxiedInterfaces[index] = DecoratingProxy.class; 159 } 160 return proxiedInterfaces; 161 } 162 163 /** 164 * Extract the user-specified interfaces that the given proxy implements, 165 * i.e. all non-Advised interfaces that the proxy implements. 166 * @param proxy the proxy to analyze (usually a JDK dynamic proxy) 167 * @return all user-specified interfaces that the proxy implements, 168 * in the original order (never {@code null} or empty) 169 * @see Advised 170 */ 171 public static Class<?>[] proxiedUserInterfaces(Object proxy) { 172 Class<?>[] proxyInterfaces = proxy.getClass().getInterfaces(); 173 int nonUserIfcCount = 0; 174 if (proxy instanceof SpringProxy) { 175 nonUserIfcCount++; 176 } 177 if (proxy instanceof Advised) { 178 nonUserIfcCount++; 179 } 180 if (proxy instanceof DecoratingProxy) { 181 nonUserIfcCount++; 182 } 183 Class<?>[] userInterfaces = Arrays.copyOf(proxyInterfaces, proxyInterfaces.length - nonUserIfcCount); 184 Assert.notEmpty(userInterfaces, "JDK proxy must implement one or more interfaces"); 185 return userInterfaces; 186 } 187 188 /** 189 * Check equality of the proxies behind the given AdvisedSupport objects. 190 * Not the same as equality of the AdvisedSupport objects: 191 * rather, equality of interfaces, advisors and target sources. 192 */ 193 public static boolean equalsInProxy(AdvisedSupport a, AdvisedSupport b) { 194 return (a == b || 195 (equalsProxiedInterfaces(a, b) && equalsAdvisors(a, b) && a.getTargetSource().equals(b.getTargetSource()))); 196 } 197 198 /** 199 * Check equality of the proxied interfaces behind the given AdvisedSupport objects. 200 */ 201 public static boolean equalsProxiedInterfaces(AdvisedSupport a, AdvisedSupport b) { 202 return Arrays.equals(a.getProxiedInterfaces(), b.getProxiedInterfaces()); 203 } 204 205 /** 206 * Check equality of the advisors behind the given AdvisedSupport objects. 207 */ 208 public static boolean equalsAdvisors(AdvisedSupport a, AdvisedSupport b) { 209 return Arrays.equals(a.getAdvisors(), b.getAdvisors()); 210 } 211 212 213 /** 214 * Adapt the given arguments to the target signature in the given method, 215 * if necessary: in particular, if a given vararg argument array does not 216 * match the array type of the declared vararg parameter in the method. 217 * @param method the target method 218 * @param arguments the given arguments 219 * @return a cloned argument array, or the original if no adaptation is needed 220 * @since 4.2.3 221 */ 222 static Object[] adaptArgumentsIfNecessary(Method method, @Nullable Object[] arguments) { 223 if (ObjectUtils.isEmpty(arguments)) { 224 return new Object[0]; 225 } 226 if (method.isVarArgs()) { 227 if (method.getParameterCount() == arguments.length) { 228 Class<?>[] paramTypes = method.getParameterTypes(); 229 int varargIndex = paramTypes.length - 1; 230 Class<?> varargType = paramTypes[varargIndex]; 231 if (varargType.isArray()) { 232 Object varargArray = arguments[varargIndex]; 233 if (varargArray instanceof Object[] && !varargType.isInstance(varargArray)) { 234 Object[] newArguments = new Object[arguments.length]; 235 System.arraycopy(arguments, 0, newArguments, 0, varargIndex); 236 Class<?> targetElementType = varargType.getComponentType(); 237 int varargLength = Array.getLength(varargArray); 238 Object newVarargArray = Array.newInstance(targetElementType, varargLength); 239 System.arraycopy(varargArray, 0, newVarargArray, 0, varargLength); 240 newArguments[varargIndex] = newVarargArray; 241 return newArguments; 242 } 243 } 244 } 245 } 246 return arguments; 247 } 248 249}