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.InvocationHandler;
  18. import java.lang.reflect.Method;
  19. import java.lang.reflect.Proxy;
  20. import java.util.List;
  21. import org.aopalliance.intercept.MethodInvocation;
  22. import org.apache.commons.logging.Log;
  23. import org.apache.commons.logging.LogFactory;
  24. import org.springframework.aop.TargetSource;
  25. /**
  26. * InvocationHandler implementation for the Spring AOP framework,
  27. * based on J2SE 1.3+ dynamic proxies.
  28. *
  29. * <p>Creates a J2SE proxy, implementing the interfaces exposed by the
  30. * proxy. Dynamic proxies cannot be used to proxy methods defined in
  31. * classes, rather than interface.
  32. *
  33. * <p>Objects of this type should be obtained through proxy factories,
  34. * configured by an AdvisedSupport class. This class is internal
  35. * to the Spring framework and need not be used directly by client code.
  36. *
  37. * <p>Proxies created using this class will be threadsafe if the
  38. * underlying (target) class is threadsafe.
  39. *
  40. * @author Rod Johnson
  41. * @author Juergen Hoeller
  42. * @version $Id: JdkDynamicAopProxy.java,v 1.14 2004/04/01 15:35:46 jhoeller Exp $
  43. * @see java.lang.reflect.Proxy
  44. * @see org.springframework.aop.framework.AdvisedSupport
  45. * @see org.springframework.aop.framework.ProxyFactory
  46. */
  47. final class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
  48. /*
  49. * NOTE: We could avoid the code duplication between this class and the CGLIB
  50. * proxies by refactoring invoke() into a template method. However, this approach
  51. * adds at least 10% performance overhead versus a copy-paste solution, so we sacrifice
  52. * elegance for performance. (We have a good test suite to ensure that the different
  53. * proxies behave the same :-)
  54. * This way, we can also more easily take advantage of minor optimizations in each class.
  55. */
  56. private final Log logger = LogFactory.getLog(getClass());
  57. /** Config used to configure this proxy */
  58. private final AdvisedSupport advised;
  59. /**
  60. * Construct a new JDK proxy.
  61. * @throws AopConfigException if the config is invalid. We try
  62. * to throw an informative exception in this case, rather than let
  63. * a mysterious failure happen later.
  64. */
  65. protected JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
  66. if (config == null)
  67. throw new AopConfigException("Cannot create AopProxy with null ProxyConfig");
  68. if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE)
  69. throw new AopConfigException("Cannot create AopProxy with no advisors and no target source");
  70. this.advised = config;
  71. }
  72. /**
  73. * Implementation of InvocationHandler.invoke.
  74. * Callers will see exactly the exception thrown by the target, unless a hook
  75. * method throws an exception.
  76. */
  77. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  78. MethodInvocation invocation = null;
  79. Object oldProxy = null;
  80. boolean setProxyContext = false;
  81. TargetSource targetSource = advised.targetSource;
  82. Class targetClass = null;
  83. Object target = null;
  84. try {
  85. // Try special rules for equals() method and implementation of the
  86. // Advised AOP configuration interface
  87. // Short-circuit expensive Method.equals() call, as Object.equals() isn't overloaded
  88. if (method.getDeclaringClass() == Object.class && "equals".equals(method.getName())) {
  89. // What if equals throws exception!?
  90. // This class implements the equals() method itself
  91. return new Boolean(equals(args[0]));
  92. }
  93. else if (Advised.class == method.getDeclaringClass()) {
  94. // Service invocations on ProxyConfig with the proxy config
  95. return AopProxyUtils.invokeJoinpointUsingReflection(this.advised, method, args);
  96. }
  97. Object retVal = null;
  98. // May be null. Get as late as possible to minimize the time we "own" the target,
  99. // in case it comes from a pool.
  100. target = targetSource.getTarget();
  101. if (target != null) {
  102. targetClass = target.getClass();
  103. }
  104. if (this.advised.exposeProxy) {
  105. // Make invocation available if necessary
  106. oldProxy = AopContext.setCurrentProxy(proxy);
  107. setProxyContext = true;
  108. }
  109. // Get the interception chain for this method
  110. List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
  111. this.advised, proxy, method, targetClass);
  112. // Check whether we have any advice. If we don't, we can fallback on
  113. // direct reflective invocation of the target, and avoid creating a MethodInvocation
  114. if (chain.isEmpty()) {
  115. // We can skip creating a MethodInvocation: just invoke the target directly
  116. // Note that the final invoker must be an InvokerInterceptor so we know it does
  117. // nothing but a reflective operation on the target, and no hot swapping or fancy proxying
  118. retVal = AopProxyUtils.invokeJoinpointUsingReflection(target, method, args);
  119. }
  120. else {
  121. // We need to create a method invocation...
  122. //invocation = advised.getMethodInvocationFactory().getMethodInvocation(proxy, method, targetClass, target, args, chain, advised);
  123. invocation = new ReflectiveMethodInvocation(proxy, target,
  124. method, args, targetClass, chain);
  125. // Proceed to the joinpoint through the interceptor chain
  126. retVal = invocation.proceed();
  127. }
  128. // Massage return value if necessary
  129. if (retVal != null && retVal == target) {
  130. // Special case: it returned "this"
  131. // Note that we can't help if the target sets
  132. // a reference to itself in another returned object
  133. retVal = proxy;
  134. }
  135. return retVal;
  136. }
  137. finally {
  138. if (target != null && !targetSource.isStatic()) {
  139. // Must have come from TargetSource
  140. targetSource.releaseTarget(target);
  141. }
  142. if (setProxyContext) {
  143. // Restore old proxy
  144. AopContext.setCurrentProxy(oldProxy);
  145. }
  146. //if (invocation != null) {
  147. // advised.getMethodInvocationFactory().release(invocation);
  148. //}
  149. }
  150. }
  151. /**
  152. * Create a new Proxy object for the given object, proxying
  153. * the given interface. Uses the thread context class loader.
  154. */
  155. public Object getProxy() {
  156. return getProxy(Thread.currentThread().getContextClassLoader());
  157. }
  158. /**
  159. * Create a new Proxy object for the given object, proxying
  160. * the given interface. Uses the given class loader.
  161. */
  162. public Object getProxy(ClassLoader cl) {
  163. if (logger.isDebugEnabled()) {
  164. logger.debug("Creating JDK dynamic proxy for [" + this.advised.getTargetSource().getTargetClass() + "]");
  165. }
  166. Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
  167. return Proxy.newProxyInstance(cl, proxiedInterfaces, this);
  168. }
  169. /**
  170. * Equality means interceptors and interfaces and
  171. * TargetSource are equal.
  172. * @see java.lang.Object#equals(java.lang.Object)
  173. * @param other may be a dynamic proxy wrapping an instance
  174. * of this class
  175. */
  176. public boolean equals(Object other) {
  177. if (other == null)
  178. return false;
  179. if (other == this)
  180. return true;
  181. JdkDynamicAopProxy aopr2 = null;
  182. if (other instanceof JdkDynamicAopProxy) {
  183. aopr2 = (JdkDynamicAopProxy) other;
  184. }
  185. else if (Proxy.isProxyClass(other.getClass())) {
  186. InvocationHandler ih = Proxy.getInvocationHandler(other);
  187. if (!(ih instanceof JdkDynamicAopProxy))
  188. return false;
  189. aopr2 = (JdkDynamicAopProxy) ih;
  190. }
  191. else {
  192. // Not a valid comparison
  193. return false;
  194. }
  195. // If we get here, aopr2 is the other AopProxy
  196. return AopProxyUtils.equalsInProxy(this.advised, aopr2.advised);
  197. }
  198. }