📄 abstractaspectjadvice.java
字号:
* <p>If the first argument is of type JoinPoint or ProceedingJoinPoint then we
* pass a JoinPoint in that position (ProceedingJoinPoint for around advice).
* <p>If the first argument is of type <code>JoinPoint.StaticPart</code>
* then we pass a <code>JoinPoint.StaticPart</code> in that position.
* <p>Remaining arguments have to be bound by pointcut evaluation at
* a given join point. We will get back a map from argument name to
* value. We need to calculate which advice parameter needs to be bound
* to which argument name. There are multiple strategies for determining
* this binding, which are arranged in a ChainOfResponsibility.
*/
public synchronized final void calculateArgumentBindings() {
// The simple case... nothing to bind.
if (this.argumentsIntrospected || this.adviceInvocationArgumentCount == 0) {
return;
}
int numUnboundArgs = this.adviceInvocationArgumentCount;
Class[] parameterTypes = this.aspectJAdviceMethod.getParameterTypes();
if (maybeBindJoinPoint(parameterTypes[0])) {
numUnboundArgs--;
}
else if (maybeBindJoinPointStaticPart(parameterTypes[0])) {
numUnboundArgs--;
}
if (numUnboundArgs > 0) {
// need to bind arguments by name as returned from the pointcut match
bindArgumentsByName(numUnboundArgs);
}
this.argumentsIntrospected = true;
}
private boolean maybeBindJoinPoint(Class candidateParameterType) {
if (candidateParameterType.equals(JoinPoint.class) || candidateParameterType.equals(ProceedingJoinPoint.class)) {
this.joinPointArgumentIndex = 0;
return true;
}
else {
return false;
}
}
private boolean maybeBindJoinPointStaticPart(Class candidateParameterType) {
if (candidateParameterType.equals(JoinPoint.StaticPart.class)) {
this.joinPointStaticPartArgumentIndex = 0;
return true;
}
else {
return false;
}
}
private void bindArgumentsByName(int numArgumentsExpectingToBind) {
if (this.argumentNames == null) {
this.argumentNames = createParameterNameDiscoverer().getParameterNames(this.aspectJAdviceMethod);
}
if (this.argumentNames != null) {
// We have been able to determine the arg names.
bindExplicitArguments(numArgumentsExpectingToBind);
}
else {
throw new IllegalStateException("Advice method [" + this.aspectJAdviceMethod.getName() + "] " +
"requires " + numArgumentsExpectingToBind + " arguments to be bound by name, but " +
"the argument names were not specified and could not be discovered.");
}
}
/**
* Create a ParameterNameDiscoverer to be used for argument binding.
* <p>The default implementation creates a {@link PrioritizedParameterNameDiscoverer}
* containing a {@link LocalVariableTableParameterNameDiscoverer} and an
* {@link AspectJAdviceParameterNameDiscoverer}.
*/
protected ParameterNameDiscoverer createParameterNameDiscoverer() {
// We need to discover them, or if that fails, guess,
// and if we can't guess with 100% accuracy, fail.
PrioritizedParameterNameDiscoverer discoverer = new PrioritizedParameterNameDiscoverer();
if (ASM_AVAILABLE) {
discoverer.addDiscoverer(new LocalVariableTableParameterNameDiscoverer());
}
AspectJAdviceParameterNameDiscoverer adviceParameterNameDiscoverer =
new AspectJAdviceParameterNameDiscoverer(this.pointcut.getExpression());
adviceParameterNameDiscoverer.setReturningName(this.returningName);
adviceParameterNameDiscoverer.setThrowingName(this.throwingName);
// Last in chain, so if we're called and we fail, that's bad...
adviceParameterNameDiscoverer.setRaiseExceptions(true);
discoverer.addDiscoverer(adviceParameterNameDiscoverer);
return discoverer;
}
private void bindExplicitArguments(int numArgumentsLeftToBind) {
this.argumentBindings = new HashMap();
int numExpectedArgumentNames = this.aspectJAdviceMethod.getParameterTypes().length;
if (this.argumentNames.length != numExpectedArgumentNames) {
throw new IllegalStateException("Expecting to find " + numExpectedArgumentNames
+ " arguments to bind by name in advice, but actually found " +
this.argumentNames.length + " arguments.");
}
// So we match in number...
int argumentIndexOffset = this.adviceInvocationArgumentCount - numArgumentsLeftToBind;
for (int i = argumentIndexOffset; i < this.argumentNames.length; i++) {
this.argumentBindings.put(this.argumentNames[i],new Integer(i));
}
// Check that returning and throwing were in the argument names list if
// specified, and find the discovered argument types.
if (this.returningName != null) {
if (!this.argumentBindings.containsKey(this.returningName)) {
throw new IllegalStateException("Returning argument name '"
+ this.returningName + "' was not bound in advice arguments");
}
else {
Integer index = (Integer) this.argumentBindings.get(this.returningName);
this.discoveredReturningType = this.aspectJAdviceMethod.getParameterTypes()[index.intValue()];
if (JdkVersion.isAtLeastJava15()) {
this.discoveredReturningGenericType =
this.aspectJAdviceMethod.getGenericParameterTypes()[index.intValue()];
}
}
}
if (this.throwingName != null) {
if (!this.argumentBindings.containsKey(this.throwingName)) {
throw new IllegalStateException("Throwing argument name '"
+ this.throwingName + "' was not bound in advice arguments");
}
else {
Integer index = (Integer) this.argumentBindings.get(this.throwingName);
this.discoveredThrowingType = this.aspectJAdviceMethod.getParameterTypes()[index.intValue()];
}
}
// configure the pointcut expression accordingly.
configurePointcutParameters(argumentIndexOffset);
}
/**
* All parameters from argumentIndexOffset onwards are candidates for
* pointcut parameters - but returning and throwing vars are handled differently
* and must be removed from the list if present.
*/
private void configurePointcutParameters(int argumentIndexOffset) {
int numParametersToRemove = argumentIndexOffset;
if (this.returningName != null) {
numParametersToRemove++;
}
if (this.throwingName != null) {
numParametersToRemove++;
}
String[] pointcutParameterNames = new String[this.argumentNames.length - numParametersToRemove];
Class[] pointcutParameterTypes = new Class[pointcutParameterNames.length];
Class[] methodParameterTypes = this.aspectJAdviceMethod.getParameterTypes();
int index = 0;
for (int i = 0; i < this.argumentNames.length; i++) {
if (i < argumentIndexOffset) {
continue;
}
if (this.argumentNames[i].equals(this.returningName) ||
this.argumentNames[i].equals(this.throwingName)) {
continue;
}
pointcutParameterNames[index] = this.argumentNames[i];
pointcutParameterTypes[index] = methodParameterTypes[i];
index++;
}
this.pointcut.setParameterNames(pointcutParameterNames);
this.pointcut.setParameterTypes(pointcutParameterTypes);
}
/**
* Take the arguments at the method execution join point and output a set of arguments
* to the advice method
* @param jp the current JoinPoint
* @param jpMatch the join point match that matched this execution join point
* @param returnValue the return value from the method execution (may be null)
* @param ex the exception thrown by the method execution (may be null)
* @return the empty array if there are no arguments
*/
protected Object[] argBinding(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable ex) {
calculateArgumentBindings();
// AMC start
Object[] adviceInvocationArgs = new Object[this.adviceInvocationArgumentCount];
int numBound = 0;
if (this.joinPointArgumentIndex != -1) {
adviceInvocationArgs[this.joinPointArgumentIndex] = jp;
numBound++;
}
else if (this.joinPointStaticPartArgumentIndex != -1) {
adviceInvocationArgs[this.joinPointStaticPartArgumentIndex] = jp.getStaticPart();
numBound++;
}
if (!CollectionUtils.isEmpty(this.argumentBindings)) {
// binding from pointcut match
if (jpMatch != null) {
PointcutParameter[] parameterBindings = jpMatch.getParameterBindings();
for (int i = 0; i < parameterBindings.length; i++) {
PointcutParameter parameter = parameterBindings[i];
String name = parameter.getName();
Integer index = (Integer) this.argumentBindings.get(name);
adviceInvocationArgs[index.intValue()] = parameter.getBinding();
numBound++;
}
}
// binding from returning clause
if (this.returningName != null) {
Integer index = (Integer) this.argumentBindings.get(this.returningName);
adviceInvocationArgs[index.intValue()] = returnValue;
numBound++;
}
// binding from thrown exception
if (this.throwingName != null) {
Integer index = (Integer) this.argumentBindings.get(this.throwingName);
adviceInvocationArgs[index.intValue()] = ex;
numBound++;
}
}
if (numBound != this.adviceInvocationArgumentCount) {
throw new IllegalStateException("Required to bind " + this.adviceInvocationArgumentCount
+ " arguments, but only bound " + numBound + " (JoinPointMatch " +
(jpMatch == null ? "was NOT" : "WAS") +
" bound in invocation)");
}
return adviceInvocationArgs;
}
/**
* Invoke the advice method.
* @param jpMatch the JoinPointMatch that matched this execution join point
* @param returnValue the return value from the method execution (may be null)
* @param ex the exception thrown by the method execution (may be null)
* @return the invocation result
* @throws Throwable in case of invocation failure
*/
protected Object invokeAdviceMethod(JoinPointMatch jpMatch, Object returnValue, Throwable ex) throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(getJoinPoint(), jpMatch, returnValue, ex));
}
// As above, but in this case we are given the join point.
protected Object invokeAdviceMethod(JoinPoint jp, JoinPointMatch jpMatch, Object returnValue, Throwable t)
throws Throwable {
return invokeAdviceMethodWithGivenArgs(argBinding(jp, jpMatch, returnValue, t));
}
protected Object invokeAdviceMethodWithGivenArgs(Object[] args) throws Throwable {
Object[] actualArgs = args;
if (this.aspectJAdviceMethod.getParameterTypes().length == 0) {
actualArgs = null;
}
try {
if (!Modifier.isPublic(this.aspectJAdviceMethod.getModifiers()) ||
!Modifier.isPublic(this.aspectJAdviceMethod.getDeclaringClass().getModifiers())) {
this.aspectJAdviceMethod.setAccessible(true);
}
// TODO AopUtils.invokeJoinpointUsingReflection
return this.aspectJAdviceMethod.invoke(this.aspectInstanceFactory.getAspectInstance(), actualArgs);
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("Mismatch on arguments to advice method [" +
this.aspectJAdviceMethod + "]; pointcut expression [" +
this.pointcut.getPointcutExpression() + "]", ex);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
/**
* Overridden in around advice to return proceeding join point.
*/
protected JoinPoint getJoinPoint() {
return currentJoinPoint();
}
/**
* Get the current join point match at the join point we are being dispatched on.
*/
protected JoinPointMatch getJoinPointMatch() {
MethodInvocation mi = ExposeInvocationInterceptor.currentInvocation();
if (!(mi instanceof ProxyMethodInvocation)) {
throw new IllegalStateException("MethodInvocation is not a Spring ProxyMethodInvocation: " + mi);
}
return getJoinPointMatch((ProxyMethodInvocation) mi);
}
// Note: We can't use JoinPointMatch.getClass().getName() as the key, since
// Spring AOP does all the matching at a join point, and then all the invocations.
// Under this scenario, if we just use JoinPointMatch as the key, then
// 'last man wins' which is not what we want at all.
// Using the expression is guaranteed to be safe, since 2 identical expressions
// are guaranteed to bind in exactly the same way.
protected JoinPointMatch getJoinPointMatch(ProxyMethodInvocation pmi) {
return (JoinPointMatch) pmi.getUserAttribute(this.pointcut.getExpression());
}
public String toString() {
return getClass().getName() + ": advice method [" + this.aspectJAdviceMethod + "]; " +
"aspect name '" + this.aspectName + "'";
}
/**
* MethodMatcher that excludes the specified advice method.
* @see AbstractAspectJAdvice#buildSafePointcut()
*/
private static class AdviceExcludingMethodMatcher extends StaticMethodMatcher {
private final Method adviceMethod;
public AdviceExcludingMethodMatcher(Method adviceMethod) {
this.adviceMethod = adviceMethod;
}
public boolean matches(Method method, Class targetClass) {
return !this.adviceMethod.equals(method);
}
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -