1. /*
  2. * Copyright 2002-2004 the original author or authors.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. package org.springframework.aop.framework;
  17. import java.lang.reflect.Method;
  18. import java.lang.reflect.UndeclaredThrowableException;
  19. import java.util.List;
  20. import net.sf.cglib.core.CodeGenerationException;
  21. import net.sf.cglib.proxy.Callback;
  22. import net.sf.cglib.proxy.CallbackFilter;
  23. import net.sf.cglib.proxy.Enhancer;
  24. import net.sf.cglib.proxy.Factory;
  25. import net.sf.cglib.proxy.MethodInterceptor;
  26. import net.sf.cglib.proxy.MethodProxy;
  27. import net.sf.cglib.proxy.NoOp;
  28. import org.aopalliance.aop.AspectException;
  29. import org.aopalliance.intercept.MethodInvocation;
  30. import org.apache.commons.logging.Log;
  31. import org.apache.commons.logging.LogFactory;
  32. import org.springframework.aop.TargetSource;
  33. /**
  34. * CGLIB 2 AopProxy implementation for the Spring AOP framework.
  35. * Also implements the CGLIB MethodInterceptor and CallbackFilter
  36. * interfaces.
  37. *
  38. * <p>Objects of this type should be obtained through proxy factories,
  39. * configured by a AdvisedSupport implementation. This class is internal
  40. * to the Spring framework and need not be used directly by client code.
  41. *
  42. * <p>Proxies created using this class are threadsafe if the
  43. * underlying (target) class is threadsafe.
  44. *
  45. * @author Rod Johnson
  46. * @version $Id: Cglib2AopProxy.java,v 1.7 2004/04/01 15:35:46 jhoeller Exp $
  47. */
  48. class Cglib2AopProxy implements AopProxy, MethodInterceptor, CallbackFilter {
  49. // Constants for CGLIB callback array indices
  50. private static final int AOP_PROXY = 0;
  51. private static final int INVOKE_TARGET = 1;
  52. private static final int NO_OVERRIDE = 2;
  53. protected final Log logger = LogFactory.getLog(getClass());
  54. /** Config used to configure this proxy */
  55. protected final AdvisedSupport advised;
  56. /**
  57. *
  58. * @throws AopConfigException if the config is invalid. We try
  59. * to throw an informative exception in this case, rather than let
  60. * a mysterious failure happen later.
  61. */
  62. protected Cglib2AopProxy(AdvisedSupport config) throws AopConfigException {
  63. if (config == null)
  64. throw new AopConfigException("Cannot create AopProxy with null ProxyConfig");
  65. if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE)
  66. throw new AopConfigException("Cannot create AopProxy with no advisors and no target source");
  67. this.advised = config;
  68. if (this.advised.getTargetSource().getTargetClass() == null) {
  69. throw new AopConfigException("Either an interface or a target is required for proxy creation");
  70. }
  71. }
  72. /**
  73. * Implementation of MethodInterceptor.
  74. * Callers will see exactly the exception thrown by the target, unless a hook
  75. * method throws an exception.
  76. */
  77. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  78. MethodInvocation invocation = null;
  79. Object oldProxy = null;
  80. boolean setProxyContext = false;
  81. TargetSource targetSource = advised.targetSource;
  82. Class targetClass = null;//targetSource.getTargetClass();
  83. Object target = null;
  84. try {
  85. // Try special rules for equals() method and implementation of the
  86. // ProxyConfig AOP configuration interface
  87. if (isEqualsMethod(method)) {
  88. // This class implements the equals() method itself
  89. // We don't need to use reflection
  90. return new Boolean(equals(args[0]));
  91. }
  92. else if (Advised.class == method.getDeclaringClass()) {
  93. // Service invocations on ProxyConfig with the proxy config
  94. return AopProxyUtils.invokeJoinpointUsingReflection(this.advised, method, args);
  95. }
  96. Object retVal = null;
  97. // May be null. Get as late as possible to minimize the time we "own" the target,
  98. // in case it comes from a pool.
  99. target = targetSource.getTarget();
  100. if (target != null) {
  101. targetClass = target.getClass();
  102. }
  103. if (this.advised.exposeProxy) {
  104. // Make invocation available if necessary
  105. oldProxy = AopContext.setCurrentProxy(proxy);
  106. setProxyContext = true;
  107. }
  108. List chain = advised.getAdvisorChainFactory().getInterceptorsAndDynamicInterceptionAdvice(this.advised, proxy, method, targetClass);
  109. // Check whether we only have one InvokerInterceptor: that is, no real advice,
  110. // but just reflective invocation of the target.
  111. if (chain.isEmpty()) {
  112. // We can skip creating a MethodInvocation: just invoke the target directly
  113. // Note that the final invoker must be an InvokerInterceptor so we know it does
  114. // nothing but a reflective operation on the target, and no hot swapping or fancy proxying
  115. retVal = methodProxy.invoke(target, args);
  116. }
  117. else {
  118. // We need to create a method invocation...
  119. invocation = new MethodInvocationImpl(proxy, target, method, args,
  120. targetClass, chain, methodProxy);
  121. // If we get here, we need to create a MethodInvocation
  122. retVal = invocation.proceed();
  123. }
  124. retVal = massageReturnTypeIfNecessary(proxy, target, retVal);
  125. return retVal;
  126. }
  127. catch (Throwable t) {
  128. // In CGLIB2, unlike CGLIB 1, it's necessary to wrap
  129. // undeclared throwable exceptions. As we don't care about JDK 1.2
  130. // compatibility, we use java.lang.reflect.UndeclaredThrowableException.
  131. if ( (t instanceof Exception) && !(t instanceof RuntimeException)) {
  132. // It's a checked exception: we must check it's legal
  133. Class[] permittedThrows = method.getExceptionTypes();
  134. for (int i = 0; i < permittedThrows.length; i++) {
  135. if (permittedThrows[i].isAssignableFrom(t.getClass())) {
  136. throw t;
  137. }
  138. //System.err.println("No match t=" + t + " throws=" + permittedThrows[i]);
  139. }
  140. throw new UndeclaredThrowableException(t);
  141. }
  142. // It's not a checked exception, so we can rethrow it
  143. throw t;
  144. }
  145. finally {
  146. if (target != null && !targetSource.isStatic()) {
  147. // Must have come from TargetSource
  148. targetSource.releaseTarget(target);
  149. }
  150. if (setProxyContext) {
  151. // Restore old proxy
  152. AopContext.setCurrentProxy(oldProxy);
  153. }
  154. }
  155. } // intercept
  156. /**
  157. * Wrap a return of this if necessary to be the proxy
  158. */
  159. protected static Object massageReturnTypeIfNecessary(Object proxy, Object target, Object retVal) {
  160. // Massage return value if necessary
  161. if (retVal != null && retVal == target) {
  162. // Special case: it returned "this"
  163. // Note that we can't help if the target sets
  164. // a reference to itself in another returned object
  165. retVal = proxy;
  166. }
  167. return retVal;
  168. }
  169. /**
  170. * Is the given method the equals method?
  171. */
  172. protected final boolean isEqualsMethod(Method m) {
  173. return "equals".equals(m.getName()) &&
  174. m.getParameterTypes().length == 1 &&
  175. m.getParameterTypes()[0] == Object.class;
  176. }
  177. /**
  178. * Create a new Proxy object for the given object, proxying
  179. * the given interface. Uses the thread context class loader.
  180. */
  181. public Object getProxy() {
  182. return getProxy(Thread.currentThread().getContextClassLoader());
  183. }
  184. /**
  185. * Create a new Proxy object for the given object, proxying
  186. * the given interface. Uses the given class loader.
  187. */
  188. public Object getProxy(ClassLoader cl) {
  189. if (logger.isDebugEnabled()) {
  190. logger.debug("Creating CGLIB proxy for [" + this.advised.getTargetSource().getTargetClass() + "]");
  191. }
  192. Enhancer e = new Enhancer();
  193. try {
  194. e.setSuperclass(advised.getTargetSource().getTargetClass());
  195. e.setCallbackFilter(this);
  196. e.setInterfaces(AopProxyUtils.completeProxiedInterfaces(advised));
  197. Callback targetInvoker = canApplyCglibOptimizations() ?
  198. (Callback) new StaticTargetInvoker(advised.getTargetSource().getTarget()) :
  199. (Callback) new DynamicTargetInvoker();
  200. e.setCallbacks(new Callback[] {
  201. this, // For normal advice
  202. targetInvoker, // invoke target without considering advice, if optimized
  203. NoOp.INSTANCE // no override for methods mapped to this
  204. });
  205. return e.create();
  206. }
  207. catch (CodeGenerationException ex) {
  208. throw new AspectException("Couldn't generate CGLIB subclass of class '" + advised.getTargetSource().getTargetClass() + "': " +
  209. "Common causes of this problem include using a final class, or a non-visible class", ex);
  210. }
  211. catch (Exception ex) {
  212. // TargetSource getTarget failed
  213. throw new AopConfigException("Unexpected AOP exception", ex);
  214. }
  215. }
  216. /**
  217. * Invoker used to invoke the target without creating a method invocation
  218. * or evaluating an advice chain. (We know there was no advice for this method.)
  219. */
  220. private class DynamicTargetInvoker implements MethodInterceptor {
  221. /**
  222. * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
  223. */
  224. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  225. Object target = advised.getTargetSource().getTarget();
  226. Object ret = methodProxy.invoke(target, args);
  227. return massageReturnTypeIfNecessary(proxy, target, ret);
  228. }
  229. }
  230. /**
  231. * Like DynamicTargetInvoker, for use when there's a static TargetSource
  232. */
  233. private static class StaticTargetInvoker implements MethodInterceptor {
  234. private final Object target;
  235. public StaticTargetInvoker(Object target) {
  236. this.target = target;
  237. }
  238. /**
  239. * @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
  240. */
  241. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  242. Object ret = methodProxy.invoke(target, args);
  243. return massageReturnTypeIfNecessary(proxy, target, ret);
  244. }
  245. }
  246. /**
  247. * Given the Advised object we have, can we apply CGLIB optimizations
  248. * (directly invoking the target for methods with no advice)?
  249. */
  250. private boolean canApplyCglibOptimizations() {
  251. return advised.getOptimize() &&
  252. advised.getTargetSource().isStatic() &&
  253. !advised.getExposeProxy();
  254. }
  255. /**
  256. * Implementation of CallbackFilter.accept() to return the index of the
  257. * callback we need. This will mean either no overriding,
  258. * AOP_PROXY (run through our intercept method) or INVOKE_TARGET
  259. * (optimized direct invocation of target without re-evaluating
  260. * advice chain at runtime).
  261. * @see net.sf.cglib.proxy.CallbackFilter#accept(java.lang.reflect.Method)
  262. */
  263. public int accept(Method method) {
  264. if (method.getName().equals("finalize") && method.getDeclaringClass() == Object.class) {
  265. return NO_OVERRIDE;
  266. }
  267. if (!canApplyCglibOptimizations()) {
  268. return AOP_PROXY;
  269. }
  270. // Could consider more aggressive optimization in which we have a distinct
  271. // callback with the advice chain for each method, but it's probably not
  272. // worth it
  273. // We can apply optimizations
  274. // The optimization means that we evaluate whether or not there's an
  275. // advice chain once only, befre each invocation.
  276. Class targetClass = advised.getTargetSource().getTargetClass();
  277. // We must always proxy equals, to direct calls to this
  278. if (isEqualsMethod(method))
  279. return AOP_PROXY;
  280. // Proxy is not yet available, but that shouldn't matter
  281. List chain = advised.getAdvisorChainFactory().getInterceptorsAndDynamicInterceptionAdvice(advised, null, method, targetClass);
  282. boolean haveAdvice = !chain.isEmpty();
  283. if (haveAdvice) {
  284. logger.info("CGLIB proxy for " + targetClass.getName() +
  285. " WILL override " + method);
  286. }
  287. else {
  288. logger.info("Chain is empty for " + method + "; will NOT override");
  289. }
  290. return haveAdvice ? AOP_PROXY : INVOKE_TARGET;
  291. }
  292. /**
  293. * Equality means interceptors and interfaces are ==.
  294. * @see java.lang.Object#equals(java.lang.Object)
  295. * @param other may be a dynamic proxy wrapping an instance
  296. * of this class
  297. */
  298. public boolean equals(Object other) {
  299. if (other == null)
  300. return false;
  301. if (other == this)
  302. return true;
  303. Cglib2AopProxy otherCglibProxy = null;
  304. if (other instanceof Cglib2AopProxy) {
  305. otherCglibProxy = (Cglib2AopProxy) other;
  306. }
  307. else if (other instanceof Factory) {
  308. // The 0th callback will be the Cglib2AopProxy if we're correct
  309. Callback callback = ((Factory) other).getCallback(AOP_PROXY);
  310. if (!(callback instanceof Cglib2AopProxy))
  311. return false;
  312. otherCglibProxy = (Cglib2AopProxy) callback;
  313. }
  314. else {
  315. // Not a valid comparison
  316. return false;
  317. }
  318. return AopProxyUtils.equalsInProxy(advised, otherCglibProxy.advised);
  319. }
  320. /**
  321. * Implementation of AOP Alliance MethodInvocation used by this AOP proxy
  322. */
  323. private static class MethodInvocationImpl extends ReflectiveMethodInvocation {
  324. private MethodProxy methodProxy;
  325. public MethodInvocationImpl(Object proxy, Object target, Method m, Object[] arguments, Class targetClass,
  326. List interceptorsAndDynamicMethodMatchers,
  327. MethodProxy methodProxy) {
  328. super(proxy, target, m, arguments, targetClass, interceptorsAndDynamicMethodMatchers);
  329. this.methodProxy = methodProxy;
  330. }
  331. /**
  332. * Gives a marginal performance improvement versus using reflection to invoke the target.
  333. * @see org.springframework.aop.framework.ReflectiveMethodInvocation#invokeJoinpoint()
  334. */
  335. protected Object invokeJoinpoint() throws Throwable {
  336. return methodProxy.invoke(target, arguments);
  337. }
  338. }
  339. }