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