Top Description Inners Fields Constructors Methods
org.springframework.aop.aspectj

public Class AspectJAdviceParameterNameDiscoverer

extends Object
implements ParameterNameDiscoverer
Class Inheritance
All Implemented Interfaces
org.springframework.core.ParameterNameDiscoverer
Imports
java.lang.annotation.Annotation, java.lang.reflect.Constructor, .Method, java.util.ArrayList, .HashSet, .List, .Set, org.aspectj.lang.JoinPoint, .ProceedingJoinPoint, org.aspectj.weaver.tools.PointcutParser, .PointcutPrimitive, org.springframework.core.ParameterNameDiscoverer, org.springframework.lang.Nullable, org.springframework.util.StringUtils

ParameterNameDiscoverer implementation that tries to deduce parameter names for an advice method from the pointcut expression, returning, and throwing clauses. If an unambiguous interpretation is not available, it returns null.

Algorithm Summary

If an unambiguous binding can be deduced, then it is. If the advice requirements cannot possibly be satisfied, then null is returned. By setting the raiseExceptions property to true, descriptive exceptions will be thrown instead of returning null in the case that the parameter names cannot be discovered.

Algorithm Details

This class interprets arguments in the following way:

  1. If the first parameter of the method is of type JoinPoint or ProceedingJoinPoint, it is assumed to be for passing thisJoinPoint to the advice, and the parameter name will be assigned the value "thisJoinPoint".
  2. If the first parameter of the method is of type JoinPoint.StaticPart, it is assumed to be for passing "thisJoinPointStaticPart" to the advice, and the parameter name will be assigned the value "thisJoinPointStaticPart".
  3. If a throwingName has been set, and there are no unbound arguments of type Throwable+, then an IllegalArgumentException is raised. If there is more than one unbound argument of type Throwable+, then an AmbiguousBindingException is raised. If there is exactly one unbound argument of type Throwable+, then the corresponding parameter name is assigned the value <throwingName>.
  4. If there remain unbound arguments, then the pointcut expression is examined. Let a be the number of annotation-based pointcut expressions (@annotation, @this, @target, @args, @within, @withincode) that are used in binding form. Usage in binding form has itself to be deduced: if the expression inside the pointcut is a single string literal that meets Java variable name conventions it is assumed to be a variable name. If a is zero we proceed to the next stage. If a > 1 then an AmbiguousBindingException is raised. If a == 1, and there are no unbound arguments of type Annotation+, then an IllegalArgumentException is raised. If there is exactly one such argument, then the corresponding parameter name is assigned the value from the pointcut expression.
  5. If a returningName has been set, and there are no unbound arguments then an IllegalArgumentException is raised. If there is more than one unbound argument then an AmbiguousBindingException is raised. If there is exactly one unbound argument then the corresponding parameter name is assigned the value of the returningName.
  6. If there remain unbound arguments, then the pointcut expression is examined once more for this, target, and args pointcut expressions used in the binding form (binding forms are deduced as described for the annotation based pointcuts). If there remains more than one unbound argument of a primitive type (which can only be bound in args) then an AmbiguousBindingException is raised. If there is exactly one argument of a primitive type, then if exactly one args bound variable was found, we assign the corresponding parameter name the variable name. If there were no args bound variables found an IllegalStateException is raised. If there are multiple args bound variables, an AmbiguousBindingException is raised. At this point, if there remains more than one unbound argument we raise an AmbiguousBindingException. If there are no unbound arguments remaining, we are done. If there is exactly one unbound argument remaining, and only one candidate variable name unbound from this, target, or args, it is assigned as the corresponding parameter name. If there are multiple possibilities, an AmbiguousBindingException is raised.

The behavior on raising an IllegalArgumentException or AmbiguousBindingException is configurable to allow this discoverer to be used as part of a chain-of-responsibility. By default the condition will be logged and the getParameterNames(Method) method will simply return null. If the raiseExceptions property is set to true, the conditions will be thrown as IllegalArgumentException and AmbiguousBindingException, respectively.

Authors
Adrian Colyer, Juergen Hoeller
Since
2.0

Nested and Inner Type Summary

Modifier and TypeClass and Description
public static class
AspectJAdviceParameterNameDiscoverer.AmbiguousBindingException

Thrown in response to an ambiguous binding being detected when trying to resolve a method's parameter names.

private static record
AspectJAdviceParameterNameDiscoverer.PointcutBody

Simple record to hold the extracted text from a pointcut body, together with the number of tokens consumed in extracting it.

Field Summary

Modifier and TypeField and Description
private Class<?>[]
private static final Set<String>
private int
private String[]
private final String
pointcutExpression

The pointcut expression associated with the advice, as a simple String.

private boolean
private String
returningName

If the advice is afterReturning, and binds the return value, this is the parameter name used.

private static final Set<String>
private static final int
private static final int
private static final int
private static final int
private static final int
private static final int
private static final int
private static final int
private static final String
private static final String
private String
throwingName

If the advice is afterThrowing, and binds the thrown value, this is the parameter name used.

Constructor Summary

AccessConstructor and Description
public
AspectJAdviceParameterNameDiscoverer(String pointcutExpression)

Create a new discoverer that attempts to discover parameter names.

Method Summary

Modifier and TypeMethod and Description
private boolean
private void
bindAnnotationsFromVarNames(List<String> varNames)

Match the given list of extracted variable names to argument slots.

private void
bindParameterName(int index, String name)

private int
private int
private void
findAndBind(Class<?> argumentType, String varName)

Find the argument index with the given type, and bind the given varName in that position.

public String[]

Returns:

the parameter names
getParameterNames
(Method
the target Method
method
)

Implements org.springframework.core.ParameterNameDiscoverer.getParameterNames.

Deduce the parameter names for an advice method.

public String[]

Returns:

null
getParameterNames
(Constructor<?>
the constructor to find parameter names for
ctor
)

Implements org.springframework.core.ParameterNameDiscoverer.getParameterNames.

An advice method can never be a constructor in Spring.

private AspectJAdviceParameterNameDiscoverer.PointcutBody
getPointcutBody(String[] tokens, int startIndex)

We've found the start of a binding pointcut at the given index into the token array.

private boolean
isSubtypeOf(Class<?> supertype, int argumentNumber)

Return true if the given argument type is a subclass of the given supertype.

private boolean
isUnbound(int i)

private void
maybeBindAnnotationsFromPointcutExpression()

Parse the string pointcut expression looking for: @this, @target, @args, @within, @withincode, @annotation.

private void
maybeBindPrimitiveArgsFromPointcutExpression()

Match up args against unbound arguments of primitive types.

private void
private void
maybeBindReturningVariable()

If a returning variable was specified and there is only one choice remaining, bind it.

private boolean
maybeBindThisJoinPoint()

If the first parameter is of type JoinPoint or ProceedingJoinPoint, bind "thisJoinPoint" as parameter name and return true, else return false.

private void
private void
maybeBindThisOrTargetOrArgsFromPointcutExpression()

Parse the string pointcut expression looking for this(), target() and args() expressions.

private void
maybeBindThrowingVariable()

If a throwing name was specified and there is exactly one choice remaining (argument that is a subtype of Throwable) then bind it.

private String
maybeExtractVariableName(String candidateToken)

If the token starts meets Java identifier conventions, it's in.

private void
maybeExtractVariableNamesFromArgs(String argsSpec, List<String> varNames)

Given an args pointcut body (could be args or at_args), add any candidate variable names to the given list.

public void
setRaiseExceptions(boolean
true if exceptions are to be thrown
raiseExceptions
)

Indicate whether IllegalArgumentException and AmbiguousBindingException must be thrown as appropriate in the case of failing to deduce advice parameter names.

public void
setReturningName(String
the name of the returning variable
returningName
)

If afterReturning advice binds the return value, the returning variable name must be specified.

public void
setThrowingName(String
the name of the throwing variable
throwingName
)

If afterThrowing advice binds the thrown value, the throwing variable name must be specified.

Inherited from java.lang.Object:
cloneequalsfinalizegetClasshashCodenotifynotifyAlltoStringwaitwaitwait

Field Detail

argumentTypesback to summary
private Class<?>[] argumentTypes
nonReferencePointcutTokensback to summary
private static final Set<String> nonReferencePointcutTokens
numberOfRemainingUnboundArgumentsback to summary
private int numberOfRemainingUnboundArguments
parameterNameBindingsback to summary
private String[] parameterNameBindings
pointcutExpressionback to summary
private final String pointcutExpression

The pointcut expression associated with the advice, as a simple String.

Annotations
@Nullable
raiseExceptionsback to summary
private boolean raiseExceptions
returningNameback to summary
private String returningName

If the advice is afterReturning, and binds the return value, this is the parameter name used.

Annotations
@Nullable
singleValuedAnnotationPcdsback to summary
private static final Set<String> singleValuedAnnotationPcds
STEP_ANNOTATION_BINDINGback to summary
private static final int STEP_ANNOTATION_BINDING
STEP_FINISHEDback to summary
private static final int STEP_FINISHED
STEP_JOIN_POINT_BINDINGback to summary
private static final int STEP_JOIN_POINT_BINDING
STEP_PRIMITIVE_ARGS_BINDINGback to summary
private static final int STEP_PRIMITIVE_ARGS_BINDING
STEP_REFERENCE_PCUT_BINDINGback to summary
private static final int STEP_REFERENCE_PCUT_BINDING
STEP_RETURNING_BINDINGback to summary
private static final int STEP_RETURNING_BINDING
STEP_THIS_TARGET_ARGS_BINDINGback to summary
private static final int STEP_THIS_TARGET_ARGS_BINDING
STEP_THROWING_BINDINGback to summary
private static final int STEP_THROWING_BINDING
THIS_JOIN_POINTback to summary
private static final String THIS_JOIN_POINT
THIS_JOIN_POINT_STATIC_PARTback to summary
private static final String THIS_JOIN_POINT_STATIC_PART
throwingNameback to summary
private String throwingName

If the advice is afterThrowing, and binds the thrown value, this is the parameter name used.

Annotations
@Nullable

Constructor Detail

AspectJAdviceParameterNameDiscovererback to summary
public AspectJAdviceParameterNameDiscoverer(String pointcutExpression)

Create a new discoverer that attempts to discover parameter names. from the given pointcut expression.

Method Detail

alreadyBoundback to summary
private boolean alreadyBound(String varName)
bindAnnotationsFromVarNamesback to summary
private void bindAnnotationsFromVarNames(List<String> varNames)

Match the given list of extracted variable names to argument slots.

bindParameterNameback to summary
private void bindParameterName(int index, String name)
countNumberOfUnboundAnnotationArgumentsback to summary
private int countNumberOfUnboundAnnotationArguments()
countNumberOfUnboundPrimitiveArgumentsback to summary
private int countNumberOfUnboundPrimitiveArguments()
findAndBindback to summary
private void findAndBind(Class<?> argumentType, String varName)

Find the argument index with the given type, and bind the given varName in that position.

getParameterNamesback to summary
public String[] getParameterNames(Method method)

Implements org.springframework.core.ParameterNameDiscoverer.getParameterNames.

Deduce the parameter names for an advice method.

See the class-level javadoc for this class for details on the algorithm used.

Parameters
method:Method

the target Method

Returns:String[]

the parameter names

Annotations
@Override
@Nullable
getParameterNamesback to summary
public String[] getParameterNames(Constructor<?> ctor)

Implements org.springframework.core.ParameterNameDiscoverer.getParameterNames.

An advice method can never be a constructor in Spring.

Parameters
ctor:Constructor<?>

Doc from org.springframework.core.ParameterNameDiscoverer.getParameterNames.

the constructor to find parameter names for

Returns:String[]

null

Annotations
@Override
@Nullable
Exceptions
UnsupportedOperationException:
if raiseExceptions has been set to true
getPointcutBodyback to summary
private AspectJAdviceParameterNameDiscoverer.PointcutBody getPointcutBody(String[] tokens, int startIndex)

We've found the start of a binding pointcut at the given index into the token array. Now we need to extract the pointcut body and return it.

isSubtypeOfback to summary
private boolean isSubtypeOf(Class<?> supertype, int argumentNumber)

Return true if the given argument type is a subclass of the given supertype.

isUnboundback to summary
private boolean isUnbound(int i)
maybeBindAnnotationsFromPointcutExpressionback to summary
private void maybeBindAnnotationsFromPointcutExpression()

Parse the string pointcut expression looking for: @this, @target, @args, @within, @withincode, @annotation. If we find one of these pointcut expressions, try and extract a candidate variable name (or variable names, in the case of args).

Some more support from AspectJ in doing this exercise would be nice... :)

maybeBindPrimitiveArgsFromPointcutExpressionback to summary
private void maybeBindPrimitiveArgsFromPointcutExpression()

Match up args against unbound arguments of primitive types.

maybeBindReferencePointcutParameterback to summary
private void maybeBindReferencePointcutParameter()
maybeBindReturningVariableback to summary
private void maybeBindReturningVariable()

If a returning variable was specified and there is only one choice remaining, bind it.

maybeBindThisJoinPointback to summary
private boolean maybeBindThisJoinPoint()

If the first parameter is of type JoinPoint or ProceedingJoinPoint, bind "thisJoinPoint" as parameter name and return true, else return false.

maybeBindThisJoinPointStaticPartback to summary
private void maybeBindThisJoinPointStaticPart()
maybeBindThisOrTargetOrArgsFromPointcutExpressionback to summary
private void maybeBindThisOrTargetOrArgsFromPointcutExpression()

Parse the string pointcut expression looking for this(), target() and args() expressions. If we find one, try and extract a candidate variable name and bind it.

maybeBindThrowingVariableback to summary
private void maybeBindThrowingVariable()

If a throwing name was specified and there is exactly one choice remaining (argument that is a subtype of Throwable) then bind it.

maybeExtractVariableNameback to summary
private String maybeExtractVariableName(String candidateToken)

If the token starts meets Java identifier conventions, it's in.

Annotations
@Nullable
maybeExtractVariableNamesFromArgsback to summary
private void maybeExtractVariableNamesFromArgs(String argsSpec, List<String> varNames)

Given an args pointcut body (could be args or at_args), add any candidate variable names to the given list.

setRaiseExceptionsback to summary
public void setRaiseExceptions(boolean raiseExceptions)

Indicate whether IllegalArgumentException and AmbiguousBindingException must be thrown as appropriate in the case of failing to deduce advice parameter names.

Parameters
raiseExceptions:boolean

true if exceptions are to be thrown

setReturningNameback to summary
public void setReturningName(String returningName)

If afterReturning advice binds the return value, the returning variable name must be specified.

Parameters
returningName:String

the name of the returning variable

setThrowingNameback to summary
public void setThrowingName(String throwingName)

If afterThrowing advice binds the thrown value, the throwing variable name must be specified.

Parameters
throwingName:String

the name of the throwing variable

org.springframework.aop.aspectj back to summary

public Class AspectJAdviceParameterNameDiscoverer.AmbiguousBindingException

extends RuntimeException
Class Inheritance
Annotations
@SuppressWarnings:serial

Thrown in response to an ambiguous binding being detected when trying to resolve a method's parameter names.

Constructor Summary

AccessConstructor and Description
public
AmbiguousBindingException(String
the detail message
msg
)

Construct a new AmbiguousBindingException with the specified message.

Constructor Detail

AmbiguousBindingExceptionback to summary
public AmbiguousBindingException(String msg)

Construct a new AmbiguousBindingException with the specified message.

Parameters
msg:String

the detail message

org.springframework.aop.aspectj back to summary

private final Record AspectJAdviceParameterNameDiscoverer.PointcutBody

extends Record
Class Inheritance
Record Components
numTokensConsumed:int 
text:String 

Simple record to hold the extracted text from a pointcut body, together with the number of tokens consumed in extracting it.

Field Summary

Modifier and TypeField and Description
private final int
numTokensConsumed

Record Component accessed by numTokensConsumed().

private final String
text

Record Component accessed by text().

Constructor Summary

AccessConstructor and Description
private
PointcutBody(int numTokensConsumed, String text)

Method Summary

Modifier and TypeMethod and Description
public final boolean
equals(Object
the reference object with which to compare.
o
)

Implements abstract java.lang.Record.equals.

Indicates whether some other object is "equal to" this one.

public final int
hashCode()

Implements abstract java.lang.Record.hashCode.

Returns a hash code value for the record.

public int
numTokensConsumed()

Record Component getter of numTokensConsumed.

public String
text()

Record Component getter of text.

public final String
toString()

Implements abstract java.lang.Record.toString.

Returns a string representation of the record.

Field Detail

numTokensConsumedback to summary
private final int numTokensConsumed

Record Component accessed by numTokensConsumed().

textback to summary
private final String text

Record Component accessed by text().

Annotations
@Nullable

Constructor Detail

PointcutBodyback to summary
private PointcutBody(int numTokensConsumed, String text)

Method Detail

equalsback to summary
public final boolean equals(Object o)

Implements abstract java.lang.Record.equals.

Doc from java.lang.Record.equals.

Indicates whether some other object is "equal to" this one. In addition to the general contract of Object.equals, record classes must further obey the invariant that when a record instance is "copied" by passing the result of the record component accessor methods to the canonical constructor, as follows:

    R copy = new R(r.c1(), r.c2(), ..., r.cn());
then it must be the case that r.equals(copy).
Parameters
o:Object

the reference object with which to compare.

Returns:boolean

true if this record is equal to the argument; false otherwise.

hashCodeback to summary
public final int hashCode()

Implements abstract java.lang.Record.hashCode.

Doc from java.lang.Record.hashCode.

Returns a hash code value for the record. Obeys the general contract of Object.hashCode. For records, hashing behavior is constrained by the refined contract of Record.equals, so that any two records created from the same components must have the same hash code.

Returns:int

a hash code value for this record.

numTokensConsumedback to summary
public int numTokensConsumed()

Record Component getter of numTokensConsumed.

textback to summary
public String text()

Record Component getter of text.

Annotations
@Nullable
toStringback to summary
public final String toString()

Implements abstract java.lang.Record.toString.

Doc from java.lang.Record.toString.

Returns a string representation of the record. In accordance with the general contract of Object#toString(), the toString method returns a string that "textually represents" this record. The result should be a concise but informative representation that is easy for a person to read.

In addition to this general contract, record classes must further participate in the invariant that any two records which are equal must produce equal strings. This invariant is necessarily relaxed in the rare case where corresponding equal component values might fail to produce equal strings for themselves.

Returns:String

a string representation of the object.