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}