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

📄 cglib2aopproxy.java

📁 Java/J2EE application framework based on [Expert One-on-One J2EE Design and Development] by Rod John
💻 JAVA
📖 第 1 页 / 共 2 页
字号:
/*
 * Copyright 2002-2004 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.framework;

import java.io.Serializable;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
import net.sf.cglib.proxy.Dispatcher;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import net.sf.cglib.transform.impl.UndeclaredThrowableStrategy;
import org.aopalliance.aop.AspectException;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * CGLIB 2 AopProxy implementation for the Spring AOP framework.
 *
 * <p>Objects of this type should be obtained through proxy factories, configured
 * by a AdvisedSupport implementation. This class is internal to the Spring
 * framework and need not be used directly by client code.
 *
 * <p/>Proxies created using this class are threadsafe if the underlying (target)
 * class is threadsafe.
 *
 * @author Rod Johnson
 * @author Rob Harrop
 */
public class Cglib2AopProxy implements AopProxy, Serializable {

	// Constants for CGLIB callback array indices

	private static final int AOP_PROXY = 0;

	private static final int INVOKE_TARGET = 1;

	private static final int NO_OVERRIDE = 2;

	private static final int DISPATCH_TARGET = 3;

	private static final int DISPATCH_ADVISED = 4;

	private static final int INVOKE_EQUALS = 5;


	/**
	 * Static to optimize serialization
	 */
	protected final static Log logger = LogFactory.getLog(Cglib2AopProxy.class);

	/**
	 * Dispatcher used for methods on <code>Advised</code>
	 */
	private final transient AdvisedDispatcher advisedDispatcher = new AdvisedDispatcher();

	private transient int fixedInterceptorOffset;

	private transient Map fixedInterceptorMap;

	/**
	 * Config used to configure this proxy
	 */
	protected AdvisedSupport advised;


	/**
	 * @throws AopConfigException if the config is invalid. We try to throw an informative
	 * exception in this case, rather than let a mysterious failure happen later.
	 */
	protected Cglib2AopProxy(AdvisedSupport config) throws AopConfigException {
		if (config == null) {
			throw new AopConfigException("Cannot create AopProxy with null ProxyConfig");
		}
		if (config.getAdvisors().length == 0 &&
		    config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("Cannot create AopProxy with no advisors and no target source");
		}
		this.advised = config;
		if (this.advised.getTargetSource().getTargetClass() == null) {
			throw new AopConfigException("Either an interface or a target is required for proxy creation");
		}
	}

	/**
	 * Wrap a return of this if necessary to be the proxy
	 */
	protected static Object massageReturnTypeIfNecessary(Object proxy, Object target, Object retVal) {
		// Massage return value if necessary
		if (retVal != null && retVal == target) {
			// Special case: it returned "this"
			// Note that we can't help if the target sets
			// a reference to itself in another returned object
			retVal = proxy;
		}
		return retVal;
	}

	/**
	 * Is the given method the equals method?
	 */
	protected final boolean isEqualsMethod(Method m) {
		return "equals".equals(m.getName())
		    && m.getParameterTypes().length == 1
		    && m.getParameterTypes()[0] == Object.class;
	}

	/**
	 * Create a new Proxy object for the given object, proxying the given
	 * interface. Uses the thread context class loader.
	 */
	public Object getProxy() {
		return getProxy(Thread.currentThread().getContextClassLoader());
	}

	/**
	 * Create a new Proxy object for the given object, proxying the given
	 * interface. Uses the given class loader.
	 */
	public Object getProxy(ClassLoader cl) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating CGLIB proxy for [" + this.advised.getTargetSource().getTargetClass() + "]");
		}

		Enhancer e = new Enhancer();
		try {
			Class rootClass = advised.getTargetSource().getTargetClass();

			e.setSuperclass(rootClass);
			e.setCallbackFilter(new ProxyCallbackFilter(advised));

			e.setStrategy(new UndeclaredThrowableStrategy(UndeclaredThrowableException.class));


			e.setInterfaces(AopProxyUtils.completeProxiedInterfaces(advised));

			Callback[] callbacks = getCallbacks(rootClass);

			e.setCallbacks(callbacks);

			Class[] types = new Class[callbacks.length];

			for (int x = 0; x < types.length; x++) {
				types[x] = callbacks[x].getClass();
			}
			e.setCallbackTypes(types);

			return e.create();
		}
		catch (CodeGenerationException ex) {
			throw new AspectException("Couldn't generate CGLIB subclass of class '"
			    + advised.getTargetSource().getTargetClass()
			    + "': "
			    + "Common causes of this problem include using a final class, or a non-visible class",
			    ex);
		}
		catch (IllegalArgumentException ex) {
			throw new AspectException("Couldn't generate CGLIB subclass of class '"
			    + advised.getTargetSource().getTargetClass()
			    + "': "
			    + "Common causes of this problem include using a final class, or a non-visible class",
			    ex);
		}
		catch (Exception ex) {
			// TargetSource getTarget failed
			throw new AspectException("Unexpected AOP exception", ex);
		}
	}

	private Callback[] getCallbacks(Class rootClass) throws Exception {
		// parameters used for optimisation choices
		boolean exposeProxy = advised.getExposeProxy();
		boolean isFrozen = advised.isFrozen();
		boolean isStatic = advised.getTargetSource().isStatic();

		// choose an "aop" interceptor (used for aop calls)
		Callback aopInterceptor = new DynamicAdvisedInterceptor();

		// choose a "straight to target" interceptor. (used for calls that are
		// unadvised but can return this). May be required to expose the proxy
		Callback targetInterceptor = null;

		if (exposeProxy) {
			targetInterceptor = isStatic
			    ? (Callback) new StaticUnadvisedExposedInterceptor(advised
			    .getTargetSource().getTarget())
			    : (Callback) new DynamicUnadvisedExposedInterceptor();
		}
		else {
			targetInterceptor = isStatic
			    ? (Callback) new StaticUnadvisedInterceptor(advised
			    .getTargetSource().getTarget())
			    : (Callback) new DynamicUnadvisedInterceptor();
		}

		// choose a "direct to target" dispatcher (used for
		// unadvised calls to static targets that cannot return this)
		Callback targetDispatcher = isStatic ? (Callback) new StaticDispatcher(advised.getTargetSource().getTarget()) : new NoOp();

		Callback[] mainCallbacks = new Callback[]{aopInterceptor, // For
		                                          // normal
		                                          // advice
		                                          targetInterceptor, // invoke target without considering
		                                          // advice, if optimized
		                                          new NoOp(), // no override for methods mapped to this.
		                                          targetDispatcher, advisedDispatcher,
		                                          new EqualsInterceptor(this.advised)

		};

		Callback[] callbacks;

		// if the target is a static one and the
		// advice chain is frozen then we can make some optimisations
		// by sending the aop calls direct to the target using the fixed
		// chain for that method
		if (isStatic && isFrozen) {
			Callback[] fixedCallbacks = null;

			Method[] methods = rootClass.getMethods();
			fixedCallbacks = new Callback[methods.length];

			fixedInterceptorMap = new HashMap();

			// TODO: small memory optimisation here (can skip creation for
			// methods with no advice)
			for (int x = 0; x < methods.length; x++) {

				List chain = advised.getAdvisorChainFactory()
				    .getInterceptorsAndDynamicInterceptionAdvice(advised,
				        null, methods[x], rootClass);

				fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(chain, advised.getTargetSource().getTarget());

				fixedInterceptorMap.put(methods[x].toString(), new Integer(x));
			}

			// now copy both the callbacks from mainCallbacks
			// and fixedCallbacks into the callbacks array.
			callbacks = new Callback[mainCallbacks.length
			    + fixedCallbacks.length];

			for (int x = 0; x < mainCallbacks.length; x++) {
				callbacks[x] = mainCallbacks[x];
			}

			for (int x = 0; x < fixedCallbacks.length; x++) {
				callbacks[x + mainCallbacks.length] = fixedCallbacks[x];
			}

			fixedInterceptorOffset = mainCallbacks.length;
		}
		else {
			callbacks = mainCallbacks;
		}
		return callbacks;
	}

	/**
	 * Checks to see if this CallbackFilter is the same CallbackFilter used for
	 * another proxy.
	 */
	public boolean equals(Object other) {
		if (other == null) {
			return false;
		}
		if (other == this) {
			return true;
		}

		Cglib2AopProxy otherCglibProxy = null;
		if (other instanceof Cglib2AopProxy) {
			otherCglibProxy = (Cglib2AopProxy) other;
		}
		else {
			// not a valid comparison
			return false;
		}

		return AopProxyUtils.equalsInProxy(advised, otherCglibProxy.advised);
	}

	public int hashCode() {
		return 0;
	}


	/**
	 * Method interceptor used for static targets with no advice chain. The call
	 * is passed directly back to the target. Used when the proxy needs to be
	 * exposed and it can't be determined that the method won't return
	 * <code>this</code>.
	 */
	private static class StaticUnadvisedInterceptor
	    implements MethodInterceptor, Serializable {

		private final Object target;

		public StaticUnadvisedInterceptor(Object target) {
			this.target = target;
		}

		public Object intercept(Object proxy, Method method, Object[] args,
		    MethodProxy methodProxy) throws Throwable {

			Object retVal = methodProxy.invoke(target, args);
			return massageReturnTypeIfNecessary(proxy, target, retVal);
		}
	}


	/**
	 * Method interceptor used for static targets with no advice chain, when the
	 * proxy is to be exposed.
	 */
	private static class StaticUnadvisedExposedInterceptor
	    implements MethodInterceptor, Serializable {

		private final Object target;

		public StaticUnadvisedExposedInterceptor(Object target) {
			this.target = target;
		}

		public Object intercept(Object proxy, Method method, Object[] args,
		    MethodProxy methodProxy) throws Throwable {

			Object oldProxy = null;
			boolean setProxyContext = false;

			try {
				oldProxy = AopContext.setCurrentProxy(proxy);

				Object retVal = methodProxy.invoke(target, args);
				return massageReturnTypeIfNecessary(proxy, target, retVal);
			}
			finally {
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}


	/**
	 * Interceptor used to invoke a dynamic target without creating a method
	 * invocation or evaluating an advice chain. (We know there was no advice
	 * for this method.)
	 */
	private class DynamicUnadvisedInterceptor implements MethodInterceptor, Serializable {

		public Object intercept(Object proxy, Method method, Object[] args,
		    MethodProxy methodProxy) throws Throwable {

			Object target = advised.getTargetSource().getTarget();

			try {
				Object ret = methodProxy.invoke(target, args);
				return massageReturnTypeIfNecessary(proxy, target, ret);
			}
			finally {
				advised.getTargetSource().releaseTarget(target);
			}
		}
	}


	/**
	 * Interceptor for unadvised dynamic targets when the proxy needs exposing.
	 */
	private class DynamicUnadvisedExposedInterceptor
	    implements MethodInterceptor, Serializable {

		public Object intercept(Object proxy, Method method, Object[] args,
		    MethodProxy methodProxy) throws Throwable {

			Object oldProxy = null;
			Object target = advised.getTargetSource().getTarget();

			try {
				oldProxy = AopContext.setCurrentProxy(proxy);
				Object ret = methodProxy.invoke(target, args);
				return massageReturnTypeIfNecessary(proxy, target, ret);
			}
			finally {
				AopContext.setCurrentProxy(oldProxy);
				advised.getTargetSource().releaseTarget(target);
			}
		}
	}


	/**
	 * Dispatcher for a static target. Dispatcher is much faster than
	 * interceptor. This will be used whenever it can be determined that a
	 * method definitely does not return "this"
	 */
	private static class StaticDispatcher implements Dispatcher, Serializable {

		private Object target;

		public StaticDispatcher(Object target) {
			this.target = target;
		}

		public Object loadObject() {
			return target;
		}
	}


	/**
	 * Dispatcher for a dynamic target. Dispatchers are much faster than
	 * interceptors. This will be used whenever it can be determined that a
	 * method definitely does not return "this"
	 *
	 * @author robh
	 */
	private class DynamicDispatcher implements Dispatcher, Serializable {

		public Object loadObject() throws Exception {
			return advised.getTargetSource().getTarget();
		}
	}

⌨️ 快捷键说明

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