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