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.support;
  17. import java.lang.reflect.Method;
  18. import java.lang.reflect.Proxy;
  19. import java.util.ArrayList;
  20. import java.util.List;
  21. import org.springframework.aop.Advisor;
  22. import org.springframework.aop.IntroductionAdvisor;
  23. import org.springframework.aop.Pointcut;
  24. import org.springframework.aop.PointcutAdvisor;
  25. /**
  26. * Utility methods used by the AOP framework.
  27. * Not intended to be used directly by applications.
  28. * @author Rod Johnson
  29. * @author Juergen Hoeller
  30. * @version $Id: AopUtils.java,v 1.2 2004/05/23 20:50:29 jhoeller Exp $
  31. */
  32. public abstract class AopUtils {
  33. /**
  34. * Return whether the given object is a J2SE dynamic proxy.
  35. * @see java.lang.reflect.Proxy#isProxyClass
  36. */
  37. public static boolean isJdkDynamicProxy(Object o) {
  38. return Proxy.isProxyClass(o.getClass());
  39. }
  40. /**
  41. * Return whether the given object is a CGLIB proxy.
  42. */
  43. public static boolean isCglibProxy(Object o) {
  44. return o.getClass().getName().indexOf("$$") != -1;
  45. }
  46. /**
  47. * Return whether the given object is either a J2SE dynamic
  48. * proxy or a CGLIB proxy.
  49. * @see #isJdkDynamicProxy
  50. * @see #isCglibProxy
  51. */
  52. public static boolean isAopProxy(Object o) {
  53. return isJdkDynamicProxy(o) || isCglibProxy(o);
  54. }
  55. /**
  56. * Given a method, which may come from an interface, and a targetClass
  57. * used in the current AOP invocation, find the most specific method
  58. * if there is one. E.g. the method may be IFoo.bar() and the target
  59. * class may be DefaultFoo. In this case, the method may be
  60. * DefaultFoo.bar(). This enables attributes on that method to be found.
  61. * @param method method to be invoked, which may come from an interface
  62. * @param targetClass target class for the curren invocation. May
  63. * be null or may not even implement the method.
  64. * @return the more specific method, or the original method if the
  65. * targetClass doesn't specialize it or implement it or is null
  66. */
  67. public static Method getMostSpecificMethod(Method method, Class targetClass) {
  68. if (targetClass != null) {
  69. try {
  70. method = targetClass.getMethod(method.getName(), method.getParameterTypes());
  71. }
  72. catch (NoSuchMethodException ex) {
  73. // Perhaps the target class doesn't implement this method:
  74. // that's fine, just use the original method
  75. }
  76. }
  77. return method;
  78. }
  79. /**
  80. * Convenience method to convert a string array of interface names
  81. * to a class array.
  82. * @throws IllegalArgumentException if any of the classes is not an interface
  83. * @throws ClassNotFoundException if any of the classes can't be loaded
  84. * @return an array of interface classes
  85. */
  86. public static Class[] toInterfaceArray(String[] interfaceNames)
  87. throws IllegalArgumentException, ClassNotFoundException {
  88. Class interfaces[] = new Class[interfaceNames.length];
  89. for (int i = 0; i < interfaceNames.length; i++) {
  90. interfaces[i] = Class.forName(interfaceNames[i], true, Thread.currentThread().getContextClassLoader());
  91. // Check it's an interface
  92. if (!interfaces[i].isInterface())
  93. throw new IllegalArgumentException("Can proxy only interfaces: " + interfaces[i] + " is a class");
  94. }
  95. return interfaces;
  96. }
  97. /**
  98. * Return all interfaces that the given object implements as array,
  99. * including ones implemented by superclasses.
  100. * @param object the object to analyse for interfaces
  101. * @return all interfaces that the given object implements as array
  102. */
  103. public static Class[] getAllInterfaces(Object object) {
  104. List interfaces = getAllInterfacesAsList(object);
  105. return (Class[]) interfaces.toArray(new Class[interfaces.size()]);
  106. }
  107. /**
  108. * Return all interfaces that the given object implements as List,
  109. * including ones implemented by superclasses.
  110. * @param object the object to analyse for interfaces
  111. * @return all interfaces that the given object implements as List
  112. */
  113. public static List getAllInterfacesAsList(Object object) {
  114. List interfaces = new ArrayList();
  115. Class clazz = object.getClass();
  116. while (clazz != null) {
  117. for (int i = 0; i < clazz.getInterfaces().length; i++) {
  118. Class ifc = clazz.getInterfaces()[i];
  119. interfaces.add(ifc);
  120. }
  121. clazz = clazz.getSuperclass();
  122. }
  123. return interfaces;
  124. }
  125. /**
  126. * Is the given method declared on one of these interfaces?
  127. * @param m method to check
  128. * @param interfaces array of interfaces we want to check
  129. * @return whether the method is declared on one of these interfaces
  130. */
  131. public static boolean methodIsOnOneOfTheseInterfaces(Method m, Class[] interfaces) {
  132. if (interfaces == null)
  133. return false;
  134. for (int i = 0; i < interfaces.length; i++) {
  135. if (!interfaces[i].isInterface())
  136. throw new IllegalArgumentException(interfaces[i].getName() + " is not an interface");
  137. // TODO check that the method with this name actually comes from the interface?
  138. try {
  139. interfaces[i].getDeclaredMethod(m.getName(), m.getParameterTypes());
  140. return true;
  141. }
  142. catch (NoSuchMethodException ex) {
  143. // Didn't find it...keep going
  144. }
  145. } // for
  146. return false;
  147. }
  148. /**
  149. * Can the given pointcut apply at all on the given class?
  150. * This is an important test as it can be used to optimize
  151. * out a pointcut for a class
  152. * @param pc pc static or dynamic pointcut
  153. * @param targetClass class we're testing
  154. * @param proxyInterfaces proxy interfaces. If null, all methods
  155. * on class may be proxied
  156. * @return whether the pointcut can apply on any method
  157. */
  158. public static boolean canApply(Pointcut pc, Class targetClass, Class[] proxyInterfaces) {
  159. if (!pc.getClassFilter().matches(targetClass)) {
  160. return false;
  161. }
  162. // It may apply to the class
  163. // Check whether it can apply on any method
  164. // Checks public methods, including inherited methods
  165. Method[] methods = targetClass.getMethods();
  166. for (int i = 0; i < methods.length; i++) {
  167. Method m = methods[i];
  168. // If we're looking only at interfaces and this method
  169. // isn't on any of them, skip it
  170. if (proxyInterfaces != null && !methodIsOnOneOfTheseInterfaces(m, proxyInterfaces)) {
  171. continue;
  172. }
  173. if (pc.getMethodMatcher().matches(m, targetClass))
  174. return true;
  175. }
  176. return false;
  177. }
  178. public static boolean canApply(Advisor advisor, Class targetClass, Class[] proxyInterfaces) {
  179. if (advisor instanceof IntroductionAdvisor) {
  180. return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
  181. }
  182. else if (advisor instanceof PointcutAdvisor) {
  183. PointcutAdvisor pca = (PointcutAdvisor) advisor;
  184. return canApply(pca.getPointcut(), targetClass, proxyInterfaces);
  185. }
  186. else {
  187. // It doesn't have a pointcut so we assume it applies
  188. return true;
  189. }
  190. }
  191. }