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