001/*
002 * Copyright 2002-2020 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.aspectj;
018
019import java.lang.reflect.Method;
020import java.lang.reflect.Modifier;
021
022import org.aspectj.lang.JoinPoint;
023import org.aspectj.lang.ProceedingJoinPoint;
024import org.aspectj.lang.Signature;
025import org.aspectj.lang.reflect.MethodSignature;
026import org.aspectj.lang.reflect.SourceLocation;
027import org.aspectj.runtime.internal.AroundClosure;
028
029import org.springframework.aop.ProxyMethodInvocation;
030import org.springframework.core.DefaultParameterNameDiscoverer;
031import org.springframework.core.ParameterNameDiscoverer;
032import org.springframework.util.Assert;
033
034/**
035 * An implementation of the AspectJ {@link ProceedingJoinPoint} interface
036 * wrapping an AOP Alliance {@link org.aopalliance.intercept.MethodInvocation}.
037 *
038 * <p><b>Note</b>: The {@code getThis()} method returns the current Spring AOP proxy.
039 * The {@code getTarget()} method returns the current Spring AOP target (which may be
040 * {@code null} if there is no target instance) as a plain POJO without any advice.
041 * <b>If you want to call the object and have the advice take effect, use {@code getThis()}.</b>
042 * A common example is casting the object to an introduced interface in the implementation of
043 * an introduction. There is no such distinction between target and proxy in AspectJ itself.
044 *
045 * @author Rod Johnson
046 * @author Juergen Hoeller
047 * @author Adrian Colyer
048 * @author Ramnivas Laddad
049 * @since 2.0
050 */
051public class MethodInvocationProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
052
053        private static final ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
054
055        private final ProxyMethodInvocation methodInvocation;
056
057        private Object[] args;
058
059        /** Lazily initialized signature object */
060        private Signature signature;
061
062        /** Lazily initialized source location object */
063        private SourceLocation sourceLocation;
064
065
066        /**
067         * Create a new MethodInvocationProceedingJoinPoint, wrapping the given
068         * Spring ProxyMethodInvocation object.
069         * @param methodInvocation the Spring ProxyMethodInvocation object
070         */
071        public MethodInvocationProceedingJoinPoint(ProxyMethodInvocation methodInvocation) {
072                Assert.notNull(methodInvocation, "MethodInvocation must not be null");
073                this.methodInvocation = methodInvocation;
074        }
075
076
077        @Override
078        public void set$AroundClosure(AroundClosure aroundClosure) {
079                throw new UnsupportedOperationException();
080        }
081
082        @Override
083        public Object proceed() throws Throwable {
084                return this.methodInvocation.invocableClone().proceed();
085        }
086
087        @Override
088        public Object proceed(Object[] arguments) throws Throwable {
089                Assert.notNull(arguments, "Argument array passed to proceed cannot be null");
090                if (arguments.length != this.methodInvocation.getArguments().length) {
091                        throw new IllegalArgumentException("Expecting " +
092                                        this.methodInvocation.getArguments().length + " arguments to proceed, " +
093                                        "but was passed " + arguments.length + " arguments");
094                }
095                this.methodInvocation.setArguments(arguments);
096                return this.methodInvocation.invocableClone(arguments).proceed();
097        }
098
099        /**
100         * Returns the Spring AOP proxy. Cannot be {@code null}.
101         */
102        @Override
103        public Object getThis() {
104                return this.methodInvocation.getProxy();
105        }
106
107        /**
108         * Returns the Spring AOP target. May be {@code null} if there is no target.
109         */
110        @Override
111        public Object getTarget() {
112                return this.methodInvocation.getThis();
113        }
114
115        @Override
116        public Object[] getArgs() {
117                if (this.args == null) {
118                        this.args = this.methodInvocation.getArguments().clone();
119                }
120                return this.args;
121        }
122
123        @Override
124        public Signature getSignature() {
125                if (this.signature == null) {
126                        this.signature = new MethodSignatureImpl();
127                }
128                return this.signature;
129        }
130
131        @Override
132        public SourceLocation getSourceLocation() {
133                if (this.sourceLocation == null) {
134                        this.sourceLocation = new SourceLocationImpl();
135                }
136                return this.sourceLocation;
137        }
138
139        @Override
140        public String getKind() {
141                return ProceedingJoinPoint.METHOD_EXECUTION;
142        }
143
144        @Override
145        public int getId() {
146                // TODO: It's just an adapter but returning 0 might still have side effects...
147                return 0;
148        }
149
150        @Override
151        public JoinPoint.StaticPart getStaticPart() {
152                return this;
153        }
154
155        @Override
156        public String toShortString() {
157                return "execution(" + getSignature().toShortString() + ")";
158        }
159
160        @Override
161        public String toLongString() {
162                return "execution(" + getSignature().toLongString() + ")";
163        }
164
165        @Override
166        public String toString() {
167                return "execution(" + getSignature().toString() + ")";
168        }
169
170
171        /**
172         * Lazily initialized MethodSignature.
173         */
174        private class MethodSignatureImpl implements MethodSignature {
175
176                private volatile String[] parameterNames;
177
178                @Override
179                public String getName() {
180                        return methodInvocation.getMethod().getName();
181                }
182
183                @Override
184                public int getModifiers() {
185                        return methodInvocation.getMethod().getModifiers();
186                }
187
188                @Override
189                public Class<?> getDeclaringType() {
190                        return methodInvocation.getMethod().getDeclaringClass();
191                }
192
193                @Override
194                public String getDeclaringTypeName() {
195                        return methodInvocation.getMethod().getDeclaringClass().getName();
196                }
197
198                @Override
199                public Class<?> getReturnType() {
200                        return methodInvocation.getMethod().getReturnType();
201                }
202
203                @Override
204                public Method getMethod() {
205                        return methodInvocation.getMethod();
206                }
207
208                @Override
209                public Class<?>[] getParameterTypes() {
210                        return methodInvocation.getMethod().getParameterTypes();
211                }
212
213                @Override
214                public String[] getParameterNames() {
215                        String[] parameterNames = this.parameterNames;
216                        if (parameterNames == null) {
217                                parameterNames = parameterNameDiscoverer.getParameterNames(getMethod());
218                                this.parameterNames = parameterNames;
219                        }
220                        return parameterNames;
221                }
222
223                @Override
224                public Class<?>[] getExceptionTypes() {
225                        return methodInvocation.getMethod().getExceptionTypes();
226                }
227
228                @Override
229                public String toShortString() {
230                        return toString(false, false, false, false);
231                }
232
233                @Override
234                public String toLongString() {
235                        return toString(true, true, true, true);
236                }
237
238                @Override
239                public String toString() {
240                        return toString(false, true, false, true);
241                }
242
243                private String toString(boolean includeModifier, boolean includeReturnTypeAndArgs,
244                                boolean useLongReturnAndArgumentTypeName, boolean useLongTypeName) {
245
246                        StringBuilder sb = new StringBuilder();
247                        if (includeModifier) {
248                                sb.append(Modifier.toString(getModifiers()));
249                                sb.append(" ");
250                        }
251                        if (includeReturnTypeAndArgs) {
252                                appendType(sb, getReturnType(), useLongReturnAndArgumentTypeName);
253                                sb.append(" ");
254                        }
255                        appendType(sb, getDeclaringType(), useLongTypeName);
256                        sb.append(".");
257                        sb.append(getMethod().getName());
258                        sb.append("(");
259                        Class<?>[] parametersTypes = getParameterTypes();
260                        appendTypes(sb, parametersTypes, includeReturnTypeAndArgs, useLongReturnAndArgumentTypeName);
261                        sb.append(")");
262                        return sb.toString();
263                }
264
265                private void appendTypes(StringBuilder sb, Class<?>[] types, boolean includeArgs,
266                                boolean useLongReturnAndArgumentTypeName) {
267
268                        if (includeArgs) {
269                                for (int size = types.length, i = 0; i < size; i++) {
270                                        appendType(sb, types[i], useLongReturnAndArgumentTypeName);
271                                        if (i < size - 1) {
272                                                sb.append(",");
273                                        }
274                                }
275                        }
276                        else {
277                                if (types.length != 0) {
278                                        sb.append("..");
279                                }
280                        }
281                }
282
283                private void appendType(StringBuilder sb, Class<?> type, boolean useLongTypeName) {
284                        if (type.isArray()) {
285                                appendType(sb, type.getComponentType(), useLongTypeName);
286                                sb.append("[]");
287                        }
288                        else {
289                                sb.append(useLongTypeName ? type.getName() : type.getSimpleName());
290                        }
291                }
292        }
293
294
295        /**
296         * Lazily initialized SourceLocation.
297         */
298        private class SourceLocationImpl implements SourceLocation {
299
300                @Override
301                public Class<?> getWithinType() {
302                        if (methodInvocation.getThis() == null) {
303                                throw new UnsupportedOperationException("No source location joinpoint available: target is null");
304                        }
305                        return methodInvocation.getThis().getClass();
306                }
307
308                @Override
309                public String getFileName() {
310                        throw new UnsupportedOperationException();
311                }
312
313                @Override
314                public int getLine() {
315                        throw new UnsupportedOperationException();
316                }
317
318                @Override
319                @Deprecated
320                public int getColumn() {
321                        throw new UnsupportedOperationException();
322                }
323        }
324
325}