⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 aspectjadviceparameternamediscoverer.java

📁 struts+spring 源码 希望能给大家带来帮助
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 * Copyright 2002-2006 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.aop.aspectj;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.util.StringUtils;

/**
 * <p>
 * Implementation of ParameterNameDiscover that tries to deduce
 * parameter names for an advice method from the pointcut expression,
 * returning, and throwing clauses. If an unambiguous interpretation is
 * not avaliable, it will return null.
 * </p>
 * <p>
 * This class interprets arguments in the following way:
 * </p>
 * <ol>
 *   <li>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".</li>
 *   <li>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"</li>
 *   <li>If a throwingName has been set, and there are no unbound arguments of type Throwable+, 
 *   then an IllegalArgumentCondition is raised. If there is more than one unbound argument of type
 *   Throwable+, then an AmbiguousBindingCondition is raised. If there is exactly one unbound argument
 *   of type Throwable+, then the corresponding parameter name is assigned the value &lt;throwingName&gt;.
 *   </li>
 *   <li>If there remain unbound arguments, then the pointcut expression is examined. Let <code>a</code>
 *   be the number of annotation-based pointcut expressions (&#64;annotation, &#64;this, &#64;target,
 *   &#64;args, &#64;within, &#64;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 <code>a</code> is zero we proceed to the next stage. If <code>a</code> &gt; 1 then an 
 *   AmbiguousBindingCondition is raised. If <code>a</code> == 1, and there are no unbound arguments of
 *   type Annotation+, then an IllegalArgumentCondition is raised. if there is exactly one such argument,
 *   then the corresponding parameter name is assigned the value from the pointcut expression.
 *   </li>
 *   <li>If a returningName has been set, and there are no unbound arguments
 *   then an IllegalArgumentCondition is raised. If there is more than one unbound argument then 
 *   an AmbiguousBindingCondition is raised. If there is exactly one unbound argument then the 
 *   corresponding parameter name is assigned the value &lt;returningName&gt;.
 *   </li>
 *   </li>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 AmbiguousBindingCondition
 *   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 IllegalStateCondition is raised. If there are multiple args bound variables,
 *   an AmbiguousBindingCondition is raised. At this point, if there remains more than one unbound argument
 *   we raise an AmbiguousBindingCondition. 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
 *   AmbiguousBindingCondition is raised.
 * </ol>
 * <p>The behaviour on raising an IllegalArgumentCondition or AmbiguousBindingConfiguration 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 will simply return null. If the raiseExceptions property is set to true,
 * the conditions will be thrown as IllegalArgumentException and AmbiguousBindingException respectively.</p>
 * 
 * <p>Was that perfectly clear?? ;) </p>
 * <p>Short version: if an unambiguous binding can be deduced, then it is. If the advice requirements cannot
 * possibly be satisfied null is returned. By setting the raiseExceptions property to true, more descriptive
 * exceptions will be thrown instead of returning null in the case that the parameter names cannot be discovered.
 * </p>
 * 
 * @author Adrian Colyer
 * @since 2.0
 */
public class AspectJAdviceParameterNameDiscoverer implements ParameterNameDiscoverer {
	
	private static final String ANNOTATION_CLASS_NAME = "java.lang.annotation.Annotation";

	private static final String THIS_JOIN_POINT = "thisJoinPoint";
	private static final String THIS_JOIN_POINT_STATIC_PART = "thisJoinPointStaticPart";

	// steps in the binding algorithm...
	private static final int STEP_JOIN_POINT_BINDING = 1;
	private static final int STEP_THROWING_BINDING = 2;
	private static final int STEP_ANNOTATION_BINDING = 3;
	private static final int STEP_RETURNING_BINDING = 4;
	private static final int STEP_PRIMITIVE_ARGS_BINDING = 5;
	private static final int STEP_THIS_TARGET_ARGS_BINDING = 6;
	private static final int STEP_FINISHED = 7;
	
	private static final Set singleValuedAnnotationPcds = new HashSet();

	private static Class annotationClass;
	
	static {
		singleValuedAnnotationPcds.add("@this");
		singleValuedAnnotationPcds.add("@target");
		singleValuedAnnotationPcds.add("@within");
		singleValuedAnnotationPcds.add("@withincode");
		singleValuedAnnotationPcds.add("@annotation");
		
		try {
			annotationClass = Class.forName(ANNOTATION_CLASS_NAME);
		}
		catch(ClassNotFoundException ex) {
			// Running on < JDK 1.5, this is ok...
			annotationClass = null;
		}
	}


	private boolean raiseExceptions = false;
	
	/**
	 * If the advice is afterReturning, and binds the return value, this is the parameter name used. 
	 */
	private String returningName = null;
	
	/**
	 * If the advice is afterThrowing, and binds the thrown value, this is the parameter name used.
	 */
	private String throwingName = null;
	
	/**
	 * The pointcut expression associated with the advice, as a simple String.
	 */
	private String pointcutExpression = null;
	
	private Class[] argumentTypes;

	private String[] parameterNameBindings;

	private int numberOfRemainingUnboundArguments;

	private int algorithmicStep = STEP_JOIN_POINT_BINDING;
	
	
	/**
	 * Create a new discoverer that attempts to discover parameter names from the given
	 * pointcut expression.
	 * @param pointcutExpression
	 */
	public AspectJAdviceParameterNameDiscoverer(String pointcutExpression) {
		this.pointcutExpression = pointcutExpression;
	}
	
	/**
	 * <p>
	 * Set this property to true to indicate the IllegalArgumentException and AmbiguousBindingException
	 * should be thrown as appropriate in the case of failing to deduce advice parameter names.
	 * </p>
	 * @param shouldRaise
	 */
	public void setRaiseExceptions(boolean shouldRaise) {
		this.raiseExceptions = shouldRaise;
	}
	
	/**
	 * If afterReturning advice binds the return value, the returning 
	 * variable name must be specified.
	 * @param returningName
	 */
	public void setReturningName(String returningName) {
		this.returningName = returningName;
	}
	
	/**
	 * If afterThrowing advice binds the thrown value, the throwing 
	 * variable name must be specified.
	 * @param throwingName
	 */
	public void setThrowingName(String throwingName) {
		this.throwingName = throwingName;
	}
	
	/**
	 * <p>
	 * Deduce the parameter names for an advice method. See the javadoc comment for this class for 
	 * details of the algorithm used.
	 * </p>
	 */
	public String[] getParameterNames(Method method) {
		this.argumentTypes = method.getParameterTypes();
		this.numberOfRemainingUnboundArguments = this.argumentTypes.length;
		this.parameterNameBindings = new String[this.numberOfRemainingUnboundArguments];
		this.algorithmicStep = STEP_JOIN_POINT_BINDING;
		
		int minimumNumberUnboundArgs = 0;
		if (this.returningName != null) { minimumNumberUnboundArgs++; }
		if (this.throwingName != null) { minimumNumberUnboundArgs++; }
		if (this.numberOfRemainingUnboundArguments < minimumNumberUnboundArgs) {
			throw new IllegalStateException("Not enough arguments in method to satisfy binding of returning and throwing variables");
		}
		
		try {
			while ((this.numberOfRemainingUnboundArguments > 0) && (this.algorithmicStep < STEP_FINISHED)) {
				switch(this.algorithmicStep++) {
					case STEP_JOIN_POINT_BINDING:
						if (!maybeBindThisJoinPoint()) {
							maybeBindThisJoinPointStaticPart();
						}
						break;
					case STEP_THROWING_BINDING:
						maybeBindThrowingVariable();
						break;
					case STEP_ANNOTATION_BINDING:
						maybeBindAnnotationsFromPointcutExpression();
						break;
					case STEP_RETURNING_BINDING:
						maybeBindReturningVariable();
						break;
					case STEP_PRIMITIVE_ARGS_BINDING:
						maybeBindPrimitiveArgsFromPointcutExpression();
						break;
					case STEP_THIS_TARGET_ARGS_BINDING:
						maybeBindThisOrTargetOrArgsFromPointcutExpression();
						break;
					default:
						throw new IllegalStateException("Unknown algorithmic step: " + (this.algorithmicStep -1));
				}
			}
		} catch (AmbiguousBindingException ambigEx) {
			if (this.raiseExceptions) {
				throw ambigEx;
			} else {
				return null;
			}
		} catch (IllegalArgumentException illArgEx) {
			if (this.raiseExceptions) {
				throw illArgEx;
			} else {
				return null;
			}
		}
		
		if (this.numberOfRemainingUnboundArguments == 0) {
			return this.parameterNameBindings;
		} else {
			if (this.raiseExceptions) {
				throw new IllegalStateException("Failed to bind all argument names: " + 
						this.numberOfRemainingUnboundArguments + " argument(s) could not be bound");
			} else {
				// convention for failing is to return null, allowing participation in a chain of responsibility
				return null;
			}
		}
	}

	/**
	 * An advice method can never be a constructor.
	 * @return null
	 * @throws UnsupportedOperationException iff raiseExceptions has been set to true
	 */
	public String[] getParameterNames(Constructor ctor) {
		if (this.raiseExceptions) {
			throw new UnsupportedOperationException("An advice method can never be a constructor");
		} else {
			// we return null rather than throw an exception so that we behave well
			// in a chain-of-responsibility.
			return null;
		}
	}
	
	// support routines...
	// ==========================
	
	private void bindParameterName(int index, String name) {
		this.parameterNameBindings[index] = name;
		this.numberOfRemainingUnboundArguments--;
	}
	
	/**
	 * If the first parameter is of type JoinPoint or ProceedingJoinPoint,bind "thisJoinPoint" as 
	 * parameter name and return true, else return false. 
	 */
	private boolean maybeBindThisJoinPoint() {
		if ((this.argumentTypes[0] == JoinPoint.class) || (this.argumentTypes[0] == ProceedingJoinPoint.class)) {
			bindParameterName(0,THIS_JOIN_POINT);
			return true;
		} else {
			return false;
		}
	}
	
	private void maybeBindThisJoinPointStaticPart() {
		if (this.argumentTypes[0] == JoinPoint.StaticPart.class) {
			bindParameterName(0,THIS_JOIN_POINT_STATIC_PART);
		}
	}

	/**
	 * If a throwing name was specified and there is exactly one choice remaining
	 * (argument that is a subtype of Throwable) then bind it.
	 */
	private void maybeBindThrowingVariable() {
		if (this.throwingName == null) {
			return;
		}
		
		// so there is binding work to do...
		int throwableIndex = -1;
		for (int i = 0; i < this.argumentTypes.length; i++) {
			if (isUnbound(i) && isSubtypeOf(Throwable.class,i)) {
				if (throwableIndex == -1) {
					throwableIndex = i;
				} else {
					// second candidate we've found - ambiguous binding
					throw new AmbiguousBindingException("Binding of throwing parameter '" + 
							this.throwingName + "' is ambiguous: could be bound to argument " +
							throwableIndex + " or argument " + i);
				}
			}
		}
		
		if (throwableIndex == -1) {
			throw new IllegalStateException("Binding of throwing parameter '" + this.throwingName 
					+ "' could not be completed as no available arguments are a subtype of Throwable");
		} else {
			bindParameterName(throwableIndex,this.throwingName);
		}
	}

	/**
	 * If a returning variable was specified and there is only one choice remaining, bind it.
	 */
	private void maybeBindReturningVariable() {
		if (this.numberOfRemainingUnboundArguments == 0) {
			throw new IllegalStateException("algorithm assumes that there must be at least one unbound parameter on entry to this method");
		}
		
		if (this.returningName != null) {

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -