0001: /*
0002: * Copyright 2002-2007 the original author or authors.
0003: *
0004: * Licensed under the Apache License, Version 2.0 (the "License");
0005: * you may not use this file except in compliance with the License.
0006: * You may obtain a copy of the License at
0007: *
0008: * http://www.apache.org/licenses/LICENSE-2.0
0009: *
0010: * Unless required by applicable law or agreed to in writing, software
0011: * distributed under the License is distributed on an "AS IS" BASIS,
0012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013: * See the License for the specific language governing permissions and
0014: * limitations under the License.
0015: */
0016:
0017: package org.springframework.aop.framework;
0018:
0019: import java.io.Serializable;
0020: import java.lang.reflect.Method;
0021: import java.lang.reflect.Modifier;
0022: import java.lang.reflect.UndeclaredThrowableException;
0023: import java.util.HashMap;
0024: import java.util.List;
0025: import java.util.Map;
0026: import java.util.WeakHashMap;
0027:
0028: import net.sf.cglib.core.CodeGenerationException;
0029: import net.sf.cglib.proxy.Callback;
0030: import net.sf.cglib.proxy.CallbackFilter;
0031: import net.sf.cglib.proxy.Dispatcher;
0032: import net.sf.cglib.proxy.Enhancer;
0033: import net.sf.cglib.proxy.Factory;
0034: import net.sf.cglib.proxy.MethodInterceptor;
0035: import net.sf.cglib.proxy.MethodProxy;
0036: import net.sf.cglib.proxy.NoOp;
0037: import net.sf.cglib.transform.impl.UndeclaredThrowableStrategy;
0038: import org.aopalliance.aop.Advice;
0039: import org.aopalliance.intercept.MethodInvocation;
0040: import org.apache.commons.logging.Log;
0041: import org.apache.commons.logging.LogFactory;
0042:
0043: import org.springframework.aop.Advisor;
0044: import org.springframework.aop.PointcutAdvisor;
0045: import org.springframework.aop.RawTargetAccess;
0046: import org.springframework.aop.TargetSource;
0047: import org.springframework.aop.support.AopUtils;
0048: import org.springframework.util.Assert;
0049: import org.springframework.util.ObjectUtils;
0050:
0051: /**
0052: * CGLIB2-based {@link AopProxy} implementation for the Spring AOP framework.
0053: *
0054: * <p><i>Requires CGLIB 2.1+ on the classpath.</i>.
0055: * As of Spring 2.0, earlier CGLIB versions are not supported anymore.
0056: *
0057: * <p>Objects of this type should be obtained through proxy factories,
0058: * configured by an {@link AdvisedSupport} object. This class is internal
0059: * to Spring's AOP framework and need not be used directly by client code.
0060: *
0061: * <p>{@link DefaultAopProxyFactory} will automatically create CGLIB2-based
0062: * proxies if necessary, for example in case of proxying a target class
0063: * (see the {@link DefaultAopProxyFactory attendant javadoc} for details).
0064: *
0065: * <p>Proxies created using this class are thread-safe if the underlying
0066: * (target) class is thread-safe.
0067: *
0068: * @author Rod Johnson
0069: * @author Rob Harrop
0070: * @author Juergen Hoeller
0071: * @author Ramnivas Laddad
0072: * @see net.sf.cglib.proxy.Enhancer
0073: * @see AdvisedSupport#setProxyTargetClass
0074: * @see DefaultAopProxyFactory
0075: */
0076: final class Cglib2AopProxy implements AopProxy, Serializable {
0077:
0078: // Constants for CGLIB callback array indices
0079: private static final int AOP_PROXY = 0;
0080: private static final int INVOKE_TARGET = 1;
0081: private static final int NO_OVERRIDE = 2;
0082: private static final int DISPATCH_TARGET = 3;
0083: private static final int DISPATCH_ADVISED = 4;
0084: private static final int INVOKE_EQUALS = 5;
0085: private static final int INVOKE_HASHCODE = 6;
0086:
0087: /** Logger available to subclasses; static to optimize serialization */
0088: protected final static Log logger = LogFactory
0089: .getLog(Cglib2AopProxy.class);
0090:
0091: /** Keeps track of the Classes that we have validated for final methods */
0092: private static final Map validatedClasses = new WeakHashMap();
0093:
0094: /** The configuration used to configure this proxy */
0095: protected final AdvisedSupport advised;
0096:
0097: private Object[] constructorArgs;
0098:
0099: private Class[] constructorArgTypes;
0100:
0101: /** Dispatcher used for methods on Advised */
0102: private final transient AdvisedDispatcher advisedDispatcher;
0103:
0104: private transient Map fixedInterceptorMap;
0105:
0106: private transient int fixedInterceptorOffset;
0107:
0108: /**
0109: * Create a new Cglib2AopProxy for the given AOP configuration.
0110: * @param config the AOP configuration as AdvisedSupport object
0111: * @throws AopConfigException if the config is invalid. We try to throw an informative
0112: * exception in this case, rather than let a mysterious failure happen later.
0113: */
0114: public Cglib2AopProxy(AdvisedSupport config)
0115: throws AopConfigException {
0116: Assert.notNull(config, "AdvisedSupport must not be null");
0117: if (config.getAdvisors().length == 0
0118: && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
0119: throw new AopConfigException(
0120: "No advisors and no TargetSource specified");
0121: }
0122: this .advised = config;
0123: this .advisedDispatcher = new AdvisedDispatcher(this .advised);
0124: }
0125:
0126: /**
0127: * Set constructor arguments to use for creating the proxy.
0128: * @param constructorArgs the constructor argument values
0129: * @param constructorArgTypes the constructor argument types
0130: */
0131: public void setConstructorArguments(Object[] constructorArgs,
0132: Class[] constructorArgTypes) {
0133: if (constructorArgs == null || constructorArgTypes == null) {
0134: throw new IllegalArgumentException(
0135: "Both 'constructorArgs' and 'constructorArgTypes' need to be specified");
0136: }
0137: if (constructorArgs.length != constructorArgTypes.length) {
0138: throw new IllegalArgumentException(
0139: "Number of 'constructorArgs' ("
0140: + constructorArgs.length
0141: + ") must match number of 'constructorArgTypes' ("
0142: + constructorArgTypes.length + ")");
0143: }
0144: this .constructorArgs = constructorArgs;
0145: this .constructorArgTypes = constructorArgTypes;
0146: }
0147:
0148: public Object getProxy() {
0149: return getProxy(null);
0150: }
0151:
0152: public Object getProxy(ClassLoader classLoader) {
0153: if (logger.isDebugEnabled()) {
0154: logger.debug("Creating CGLIB2 proxy: target source is "
0155: + this .advised.getTargetSource());
0156: }
0157:
0158: Enhancer enhancer = createEnhancer();
0159: try {
0160: // Create proxy in specific ClassLoader, if given.
0161: if (classLoader != null) {
0162: enhancer.setClassLoader(classLoader);
0163: }
0164:
0165: Class rootClass = this .advised.getTargetClass();
0166: Assert
0167: .state(rootClass != null,
0168: "Target class must be available for creating a CGLIB proxy");
0169: Class proxySuperClass = rootClass;
0170:
0171: if (AopUtils.isCglibProxyClass(rootClass)) {
0172: proxySuperClass = rootClass.getSuperclass();
0173: Class[] additionalInterfaces = rootClass
0174: .getInterfaces();
0175: for (int i = 0; i < additionalInterfaces.length; i++) {
0176: Class additionalInterface = additionalInterfaces[i];
0177: this .advised.addInterface(additionalInterface);
0178: }
0179: }
0180:
0181: // Validate the class, writing log messages as necessary.
0182: validateClassIfNecessary(proxySuperClass);
0183:
0184: enhancer.setSuperclass(proxySuperClass);
0185: enhancer.setStrategy(new UndeclaredThrowableStrategy(
0186: UndeclaredThrowableException.class));
0187: enhancer.setInterfaces(AopProxyUtils
0188: .completeProxiedInterfaces(this .advised));
0189: enhancer.setInterceptDuringConstruction(false);
0190:
0191: Callback[] callbacks = getCallbacks(rootClass);
0192: enhancer.setCallbacks(callbacks);
0193: enhancer.setCallbackFilter(new ProxyCallbackFilter(
0194: this .advised.getConfigurationOnlyCopy(),
0195: this .fixedInterceptorMap,
0196: this .fixedInterceptorOffset));
0197:
0198: Class[] types = new Class[callbacks.length];
0199: for (int x = 0; x < types.length; x++) {
0200: types[x] = callbacks[x].getClass();
0201: }
0202: enhancer.setCallbackTypes(types);
0203:
0204: // Generate the proxy class and create a proxy instance.
0205: Object proxy;
0206: if (this .constructorArgs != null) {
0207: proxy = enhancer.create(this .constructorArgTypes,
0208: this .constructorArgs);
0209: } else {
0210: proxy = enhancer.create();
0211: }
0212:
0213: return proxy;
0214: } catch (CodeGenerationException ex) {
0215: throw new AopConfigException(
0216: "Could not generate CGLIB subclass of class ["
0217: + this .advised.getTargetClass()
0218: + "]: "
0219: + "Common causes of this problem include using a final class or a non-visible class",
0220: ex);
0221: } catch (IllegalArgumentException ex) {
0222: throw new AopConfigException(
0223: "Could not generate CGLIB subclass of class ["
0224: + this .advised.getTargetClass()
0225: + "]: "
0226: + "Common causes of this problem include using a final class or a non-visible class",
0227: ex);
0228: } catch (Exception ex) {
0229: // TargetSource.getTarget() failed
0230: throw new AopConfigException("Unexpected AOP exception", ex);
0231: }
0232: }
0233:
0234: /**
0235: * Creates the CGLIB {@link Enhancer}. Subclasses may wish to override this to return a custom
0236: * {@link Enhancer} implementation.
0237: */
0238: protected Enhancer createEnhancer() {
0239: return new Enhancer();
0240: }
0241:
0242: /**
0243: * Checks to see whether the supplied <code>Class</code> has already been validated and
0244: * validates it if not.
0245: */
0246: private void validateClassIfNecessary(Class proxySuperClass) {
0247: if (logger.isWarnEnabled()) {
0248: synchronized (validatedClasses) {
0249: if (!validatedClasses.containsKey(proxySuperClass)) {
0250: doValidateClass(proxySuperClass);
0251: validatedClasses.put(proxySuperClass, Boolean.TRUE);
0252: }
0253: }
0254: }
0255: }
0256:
0257: /**
0258: * Checks for final methods on the <code>Class</code> and writes warnings to the log
0259: * for each one found.
0260: */
0261: private void doValidateClass(Class proxySuperClass) {
0262: Method[] methods = proxySuperClass.getMethods();
0263: for (int i = 0; i < methods.length; i++) {
0264: Method method = methods[i];
0265: if (!Object.class.equals(method.getDeclaringClass())
0266: && Modifier.isFinal(method.getModifiers())) {
0267: logger
0268: .warn("Unable to proxy method ["
0269: + method
0270: + "] because it is final: "
0271: + "All calls to this method via a proxy will be routed directly to the proxy.");
0272: }
0273: }
0274: }
0275:
0276: private Callback[] getCallbacks(Class rootClass) throws Exception {
0277: // Parameters used for optimisation choices...
0278: boolean exposeProxy = this .advised.isExposeProxy();
0279: boolean isFrozen = this .advised.isFrozen();
0280: boolean isStatic = this .advised.getTargetSource().isStatic();
0281:
0282: // Choose an "aop" interceptor (used for AOP calls).
0283: Callback aopInterceptor = new DynamicAdvisedInterceptor(
0284: this .advised);
0285:
0286: // Choose a "straight to target" interceptor. (used for calls that are
0287: // unadvised but can return this). May be required to expose the proxy.
0288: Callback targetInterceptor = null;
0289:
0290: if (exposeProxy) {
0291: targetInterceptor = isStatic ? (Callback) new StaticUnadvisedExposedInterceptor(
0292: this .advised.getTargetSource().getTarget())
0293: : (Callback) new DynamicUnadvisedExposedInterceptor(
0294: this .advised.getTargetSource());
0295: } else {
0296: targetInterceptor = isStatic ? (Callback) new StaticUnadvisedInterceptor(
0297: this .advised.getTargetSource().getTarget())
0298: : (Callback) new DynamicUnadvisedInterceptor(
0299: this .advised.getTargetSource());
0300: }
0301:
0302: // Choose a "direct to target" dispatcher (used for
0303: // unadvised calls to static targets that cannot return this).
0304: Callback targetDispatcher = isStatic ? (Callback) new StaticDispatcher(
0305: this .advised.getTargetSource().getTarget())
0306: : new SerializableNoOp();
0307:
0308: Callback[] mainCallbacks = new Callback[] {
0309: aopInterceptor, // for normal advice
0310: targetInterceptor, // invoke target without considering advice, if optimized
0311: new SerializableNoOp(), // no override for methods mapped to this
0312: targetDispatcher, this .advisedDispatcher,
0313: new EqualsInterceptor(this .advised),
0314: new HashCodeInterceptor(this .advised) };
0315:
0316: Callback[] callbacks;
0317:
0318: // If the target is a static one and the advice chain is frozen,
0319: // then we can make some optimisations by sending the AOP calls
0320: // direct to the target using the fixed chain for that method.
0321: if (isStatic && isFrozen) {
0322: Method[] methods = rootClass.getMethods();
0323: Callback[] fixedCallbacks = new Callback[methods.length];
0324: this .fixedInterceptorMap = new HashMap(methods.length);
0325:
0326: // TODO: small memory optimisation here (can skip creation for
0327: // methods with no advice)
0328: for (int x = 0; x < methods.length; x++) {
0329: List chain = this .advised
0330: .getInterceptorsAndDynamicInterceptionAdvice(
0331: methods[x], rootClass);
0332: fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
0333: chain, this .advised.getTargetSource()
0334: .getTarget(), this .advised
0335: .getTargetClass());
0336: this .fixedInterceptorMap.put(methods[x].toString(),
0337: new Integer(x));
0338: }
0339:
0340: // Now copy both the callbacks from mainCallbacks
0341: // and fixedCallbacks into the callbacks array.
0342: callbacks = new Callback[mainCallbacks.length
0343: + fixedCallbacks.length];
0344:
0345: for (int x = 0; x < mainCallbacks.length; x++) {
0346: callbacks[x] = mainCallbacks[x];
0347: }
0348:
0349: for (int x = 0; x < fixedCallbacks.length; x++) {
0350: callbacks[x + mainCallbacks.length] = fixedCallbacks[x];
0351: }
0352:
0353: this .fixedInterceptorOffset = mainCallbacks.length;
0354: } else {
0355: callbacks = mainCallbacks;
0356: }
0357: return callbacks;
0358: }
0359:
0360: /**
0361: * Wrap a return of this if necessary to be the proxy
0362: */
0363: private static Object massageReturnTypeIfNecessary(Object proxy,
0364: Object target, Method method, Object retVal) {
0365: // Massage return value if necessary
0366: if (retVal != null
0367: && retVal == target
0368: && !RawTargetAccess.class.isAssignableFrom(method
0369: .getDeclaringClass())) {
0370: // Special case: it returned "this".
0371: // Note that we can't help if the target sets a reference
0372: // to itself in another returned object.
0373: retVal = proxy;
0374: }
0375: return retVal;
0376: }
0377:
0378: public boolean equals(Object other) {
0379: return (this == other || (other instanceof Cglib2AopProxy && AopProxyUtils
0380: .equalsInProxy(this .advised,
0381: ((Cglib2AopProxy) other).advised)));
0382: }
0383:
0384: public int hashCode() {
0385: return Cglib2AopProxy.class.hashCode() * 13
0386: + this .advised.getTargetSource().hashCode();
0387: }
0388:
0389: /**
0390: * Serializable replacement for CGLIB's NoOp interface.
0391: * Public to allow use elsewhere in the framework.
0392: */
0393: public static class SerializableNoOp implements NoOp, Serializable {
0394: }
0395:
0396: /**
0397: * Method interceptor used for static targets with no advice chain. The call
0398: * is passed directly back to the target. Used when the proxy needs to be
0399: * exposed and it can't be determined that the method won't return
0400: * <code>this</code>.
0401: */
0402: private static class StaticUnadvisedInterceptor implements
0403: MethodInterceptor, Serializable {
0404:
0405: private final Object target;
0406:
0407: public StaticUnadvisedInterceptor(Object target) {
0408: this .target = target;
0409: }
0410:
0411: public Object intercept(Object proxy, Method method,
0412: Object[] args, MethodProxy methodProxy)
0413: throws Throwable {
0414: Object retVal = methodProxy.invoke(this .target, args);
0415: return massageReturnTypeIfNecessary(proxy, this .target,
0416: method, retVal);
0417: }
0418: }
0419:
0420: /**
0421: * Method interceptor used for static targets with no advice chain, when the
0422: * proxy is to be exposed.
0423: */
0424: private static class StaticUnadvisedExposedInterceptor implements
0425: MethodInterceptor, Serializable {
0426:
0427: private final Object target;
0428:
0429: public StaticUnadvisedExposedInterceptor(Object target) {
0430: this .target = target;
0431: }
0432:
0433: public Object intercept(Object proxy, Method method,
0434: Object[] args, MethodProxy methodProxy)
0435: throws Throwable {
0436: Object oldProxy = null;
0437: try {
0438: oldProxy = AopContext.setCurrentProxy(proxy);
0439: Object retVal = methodProxy.invoke(this .target, args);
0440: return massageReturnTypeIfNecessary(proxy, this .target,
0441: method, retVal);
0442: } finally {
0443: AopContext.setCurrentProxy(oldProxy);
0444: }
0445: }
0446: }
0447:
0448: /**
0449: * Interceptor used to invoke a dynamic target without creating a method
0450: * invocation or evaluating an advice chain. (We know there was no advice
0451: * for this method.)
0452: */
0453: private static class DynamicUnadvisedInterceptor implements
0454: MethodInterceptor, Serializable {
0455:
0456: private final TargetSource targetSource;
0457:
0458: public DynamicUnadvisedInterceptor(TargetSource targetSource) {
0459: this .targetSource = targetSource;
0460: }
0461:
0462: public Object intercept(Object proxy, Method method,
0463: Object[] args, MethodProxy methodProxy)
0464: throws Throwable {
0465: Object target = this .targetSource.getTarget();
0466: try {
0467: Object retVal = methodProxy.invoke(target, args);
0468: return massageReturnTypeIfNecessary(proxy, target,
0469: method, retVal);
0470: } finally {
0471: this .targetSource.releaseTarget(target);
0472: }
0473: }
0474: }
0475:
0476: /**
0477: * Interceptor for unadvised dynamic targets when the proxy needs exposing.
0478: */
0479: private static class DynamicUnadvisedExposedInterceptor implements
0480: MethodInterceptor, Serializable {
0481:
0482: private final TargetSource targetSource;
0483:
0484: public DynamicUnadvisedExposedInterceptor(
0485: TargetSource targetSource) {
0486: this .targetSource = targetSource;
0487: }
0488:
0489: public Object intercept(Object proxy, Method method,
0490: Object[] args, MethodProxy methodProxy)
0491: throws Throwable {
0492: Object oldProxy = null;
0493: Object target = this .targetSource.getTarget();
0494: try {
0495: oldProxy = AopContext.setCurrentProxy(proxy);
0496: Object retVal = methodProxy.invoke(target, args);
0497: return massageReturnTypeIfNecessary(proxy, target,
0498: method, retVal);
0499: } finally {
0500: AopContext.setCurrentProxy(oldProxy);
0501: this .targetSource.releaseTarget(target);
0502: }
0503: }
0504: }
0505:
0506: /**
0507: * Dispatcher for a static target. Dispatcher is much faster than
0508: * interceptor. This will be used whenever it can be determined that a
0509: * method definitely does not return "this"
0510: */
0511: private static class StaticDispatcher implements Dispatcher,
0512: Serializable {
0513:
0514: private Object target;
0515:
0516: public StaticDispatcher(Object target) {
0517: this .target = target;
0518: }
0519:
0520: public Object loadObject() {
0521: return this .target;
0522: }
0523: }
0524:
0525: /**
0526: * Dispatcher for any methods declared on the Advised class.
0527: */
0528: private static class AdvisedDispatcher implements Dispatcher,
0529: Serializable {
0530:
0531: private final AdvisedSupport advised;
0532:
0533: public AdvisedDispatcher(AdvisedSupport advised) {
0534: this .advised = advised;
0535: }
0536:
0537: public Object loadObject() throws Exception {
0538: return this .advised;
0539: }
0540: }
0541:
0542: /**
0543: * Dispatcher for the <code>equals</code> method.
0544: * Ensures that the method call is always handled by this class.
0545: */
0546: private static class EqualsInterceptor implements
0547: MethodInterceptor, Serializable {
0548:
0549: private final AdvisedSupport advised;
0550:
0551: public EqualsInterceptor(AdvisedSupport advised) {
0552: this .advised = advised;
0553: }
0554:
0555: public Object intercept(Object proxy, Method method,
0556: Object[] args, MethodProxy methodProxy) {
0557: Object other = args[0];
0558: if (proxy == other) {
0559: return Boolean.TRUE;
0560: }
0561: AdvisedSupport otherAdvised = null;
0562: if (other instanceof Factory) {
0563: Callback callback = ((Factory) other)
0564: .getCallback(INVOKE_EQUALS);
0565: if (!(callback instanceof EqualsInterceptor)) {
0566: return Boolean.FALSE;
0567: }
0568: otherAdvised = ((EqualsInterceptor) callback).advised;
0569: } else {
0570: return Boolean.FALSE;
0571: }
0572: return (AopProxyUtils.equalsInProxy(this .advised,
0573: otherAdvised) ? Boolean.TRUE : Boolean.FALSE);
0574: }
0575: }
0576:
0577: /**
0578: * Dispatcher for the <code>hashCode</code> method.
0579: * Ensures that the method call is always handled by this class.
0580: */
0581: private static class HashCodeInterceptor implements
0582: MethodInterceptor, Serializable {
0583:
0584: private final AdvisedSupport advised;
0585:
0586: public HashCodeInterceptor(AdvisedSupport advised) {
0587: this .advised = advised;
0588: }
0589:
0590: public Object intercept(Object proxy, Method method,
0591: Object[] args, MethodProxy methodProxy) {
0592: return new Integer(Cglib2AopProxy.class.hashCode() * 13
0593: + this .advised.getTargetSource().hashCode());
0594: }
0595: }
0596:
0597: /**
0598: * Interceptor used specifically for advised methods on a frozen, static proxy.
0599: */
0600: private static class FixedChainStaticTargetInterceptor implements
0601: MethodInterceptor, Serializable {
0602:
0603: private final List adviceChain;
0604:
0605: private final Object target;
0606:
0607: private final Class targetClass;
0608:
0609: public FixedChainStaticTargetInterceptor(List adviceChain,
0610: Object target, Class targetClass) {
0611: this .adviceChain = adviceChain;
0612: this .target = target;
0613: this .targetClass = targetClass;
0614: }
0615:
0616: public Object intercept(Object proxy, Method method,
0617: Object[] args, MethodProxy methodProxy)
0618: throws Throwable {
0619: Object retVal = null;
0620: MethodInvocation invocation = new CglibMethodInvocation(
0621: proxy, this .target, method, args, this .targetClass,
0622: this .adviceChain, methodProxy);
0623: // If we get here, we need to create a MethodInvocation.
0624: retVal = invocation.proceed();
0625: retVal = massageReturnTypeIfNecessary(proxy, this .target,
0626: method, retVal);
0627: return retVal;
0628: }
0629: }
0630:
0631: /**
0632: * General purpose AOP callback. Used when the target is dynamic or when the
0633: * proxy is not frozen.
0634: */
0635: private static class DynamicAdvisedInterceptor implements
0636: MethodInterceptor, Serializable {
0637:
0638: private AdvisedSupport advised;
0639:
0640: public DynamicAdvisedInterceptor(AdvisedSupport advised) {
0641: this .advised = advised;
0642: }
0643:
0644: public Object intercept(Object proxy, Method method,
0645: Object[] args, MethodProxy methodProxy)
0646: throws Throwable {
0647: MethodInvocation invocation = null;
0648: Object oldProxy = null;
0649: boolean setProxyContext = false;
0650: Class targetClass = null;
0651: Object target = null;
0652: try {
0653: Object retVal = null;
0654: if (this .advised.exposeProxy) {
0655: // Make invocation available if necessary.
0656: oldProxy = AopContext.setCurrentProxy(proxy);
0657: setProxyContext = true;
0658: }
0659: // May be <code>null</code>. Get as late as possible to minimize the time we
0660: // "own" the target, in case it comes from a pool.
0661: target = getTarget();
0662: if (target != null) {
0663: targetClass = target.getClass();
0664: }
0665: List chain = this .advised
0666: .getInterceptorsAndDynamicInterceptionAdvice(
0667: method, targetClass);
0668: // Check whether we only have one InvokerInterceptor: that is,
0669: // no real advice, but just reflective invocation of the target.
0670: if (chain.isEmpty()
0671: && Modifier.isPublic(method.getModifiers())) {
0672: // We can skip creating a MethodInvocation: just invoke the target directly.
0673: // Note that the final invoker must be an InvokerInterceptor, so we know
0674: // it does nothing but a reflective operation on the target, and no hot
0675: // swapping or fancy proxying.
0676: retVal = methodProxy.invoke(target, args);
0677: } else {
0678: // We need to create a method invocation...
0679: invocation = new CglibMethodInvocation(proxy,
0680: target, method, args, targetClass, chain,
0681: methodProxy);
0682: // If we get here, we need to create a MethodInvocation.
0683: retVal = invocation.proceed();
0684: }
0685:
0686: retVal = massageReturnTypeIfNecessary(proxy, target,
0687: method, retVal);
0688: return retVal;
0689: } finally {
0690: if (target != null) {
0691: releaseTarget(target);
0692: }
0693: if (setProxyContext) {
0694: // Restore old proxy.
0695: AopContext.setCurrentProxy(oldProxy);
0696: }
0697: }
0698: }
0699:
0700: public boolean equals(Object other) {
0701: return (this == other || (other instanceof DynamicAdvisedInterceptor && this .advised
0702: .equals(((DynamicAdvisedInterceptor) other).advised)));
0703: }
0704:
0705: /**
0706: * CGLIB uses this to drive proxy creation.
0707: */
0708: public int hashCode() {
0709: return this .advised.hashCode();
0710: }
0711:
0712: protected Object getTarget() throws Exception {
0713: return this .advised.getTargetSource().getTarget();
0714: }
0715:
0716: protected void releaseTarget(Object target) throws Exception {
0717: this .advised.getTargetSource().releaseTarget(target);
0718: }
0719: }
0720:
0721: /**
0722: * Implementation of AOP Alliance MethodInvocation used by this AOP proxy.
0723: */
0724: private static class CglibMethodInvocation extends
0725: ReflectiveMethodInvocation {
0726:
0727: private final MethodProxy methodProxy;
0728:
0729: private boolean protectedMethod;
0730:
0731: public CglibMethodInvocation(Object proxy, Object target,
0732: Method method, Object[] arguments, Class targetClass,
0733: List interceptorsAndDynamicMethodMatchers,
0734: MethodProxy methodProxy) {
0735: super (proxy, target, method, arguments, targetClass,
0736: interceptorsAndDynamicMethodMatchers);
0737: this .methodProxy = methodProxy;
0738: this .protectedMethod = Modifier.isProtected(method
0739: .getModifiers());
0740: }
0741:
0742: /**
0743: * Gives a marginal performance improvement versus using reflection to
0744: * invoke the target when invoking public methods.
0745: */
0746: protected Object invokeJoinpoint() throws Throwable {
0747: if (this .protectedMethod) {
0748: return super .invokeJoinpoint();
0749: } else {
0750: return this .methodProxy.invoke(this .target,
0751: this .arguments);
0752: }
0753: }
0754: }
0755:
0756: /**
0757: * CallbackFilter to assign Callbacks to methods.
0758: */
0759: private static class ProxyCallbackFilter implements CallbackFilter {
0760:
0761: private final AdvisedSupport advised;
0762:
0763: private final Map fixedInterceptorMap;
0764:
0765: private final int fixedInterceptorOffset;
0766:
0767: public ProxyCallbackFilter(AdvisedSupport advised,
0768: Map fixedInterceptorMap, int fixedInterceptorOffset) {
0769: this .advised = advised;
0770: this .fixedInterceptorMap = fixedInterceptorMap;
0771: this .fixedInterceptorOffset = fixedInterceptorOffset;
0772: }
0773:
0774: /**
0775: * Implementation of CallbackFilter.accept() to return the index of the
0776: * callback we need.
0777: * <p>The callbacks for each proxy are built up of a set of fixed callbacks
0778: * for general use and then a set of callbacks that are specific to a method
0779: * for use on static targets with a fixed advice chain.
0780: * <p>The callback used is determined thus:
0781: * <dl>
0782: * <dt>For exposed proxies</dt>
0783: * <dd>Exposing the proxy requires code to execute before and after the
0784: * method/chain invocation. This means we must use
0785: * DynamicAdvisedInterceptor, since all other interceptors can avoid the
0786: * need for a try/catch block</dd>
0787: * <dt>For Object.finalize():</dt>
0788: * <dd>No override for this method is used</dd>
0789: * <dt>For equals():</dt>
0790: * <dd>The EqualsInterceptor is used to redirect equals() calls to a
0791: * special handler to this proxy.</dd>
0792: * <dt>For methods on the Advised class:</dt>
0793: * <dd>the AdvisedDispatcher is used to dispatch the call directly to
0794: * the target</dd>
0795: * <dt>For advised methods:</dt>
0796: * <dd>If the target is static and the advice chain is frozen then a
0797: * FixedChainStaticTargetInterceptor specific to the method is used to
0798: * invoke the advice chain. Otherwise a DyanmicAdvisedInterceptor is
0799: * used.</dd>
0800: * <dt>For non-advised methods:</dt>
0801: * <dd>Where it can be determined that the method will not return <code>this</code>
0802: * or when <code>ProxyFactory.getExposeProxy()</code> returns <code>false</code>,
0803: * then a Dispatcher is used. For static targets, the StaticDispatcher is used;
0804: * and for dynamic targets, a DynamicUnadvisedInterceptor is used.
0805: * If it possible for the method to return <code>this</code> then a
0806: * StaticUnadvisedInterceptor is used for static targets - the
0807: * DynamicUnadvisedInterceptor already considers this.</dd>
0808: * </dl>
0809: */
0810: public int accept(Method method) {
0811: if (method.getDeclaringClass() == Object.class
0812: && method.getName().equals("finalize")) {
0813: logger
0814: .debug("Found finalize() method - using NO_OVERRIDE");
0815: return NO_OVERRIDE;
0816: }
0817: if (!this .advised.isOpaque()
0818: && method.getDeclaringClass().isInterface()
0819: && method.getDeclaringClass().isAssignableFrom(
0820: Advised.class)) {
0821: if (logger.isDebugEnabled()) {
0822: logger
0823: .debug("Method is declared on Advised interface: "
0824: + method);
0825: }
0826: return DISPATCH_ADVISED;
0827: }
0828: // We must always proxy equals, to direct calls to this.
0829: if (AopUtils.isEqualsMethod(method)) {
0830: logger.debug("Found 'equals' method: " + method);
0831: return INVOKE_EQUALS;
0832: }
0833: // We must always calculate hashCode based on the proxy.
0834: if (AopUtils.isHashCodeMethod(method)) {
0835: logger.debug("Found 'hashCode' method: " + method);
0836: return INVOKE_HASHCODE;
0837: }
0838: Class targetClass = this .advised.getTargetClass();
0839: // Proxy is not yet available, but that shouldn't matter.
0840: List chain = this .advised
0841: .getInterceptorsAndDynamicInterceptionAdvice(
0842: method, targetClass);
0843: boolean haveAdvice = !chain.isEmpty();
0844: boolean exposeProxy = this .advised.isExposeProxy();
0845: boolean isStatic = this .advised.getTargetSource()
0846: .isStatic();
0847: boolean isFrozen = this .advised.isFrozen();
0848: if (haveAdvice || !isFrozen) {
0849: // If exposing the proxy, then AOP_PROXY must be used.
0850: if (exposeProxy) {
0851: if (logger.isDebugEnabled()) {
0852: logger
0853: .debug("Must expose proxy on advised method: "
0854: + method);
0855: }
0856: return AOP_PROXY;
0857: }
0858: String key = method.toString();
0859: // Check to see if we have fixed interceptor to serve this method.
0860: // Else use the AOP_PROXY.
0861: if (isStatic && isFrozen
0862: && this .fixedInterceptorMap.containsKey(key)) {
0863: if (logger.isDebugEnabled()) {
0864: logger
0865: .debug("Method has advice and optimisations are enabled: "
0866: + method);
0867: }
0868: // We know that we are optimising so we can use the
0869: // FixedStaticChainInterceptors.
0870: int index = ((Integer) this .fixedInterceptorMap
0871: .get(key)).intValue();
0872: return (index + this .fixedInterceptorOffset);
0873: } else {
0874: if (logger.isDebugEnabled()) {
0875: logger
0876: .debug("Unable to apply any optimisations to advised method: "
0877: + method);
0878: }
0879: return AOP_PROXY;
0880: }
0881: } else {
0882: // See if the return type of the method is outside the class hierarchy
0883: // of the target type. If so we know it never needs to have return type
0884: // massage and can use a dispatcher.
0885: // If the proxy is being exposed, then must use the interceptor the
0886: // correct one is already configured. If the target is not static cannot
0887: // use a Dispatcher because the target can not then be released.
0888: if (exposeProxy || !isStatic) {
0889: return INVOKE_TARGET;
0890: }
0891: Class returnType = method.getReturnType();
0892: if (targetClass == returnType) {
0893: if (logger.isDebugEnabled()) {
0894: logger
0895: .debug("Method "
0896: + method
0897: + "has return type same as target type (may return this) - using INVOKE_TARGET");
0898: }
0899: return INVOKE_TARGET;
0900: } else if (returnType.isPrimitive()
0901: || !returnType.isAssignableFrom(targetClass)) {
0902: if (logger.isDebugEnabled()) {
0903: logger
0904: .debug("Method "
0905: + method
0906: + " has return type that ensures this cannot be returned- using DISPATCH_TARGET");
0907: }
0908: return DISPATCH_TARGET;
0909: } else {
0910: if (logger.isDebugEnabled()) {
0911: logger
0912: .debug("Method "
0913: + method
0914: + "has return type that is assignable from the target type (may return this) - "
0915: + "using INVOKE_TARGET");
0916: }
0917: return INVOKE_TARGET;
0918: }
0919: }
0920: }
0921:
0922: public boolean equals(Object other) {
0923: if (other == this ) {
0924: return true;
0925: }
0926: if (!(other instanceof ProxyCallbackFilter)) {
0927: return false;
0928: }
0929: ProxyCallbackFilter otherCallbackFilter = (ProxyCallbackFilter) other;
0930: AdvisedSupport otherAdvised = otherCallbackFilter.advised;
0931: if (this .advised == null || otherAdvised == null) {
0932: return false;
0933: }
0934: if (this .advised.isFrozen() != otherAdvised.isFrozen()) {
0935: return false;
0936: }
0937: if (this .advised.isExposeProxy() != otherAdvised
0938: .isExposeProxy()) {
0939: return false;
0940: }
0941: if (this .advised.getTargetSource().isStatic() != otherAdvised
0942: .getTargetSource().isStatic()) {
0943: return false;
0944: }
0945: if (!AopProxyUtils.equalsProxiedInterfaces(this .advised,
0946: otherAdvised)) {
0947: return false;
0948: }
0949: // Advice instance identity is unimportant to the proxy class:
0950: // All that matters is type and ordering.
0951: Advisor[] this Advisors = this .advised.getAdvisors();
0952: Advisor[] thatAdvisors = otherAdvised.getAdvisors();
0953: if (this Advisors.length != thatAdvisors.length) {
0954: return false;
0955: }
0956: for (int i = 0; i < this Advisors.length; i++) {
0957: Advisor this Advisor = this Advisors[i];
0958: Advisor thatAdvisor = thatAdvisors[i];
0959: if (!equalsAdviceClasses(this Advisor, thatAdvisor)) {
0960: return false;
0961: }
0962: if (!equalsPointcuts(this Advisor, thatAdvisor)) {
0963: return false;
0964: }
0965: }
0966: return true;
0967: }
0968:
0969: private boolean equalsAdviceClasses(Advisor a, Advisor b) {
0970: Advice aa = a.getAdvice();
0971: Advice ba = b.getAdvice();
0972: if (aa == null || ba == null) {
0973: return (aa == ba);
0974: }
0975: return aa.getClass().equals(ba.getClass());
0976: }
0977:
0978: private boolean equalsPointcuts(Advisor a, Advisor b) {
0979: // If only one of the advisor (but not both) is PointcutAdvisor, then it is a mismatch
0980: // Takes care of the situations where an IntroductionAdvisor is used (see SPR-3959)
0981: if (a instanceof PointcutAdvisor
0982: ^ b instanceof PointcutAdvisor) {
0983: return false;
0984: }
0985: // If both are PointcutAdvisor, match their pointcuts
0986: if (a instanceof PointcutAdvisor
0987: && b instanceof PointcutAdvisor) {
0988: return ObjectUtils.nullSafeEquals(((PointcutAdvisor) a)
0989: .getPointcut(), ((PointcutAdvisor) b)
0990: .getPointcut());
0991: }
0992: // If neither is PointcutAdvisor, then from the pointcut matching perspective, it is a match
0993: return true;
0994: }
0995:
0996: public int hashCode() {
0997: int hashCode = 0;
0998: Advisor[] advisors = this .advised.getAdvisors();
0999: for (int i = 0; i < advisors.length; i++) {
1000: Advice advice = advisors[i].getAdvice();
1001: if (advice != null) {
1002: hashCode = 13 * hashCode
1003: + advice.getClass().hashCode();
1004: }
1005: }
1006: hashCode = 13 * hashCode
1007: + (this .advised.isFrozen() ? 1 : 0);
1008: hashCode = 13 * hashCode
1009: + (this .advised.isExposeProxy() ? 1 : 0);
1010: hashCode = 13 * hashCode
1011: + (this .advised.isOptimize() ? 1 : 0);
1012: hashCode = 13 * hashCode
1013: + (this .advised.isOpaque() ? 1 : 0);
1014: return hashCode;
1015: }
1016: }
1017:
1018: }
|