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

📄 cglib2aopproxy.java

📁 spring的源代码
💻 JAVA
字号:
/*
 * 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.lang.reflect.Method;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.List;

import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.CallbackFilter;
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.proxy.NoOp;
import org.aopalliance.aop.AspectException;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.aop.TargetSource;

/**
 * CGLIB 2 AopProxy implementation for the Spring AOP framework.
 * Also implements the CGLIB MethodInterceptor and CallbackFilter
 * interfaces.
 *
 * <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
 * @version $Id: Cglib2AopProxy.java,v 1.7 2004/04/01 15:35:46 jhoeller Exp $
 */
class Cglib2AopProxy implements AopProxy, MethodInterceptor, CallbackFilter {
	
	// 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;
	
	protected final Log logger = LogFactory.getLog(getClass());

	/** Config used to configure this proxy */
	protected final 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");
		}
	}
		
	
	/**
	 * Implementation of MethodInterceptor.
	 * Callers will see exactly the exception thrown by the target, unless a hook
	 * method throws an exception.
	 */
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
	
		MethodInvocation invocation = null;
		Object oldProxy = null;
		boolean setProxyContext = false;
	
		TargetSource targetSource = advised.targetSource;
		Class targetClass = null;//targetSource.getTargetClass();
		Object target = null;		
		
		try {
			// Try special rules for equals() method and implementation of the
			// ProxyConfig AOP configuration interface
			if (isEqualsMethod(method)) {
				// This class implements the equals() method itself
				// We don't need to use reflection
				return new Boolean(equals(args[0]));
			}
			else if (Advised.class == method.getDeclaringClass()) {
				// Service invocations on ProxyConfig with the proxy config
				return AopProxyUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}
			
			Object retVal = null;
			
			// May be null. Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			target = targetSource.getTarget();
			if (target != null) {
				targetClass = target.getClass();
			}
			
			if (this.advised.exposeProxy) {
				// Make invocation available if necessary
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}
		
			List chain = advised.getAdvisorChainFactory().getInterceptorsAndDynamicInterceptionAdvice(this.advised, proxy, method, targetClass);
			
			// Check whether we only have one InvokerInterceptor: that is, no real advice,
			// but just reflective invocation of the target.
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying
				retVal = methodProxy.invoke(target, args);
			}
			else {
				// We need to create a method invocation...
				invocation = new MethodInvocationImpl(proxy, target, method, args, 
							targetClass, chain, methodProxy);
				
				// If we get here, we need to create a MethodInvocation
				retVal = invocation.proceed();
			}
			
			retVal = massageReturnTypeIfNecessary(proxy, target, retVal);
			return retVal;
		}
		catch (Throwable t) {
			// In CGLIB2, unlike CGLIB 1, it's necessary to wrap
			// undeclared throwable exceptions. As we don't care about JDK 1.2
			// compatibility, we use java.lang.reflect.UndeclaredThrowableException.
			if ( (t instanceof Exception) && !(t instanceof RuntimeException)) {
				// It's a checked exception: we must check it's legal
				Class[] permittedThrows = method.getExceptionTypes();
				for (int i = 0; i < permittedThrows.length; i++) {
					if (permittedThrows[i].isAssignableFrom(t.getClass())) {
						throw t;
					}
					//System.err.println("No match t=" + t + " throws=" + permittedThrows[i]);
				}
				throw new UndeclaredThrowableException(t);
			}
			
			// It's not a checked exception, so we can rethrow it
			throw t;
		}
		finally {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource
				targetSource.releaseTarget(target);
			}
			
			if (setProxyContext) {
				// Restore old proxy
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}	// intercept
	
	
	/**
	 * 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 {
			e.setSuperclass(advised.getTargetSource().getTargetClass());
			e.setCallbackFilter(this);
			e.setInterfaces(AopProxyUtils.completeProxiedInterfaces(advised));
			Callback targetInvoker = canApplyCglibOptimizations() ? 
					(Callback) new StaticTargetInvoker(advised.getTargetSource().getTarget()) :
					(Callback) new DynamicTargetInvoker();
			
			e.setCallbacks(new Callback[] {
					this,				// For normal advice
					targetInvoker,		// invoke target without considering advice, if optimized
					NoOp.INSTANCE		// no override for methods mapped to this
			});
		
			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 (Exception ex) {
			// TargetSource getTarget failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}
	
	/**
	 * Invoker used to invoke the target without creating a method invocation
	 * or evaluating an advice chain. (We know there was no advice for this method.)
	 */
	private class DynamicTargetInvoker implements MethodInterceptor {
		 /**
		 * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
		 */
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object target = advised.getTargetSource().getTarget();
			Object ret = methodProxy.invoke(target, args);
			return massageReturnTypeIfNecessary(proxy, target, ret);
		}
	}
	
	/**
	 * Like DynamicTargetInvoker, for use when there's a static TargetSource
	 */
	private static class StaticTargetInvoker implements MethodInterceptor {
		private final Object target;
		public StaticTargetInvoker(Object target) {
			this.target = target;
		}
		 /**
		 * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
		 */
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object ret = methodProxy.invoke(target, args);
			return massageReturnTypeIfNecessary(proxy, target, ret);
		}
	}
	
	
	/**
	 * Given the Advised object we have, can we apply CGLIB optimizations 
	 * (directly invoking the target for methods with no advice)?
	 */
	private boolean canApplyCglibOptimizations() {
		return advised.getOptimize() && 
			advised.getTargetSource().isStatic() &&
			!advised.getExposeProxy();
	}
	
	/**
	 * Implementation of CallbackFilter.accept() to return the index of the
	 * callback we need. This will mean either no overriding,
	 * AOP_PROXY (run through our intercept method) or INVOKE_TARGET 
	 * (optimized direct invocation of target without re-evaluating
	 * advice chain at runtime).
	 * @see net.sf.cglib.proxy.CallbackFilter#accept(java.lang.reflect.Method)
	 */
	public int accept(Method method) {
		
		if (method.getName().equals("finalize") && method.getDeclaringClass() == Object.class) {
			return NO_OVERRIDE; 
		}
		
		if (!canApplyCglibOptimizations()) {
			return AOP_PROXY;
		}
		
		// Could consider more aggressive optimization in which we have a distinct
		// callback with the advice chain for each method, but it's probably not
		// worth it
		
		// We can apply optimizations
		// The optimization means that we evaluate whether or not there's an
		// advice chain once only, befre each invocation. 
	
		Class targetClass = advised.getTargetSource().getTargetClass();
		
		// We must always proxy equals, to direct calls to this
		if (isEqualsMethod(method))
			return AOP_PROXY;
	
		// Proxy is not yet available, but that shouldn't matter
		List chain = advised.getAdvisorChainFactory().getInterceptorsAndDynamicInterceptionAdvice(advised, null, method, targetClass);
		boolean  haveAdvice = !chain.isEmpty();

		if (haveAdvice) {
			logger.info("CGLIB proxy for " + targetClass.getName() + 
						" WILL override " + method);
		}
		else {
			logger.info("Chain is empty for " + method + "; will NOT override");
		}
		return haveAdvice ? AOP_PROXY : INVOKE_TARGET;
	}
	

	/**
	 * Equality means interceptors and interfaces are ==.
	 * @see java.lang.Object#equals(java.lang.Object)
	 * @param other may be a dynamic proxy wrapping an instance
	 * of this class
	 */
	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 if (other instanceof Factory) {
			// The 0th callback will be the Cglib2AopProxy if we're correct
			Callback callback = ((Factory) other).getCallback(AOP_PROXY);
			if (!(callback instanceof Cglib2AopProxy))
				return false;
			otherCglibProxy = (Cglib2AopProxy) callback; 
		}
		else {
			// Not a valid comparison
			return false;
		}
		
		return AopProxyUtils.equalsInProxy(advised, otherCglibProxy.advised);
	}
	
	
	/**
	 * Implementation of AOP Alliance MethodInvocation used by this AOP proxy
	 */
	private static class MethodInvocationImpl extends ReflectiveMethodInvocation {
	
		private MethodProxy methodProxy;
	
		public MethodInvocationImpl(Object proxy, Object target, Method m, Object[] arguments, Class targetClass,
				List interceptorsAndDynamicMethodMatchers,
				MethodProxy methodProxy) {
			super(proxy, target, m, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
			this.methodProxy = methodProxy;
		}
	
		/**
		 * Gives a marginal performance improvement versus using reflection to invoke the target.
		 * @see org.springframework.aop.framework.ReflectiveMethodInvocation#invokeJoinpoint()
		 */
		protected Object invokeJoinpoint() throws Throwable {
			return methodProxy.invoke(target, arguments);
		}
	}

}

⌨️ 快捷键说明

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