📄 cglib2aopproxy.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.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 + -