org.springframework.aop.support.AopUtils Maven / Gradle / Ivy
/*
* Copyright 2002-2007 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.Modifier;
import java.lang.reflect.Proxy;
import java.util.HashSet;
import java.util.Iterator;
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.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Utility methods for AOP support code.
* Mainly for internal use within Spring's AOP support.
*
* 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 && clazz.getName().indexOf(ClassUtils.CGLIB_CLASS_SEPARATOR) != -1);
}
/**
* Determine the target class of the given bean instance,
* which might be an AOP proxy.
*
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)
* @see org.springframework.aop.TargetClassAware#getTargetClass()
*/
public static Class getTargetClass(Object candidate) {
Assert.notNull(candidate, "Candidate object must not be null");
if (candidate instanceof TargetClassAware) {
return ((TargetClassAware) candidate).getTargetClass();
}
if (isCglibProxyClass(candidate.getClass())) {
return candidate.getClass().getSuperclass();
}
return candidate.getClass();
}
/**
* Determine whether the given method is an "equals" method.
* @see java.lang.Object#equals
*/
public static boolean isEqualsMethod(Method method) {
return (method != null && method.getName().equals("equals") &&
method.getParameterTypes().length == 1 && method.getParameterTypes()[0] == Object.class);
}
/**
* Determine whether the given method is a "hashCode" method.
* @see java.lang.Object#hashCode
*/
public static boolean isHashCodeMethod(Method method) {
return (method != null && method.getName().equals("hashCode") &&
method.getParameterTypes().length == 0);
}
/**
* Determine whether the given method is a "toString" method.
* @see java.lang.Object#toString()
*/
public static boolean isToStringMethod(Method method) {
return (method != null && method.getName().equals("toString") &&
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 IFoo.bar()
and the target class
* may be DefaultFoo
. In this case, the method may be
* DefaultFoo.bar()
. This enables attributes on that method to be found.
* @param method the method to be invoked, which may come from an interface
* @param targetClass the target class for the current invocation.
* May be null
or may not even implement the method.
* @return the specific target method, or the original method if the
* targetClass
doesn't implement it or is null
* @see org.springframework.util.ClassUtils#getMostSpecificMethod
*/
public static Method getMostSpecificMethod(Method method, Class targetClass) {
return ClassUtils.getMostSpecificMethod(method, targetClass);
}
/**
* Can the given pointcut apply at all on the given class?
*
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?
*
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 classes = new HashSet(ClassUtils.getAllInterfacesForClassAsSet(targetClass));
classes.add(targetClass);
for (Iterator it = classes.iterator(); it.hasNext();) {
Class clazz = (Class) it.next();
Method[] methods = clazz.getMethods();
for (int j = 0; j < methods.length; j++) {
if ((introductionAwareMethodMatcher != null &&
introductionAwareMethodMatcher.matches(methods[j], targetClass, hasIntroductions)) ||
methodMatcher.matches(methods[j], 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?
* 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 candidateAdvisors
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 findAdvisorsThatCanApply(List candidateAdvisors, Class clazz) {
if (candidateAdvisors.isEmpty()) {
return candidateAdvisors;
}
List eligibleAdvisors = new LinkedList();
for (Iterator it = candidateAdvisors.iterator(); it.hasNext();) {
Advisor candidate = (Advisor) it.next();
if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
eligibleAdvisors.add(candidate);
}
}
boolean hasIntroductions = !eligibleAdvisors.isEmpty();
for (Iterator it = candidateAdvisors.iterator(); it.hasNext();) {
Advisor candidate = (Advisor) it.next();
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 {
if (!Modifier.isPublic(method.getModifiers()) ||
!Modifier.isPublic(method.getDeclaringClass().getModifiers())) {
method.setAccessible(true);
}
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);
}
}
}