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 &mdash;
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}