/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.aop.support;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import org.springframework.aop.Advisor;
import org.springframework.aop.AopInvocationException;
import org.springframework.aop.IntroductionAdvisor;
import org.springframework.aop.IntroductionAwareMethodMatcher;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;
import org.springframework.aop.PointcutAdvisor;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.TargetClassAware;
import org.springframework.core.BridgeMethodResolver;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* Utility methods for AOP support code.
* Mainly for internal use within Spring's AOP support.
*
* <p>See {@link org.springframework.aop.framework.AopProxyUtils} for a
* collection of framework-specific AOP utility methods which depend
* on internals of Spring's AOP framework implementation.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Rob Harrop
* @see org.springframework.aop.framework.AopProxyUtils
*/
public abstract class AopUtils {
/**
* Check whether the given object is a JDK dynamic proxy or a CGLIB proxy.
* @param object the object to check
* @see #isJdkDynamicProxy
* @see #isCglibProxy
*/
public static boolean isAopProxy(Object object) {
return (object instanceof SpringProxy &&
(Proxy.isProxyClass(object.getClass()) || isCglibProxyClass(object.getClass())));
}
/**
* Check whether the given object is a JDK dynamic proxy.
* @param object the object to check
* @see java.lang.reflect.Proxy#isProxyClass
*/
public static boolean isJdkDynamicProxy(Object object) {
return (object instanceof SpringProxy && Proxy.isProxyClass(object.getClass()));
}
/**
* Check whether the given object is a CGLIB proxy.
* @param object the object to check
*/
public static boolean isCglibProxy(Object object) {
return (object instanceof SpringProxy && isCglibProxyClass(object.getClass()));
}
/**
* Check whether the specified class is a CGLIB-generated class.
* @param clazz the class to check
*/
public static boolean isCglibProxyClass(Class<?> clazz) {
return (clazz != null && isCglibProxyClassName(clazz.getName()));
}
/**
* Check whether the specified class name is a CGLIB-generated class.
* @param className the class name to check
*/
public static boolean isCglibProxyClassName(String className) {
return (className != null && className.contains(ClassUtils.CGLIB_CLASS_SEPARATOR));
}
/**
* Determine the target class of the given bean instance which might be an AOP proxy.
* <p>Returns the target class for an AOP proxy and the plain class else.
* @param candidate the instance to check (might be an AOP proxy)
* @return the target class (or the plain class of the given object as fallback;
* never <code>null</code>)
* @see org.springframework.aop.TargetClassAware#getTargetClass()
* @see org.springframework.aop.framework.AopProxyUtils#ultimateTargetClass(Object)
*/
public static Class<?> getTargetClass(Object candidate) {
Assert.notNull(candidate, "Candidate object must not be null");
Class<?> result = null;
if (candidate instanceof TargetClassAware) {
result = ((TargetClassAware) candidate).getTargetClass();
}
if (result == null) {
result = (isCglibProxy(candidate) ? candidate.getClass().getSuperclass() : candidate.getClass());
}
return result;
}
/**
* Determine whether the given method is an "equals" method.
* @see java.lang.Object#equals
*/
public static boolean isEqualsMethod(Method method) {
return ReflectionUtils.isEqualsMethod(method);
}
/**
* Determine whether the given method is a "hashCode" method.
* @see java.lang.Object#hashCode
*/
public static boolean isHashCodeMethod(Method method) {
return ReflectionUtils.isHashCodeMethod(method);
}
/**
* Determine whether the given method is a "toString" method.
* @see java.lang.Object#toString()
*/
public static boolean isToStringMethod(Method method) {
return ReflectionUtils.isToStringMethod(method);
}
/**
* Determine whether the given method is a "finalize" method.
* @see java.lang.Object#finalize()
*/
public static boolean isFinalizeMethod(Method method) {
return (method != null && method.getName().equals("finalize") &&
method.getParameterTypes().length == 0);
}
/**
* Given a method, which may come from an interface, and a target class used
* in the current AOP invocation, find the corresponding target method if there
* is one. E.g. the method may be <code>IFoo.bar()</code> and the target class
* may be <code>DefaultFoo</code>. In this case, the method may be
* <code>DefaultFoo.bar()</code>. This enables attributes on that method to be found.
* <p><b>NOTE:</b> In contrast to {@link org.springframework.util.ClassUtils#getMostSpecificMethod},
* this method resolves Java 5 bridge methods in order to retrieve attributes
* from the <i>original</i> method definition.
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation.
* May be <code>null</code> or may not even implement the method.
* @return the specific target method, or the original method if the
* <code>targetClass</code> doesn't implement it or is <code>null</code>
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
*/
public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
Method resolvedMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
// If we are dealing with method with generic parameters, find the original method.
return BridgeMethodResolver.findBridgedMethod(resolvedMethod);
}
/**
* Can the given pointcut apply at all on the given class?
* <p>This is an important test as it can be used to optimize
* out a pointcut for a class.
* @param pc the static or dynamic pointcut to check
* @param targetClass the class to test
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Pointcut pc, Class<?> targetClass) {
return canApply(pc, targetClass, false);
}
/**
* Can the given pointcut apply at all on the given class?
* <p>This is an important test as it can be used to optimize
* out a pointcut for a class.
* @param pc the static or dynamic pointcut to check
* @param targetClass the class to test
* @param hasIntroductions whether or not the advisor chain
* for this bean includes any introductions
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
if (!pc.getClassFilter().matches(targetClass)) {
return false;
}
MethodMatcher methodMatcher = pc.getMethodMatcher();
IntroductionAwareMethodMatcher introductionAwareMethodMatcher = null;
if (methodMatcher instanceof IntroductionAwareMethodMatcher) {
introductionAwareMethodMatcher = (IntroductionAwareMethodMatcher) methodMatcher;
}
Set<Class> classes = new HashSet<Class>(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Class<?> clazz : classes) {
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions)) ||
methodMatcher.matches(method, targetClass)) {
return true;
}
}
}
return false;
}
/**
* Can the given advisor apply at all on the given class?
* This is an important test as it can be used to optimize
* out a advisor for a class.
* @param advisor the advisor to check
* @param targetClass class we're testing
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Advisor advisor, Class<?> targetClass) {
return canApply(advisor, targetClass, false);
}
/**
* Can the given advisor apply at all on the given class?
* <p>This is an important test as it can be used to optimize out a advisor for a class.
* This version also takes into account introductions (for IntroductionAwareMethodMatchers).
* @param advisor the advisor to check
* @param targetClass class we're testing
* @param hasIntroductions whether or not the advisor chain for this bean includes
* any introductions
* @return whether the pointcut can apply on any method
*/
public static boolean canApply(Advisor advisor, Class<?> targetClass, boolean hasIntroductions) {
if (advisor instanceof IntroductionAdvisor) {
return ((IntroductionAdvisor) advisor).getClassFilter().matches(targetClass);
}
else if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pca = (PointcutAdvisor) advisor;
return canApply(pca.getPointcut(), targetClass, hasIntroductions);
}
else {
// It doesn't have a pointcut so we assume it applies.
return true;
}
}
/**
* Determine the sublist of the <code>candidateAdvisors</code> list
* that is applicable to the given class.
* @param candidateAdvisors the Advisors to evaluate
* @param clazz the target class
* @return sublist of Advisors that can apply to an object of the given class
* (may be the incoming List as-is)
*/
public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List<Advisor> eligibleAdvisors = new LinkedList<Advisor>();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Advisor candidate : candidateAdvisors) {
if (candidate instanceof IntroductionAdvisor) {
// already processed
continue;
}
if (canApply(candidate, clazz, hasIntroductions)) {
eligibleAdvisors.add(candidate);
}
}
return eligibleAdvisors;
}
/**
* Invoke the given target via reflection, as part of an AOP method invocation.
* @param target the target object
* @param method the method to invoke
* @param args the arguments for the method
* @return the invocation result, if any
* @throws Throwable if thrown by the target method
* @throws org.springframework.aop.AopInvocationException in case of a reflection error
*/
public static Object invokeJoinpointUsingReflection(Object target, Method method, Object[] args)
throws Throwable {
// Use reflection to invoke the method.
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
}
catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
}
catch (IllegalArgumentException ex) {
throw new AopInvocationException("AOP configuration seems to be invalid: tried calling method [" +
method + "] on target [" + target + "]", ex);
}
catch (IllegalAccessException ex) {
throw new AopInvocationException("Could not access method [" + method + "]", ex);
}
}
}
|