Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » aop » framework » [javadoc | source]
    1   /*
    2    * Copyright 2002-2007 the original author or authors.
    3    *
    4    * Licensed under the Apache License, Version 2.0 (the "License");
    5    * you may not use this file except in compliance with the License.
    6    * You may obtain a copy of the License at
    7    *
    8    *      http://www.apache.org/licenses/LICENSE-2.0
    9    *
   10    * Unless required by applicable law or agreed to in writing, software
   11    * distributed under the License is distributed on an "AS IS" BASIS,
   12    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13    * See the License for the specific language governing permissions and
   14    * limitations under the License.
   15    */
   16   
   17   package org.springframework.aop.framework;
   18   
   19   import java.lang.reflect.AccessibleObject;
   20   import java.lang.reflect.Method;
   21   import java.util.HashMap;
   22   import java.util.List;
   23   import java.util.Map;
   24   
   25   import org.aopalliance.intercept.MethodInterceptor;
   26   import org.aopalliance.intercept.MethodInvocation;
   27   
   28   import org.springframework.aop.ProxyMethodInvocation;
   29   import org.springframework.aop.support.AopUtils;
   30   
   31   /**
   32    * Spring's implementation of the AOP Alliance
   33    * {@link org.aopalliance.intercept.MethodInvocation} interface,
   34    * implementing the extended
   35    * {@link org.springframework.aop.ProxyMethodInvocation} interface.
   36    *
   37    * <p>Invokes the target object using reflection. Subclasses can override the
   38    * {@link #invokeJoinpoint()} method to change this behavior, so this is also
   39    * a useful base class for more specialized MethodInvocation implementations.
   40    *
   41    * <p>It is possible to clone an invocation, to invoke {@link #proceed()}
   42    * repeatedly (once per clone), using the {@link #invocableClone()} method.
   43    * It is also possible to attach custom attributes to the invocation,
   44    * using the {@link #setUserAttribute} / {@link #getUserAttribute} methods.
   45    *
   46    * <p><b>NOTE:</b> This class is considered internal and should not be
   47    * directly accessed. The sole reason for it being public is compatibility
   48    * with existing framework integrations (e.g. Pitchfork). For any other
   49    * purposes, use the {@link ProxyMethodInvocation} interface instead.
   50    *
   51    * @author Rod Johnson
   52    * @author Juergen Hoeller
   53    * @author Adrian Colyer
   54    * @see #invokeJoinpoint
   55    * @see #proceed
   56    * @see #invocableClone
   57    * @see #setUserAttribute
   58    * @see #getUserAttribute
   59    */
   60   public class ReflectiveMethodInvocation implements ProxyMethodInvocation, Cloneable {
   61   
   62   	protected final Object proxy;
   63   
   64   	protected final Object target;
   65   
   66   	protected final Method method;
   67   
   68   	protected Object[] arguments;
   69   
   70   	private final Class targetClass;
   71   
   72   	/**
   73   	 * Lazily initialized map of user-specific attributes for this invocation.
   74   	 */
   75   	private Map userAttributes;
   76   
   77   	/**
   78   	 * List of MethodInterceptor and InterceptorAndDynamicMethodMatcher
   79   	 * that need dynamic checks.
   80   	 */
   81   	protected final List interceptorsAndDynamicMethodMatchers;
   82   
   83   	/**
   84   	 * Index from 0 of the current interceptor we're invoking.
   85   	 * -1 until we invoke: then the current interceptor.
   86   	 */
   87   	private int currentInterceptorIndex = -1;
   88   
   89   
   90   	/**
   91   	 * Construct a new ReflectiveMethodInvocation with the given arguments.
   92   	 * @param proxy the proxy object that the invocation was made on
   93   	 * @param target the target object to invoke
   94   	 * @param method the method to invoke
   95   	 * @param arguments the arguments to invoke the method with
   96   	 * @param targetClass the target class, for MethodMatcher invocations
   97   	 * @param interceptorsAndDynamicMethodMatchers interceptors that should be applied,
   98   	 * along with any InterceptorAndDynamicMethodMatchers that need evaluation at runtime.
   99   	 * MethodMatchers included in this struct must already have been found to have matched
  100   	 * as far as was possibly statically. Passing an array might be about 10% faster,
  101   	 * but would complicate the code. And it would work only for static pointcuts.
  102   	 */
  103   	protected ReflectiveMethodInvocation(
  104   	    Object proxy, Object target, Method method, Object[] arguments,
  105   	    Class targetClass, List interceptorsAndDynamicMethodMatchers) {
  106   
  107   		this.proxy = proxy;
  108   		this.target = target;
  109   		this.targetClass = targetClass;
  110   		this.method = method;
  111   		this.arguments = arguments;
  112   		this.interceptorsAndDynamicMethodMatchers = interceptorsAndDynamicMethodMatchers;
  113   	}
  114   
  115   
  116   	public final Object getProxy() {
  117   		return this.proxy;
  118   	}
  119   
  120   	public final Object getThis() {
  121   		return this.target;
  122   	}
  123   
  124   	public final AccessibleObject getStaticPart() {
  125   		return this.method;
  126   	}
  127   
  128   	/**
  129   	 * Return the method invoked on the proxied interface.
  130   	 * May or may not correspond with a method invoked on an underlying
  131   	 * implementation of that interface.
  132   	 */
  133   	public final Method getMethod() {
  134   		return this.method;
  135   	}
  136   
  137   	public final Object[] getArguments() {
  138   		return (this.arguments != null ? this.arguments : new Object[0]);
  139   	}
  140   
  141   	public void setArguments(Object[] arguments) {
  142   		this.arguments = arguments;
  143   	}
  144   
  145   
  146   	public Object proceed() throws Throwable {
  147   		//	We start with an index of -1 and increment early.
  148   		if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
  149   			return invokeJoinpoint();
  150   		}
  151   
  152   		Object interceptorOrInterceptionAdvice =
  153   		    this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
  154   		if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
  155   			// Evaluate dynamic method matcher here: static part will already have
  156   			// been evaluated and found to match.
  157   			InterceptorAndDynamicMethodMatcher dm =
  158   			    (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
  159   			if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
  160   				return dm.interceptor.invoke(this);
  161   			}
  162   			else {
  163   				// Dynamic matching failed.
  164   				// Skip this interceptor and invoke the next in the chain.
  165   				return proceed();
  166   			}
  167   		}
  168   		else {
  169   			// It's an interceptor, so we just invoke it: The pointcut will have
  170   			// been evaluated statically before this object was constructed.
  171   			return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
  172   		}
  173   	}
  174   
  175   	/**
  176   	 * Invoke the joinpoint using reflection.
  177   	 * Subclasses can override this to use custom invocation.
  178   	 * @return the return value of the joinpoint
  179   	 * @throws Throwable if invoking the joinpoint resulted in an exception
  180   	 */
  181   	protected Object invokeJoinpoint() throws Throwable {
  182   		return AopUtils.invokeJoinpointUsingReflection(this.target, this.method, this.arguments);
  183   	}
  184   
  185   
  186   	/**
  187   	 * This implementation returns a shallow copy of this invocation object,
  188   	 * including an independent copy of the original arguments array.
  189   	 * <p>We want a shallow copy in this case: We want to use the same interceptor
  190   	 * chain and other object references, but we want an independent value for the
  191   	 * current interceptor index.
  192   	 * @see java.lang.Object#clone()
  193   	 */
  194   	public MethodInvocation invocableClone() {
  195   		Object[] cloneArguments = null;
  196   		if (this.arguments != null) {
  197   			// Build an independent copy of the arguments array.
  198   			cloneArguments = new Object[this.arguments.length];
  199   			System.arraycopy(this.arguments, 0, cloneArguments, 0, this.arguments.length);
  200   		}
  201   		return invocableClone(cloneArguments);
  202   	}
  203   
  204   	/**
  205   	 * This implementation returns a shallow copy of this invocation object,
  206   	 * using the given arguments array for the clone.
  207   	 * <p>We want a shallow copy in this case: We want to use the same interceptor
  208   	 * chain and other object references, but we want an independent value for the
  209   	 * current interceptor index.
  210   	 * @see java.lang.Object#clone()
  211   	 */
  212   	public MethodInvocation invocableClone(Object[] arguments) {
  213   		// Force initialization of the user attributes Map,
  214   		// for having a shared Map reference in the clone.
  215   		if (this.userAttributes == null) {
  216   			this.userAttributes = new HashMap();
  217   		}
  218   
  219   		// Create the MethodInvocation clone.
  220   		try {
  221   			ReflectiveMethodInvocation clone = (ReflectiveMethodInvocation) clone();
  222   			clone.arguments = arguments;
  223   			return clone;
  224   		}
  225   		catch (CloneNotSupportedException ex) {
  226   			throw new IllegalStateException(
  227   					"Should be able to clone object of type [" + getClass() + "]: " + ex);
  228   		}
  229   	}
  230   
  231   
  232   	public void setUserAttribute(String key, Object value) {
  233   		if (value != null) {
  234   			if (this.userAttributes == null) {
  235   				this.userAttributes = new HashMap();
  236   			}
  237   			this.userAttributes.put(key, value);
  238   		}
  239   		else {
  240   			if (this.userAttributes != null) {
  241   				this.userAttributes.remove(key);
  242   			}
  243   		}
  244   	}
  245   
  246   	public Object getUserAttribute(String key) {
  247   		return (this.userAttributes != null ? this.userAttributes.get(key) : null);
  248   	}
  249   
  250   	/**
  251   	 * Return user attributes associated with this invocation.
  252   	 * This method provides an invocation-bound alternative to a ThreadLocal.
  253   	 * <p>This map is initialized lazily and is not used in the AOP framework itself.
  254   	 * @return any user attributes associated with this invocation
  255   	 * (never <code>null</code>)
  256   	 */
  257   	public Map getUserAttributes() {
  258   		if (this.userAttributes == null) {
  259   			this.userAttributes = new HashMap();
  260   		}
  261   		return this.userAttributes;
  262   	}
  263   
  264   
  265   	public String toString() {
  266   		// Don't do toString on target, it may be proxied.
  267   		StringBuffer sb = new StringBuffer("ReflectiveMethodInvocation: ");
  268   		sb.append(this.method).append("; ");
  269   		if (this.target == null) {
  270   			sb.append("target is null");
  271   		}
  272   		else {
  273   			sb.append("target is of class [").append(this.target.getClass().getName()).append(']');
  274   		}
  275   		return sb.toString();
  276   	}
  277   
  278   }

Save This Page
Home » spring-framework-2.5.6-with-dependencies » org.springframework » aop » framework » [javadoc | source]