- Enhance resolve name logic in MethodUtil - Add test case for MethodUtil Signed-off-by: Eric Zhao <sczyh16@gmail.com>master
@@ -32,7 +32,7 @@ public class MethodResourceWrapper extends ResourceWrapper { | |||||
public MethodResourceWrapper(Method method, EntryType type) { | public MethodResourceWrapper(Method method, EntryType type) { | ||||
this.method = method; | this.method = method; | ||||
this.name = MethodUtil.getMethodName(method); | |||||
this.name = MethodUtil.resolveMethodName(method); | |||||
this.type = type; | this.type = type; | ||||
} | } | ||||
@@ -16,9 +16,6 @@ | |||||
package com.alibaba.csp.sentinel.util; | package com.alibaba.csp.sentinel.util; | ||||
import java.lang.reflect.Method; | import java.lang.reflect.Method; | ||||
import java.util.ArrayList; | |||||
import java.util.HashMap; | |||||
import java.util.List; | |||||
import java.util.Map; | import java.util.Map; | ||||
import java.util.concurrent.ConcurrentHashMap; | import java.util.concurrent.ConcurrentHashMap; | ||||
@@ -29,14 +26,20 @@ import java.util.concurrent.ConcurrentHashMap; | |||||
*/ | */ | ||||
public final class MethodUtil { | public final class MethodUtil { | ||||
private static volatile Map<Method, String> methodNameMap = new HashMap<Method, String>(); | |||||
private static final Map<Method, String> methodNameMap = new ConcurrentHashMap<Method, String>(); | |||||
private static final Object LOCK = new Object(); | private static final Object LOCK = new Object(); | ||||
/** | /** | ||||
* Parse and get the method name. | |||||
* Parse and resolve the method name, then cache to the map. | |||||
* | |||||
* @param method method instance | |||||
* @return resolved method name | |||||
*/ | */ | ||||
public static String getMethodName(Method method) { | |||||
public static String resolveMethodName(Method method) { | |||||
if (method == null) { | |||||
throw new IllegalArgumentException("Null method"); | |||||
} | |||||
String methodName = methodNameMap.get(method); | String methodName = methodNameMap.get(method); | ||||
if (methodName == null) { | if (methodName == null) { | ||||
synchronized (LOCK) { | synchronized (LOCK) { | ||||
@@ -52,7 +55,7 @@ public final class MethodUtil { | |||||
int paramPos = 0; | int paramPos = 0; | ||||
for (Class<?> clazz : params) { | for (Class<?> clazz : params) { | ||||
sb.append(clazz.getName()); | |||||
sb.append(clazz.getCanonicalName()); | |||||
if (++paramPos < params.length) { | if (++paramPos < params.length) { | ||||
sb.append(","); | sb.append(","); | ||||
} | } | ||||
@@ -60,12 +63,17 @@ public final class MethodUtil { | |||||
sb.append(")"); | sb.append(")"); | ||||
methodName = sb.toString(); | methodName = sb.toString(); | ||||
HashMap<Method, String> newMap = new HashMap<Method, String>(methodNameMap); | |||||
newMap.put(method, methodName); | |||||
methodNameMap = newMap; | |||||
methodNameMap.put(method, methodName); | |||||
} | } | ||||
} | } | ||||
} | } | ||||
return methodName; | return methodName; | ||||
} | } | ||||
/** | |||||
* For test. | |||||
*/ | |||||
static void clearMethodMap() { | |||||
methodNameMap.clear(); | |||||
} | |||||
} | } |
@@ -0,0 +1,63 @@ | |||||
package com.alibaba.csp.sentinel.util; | |||||
import java.lang.reflect.Method; | |||||
import org.junit.After; | |||||
import org.junit.Before; | |||||
import org.junit.Test; | |||||
import static org.junit.Assert.*; | |||||
/** | |||||
* Test cases for {@link MethodUtil}. | |||||
* | |||||
* @author Eric Zhao | |||||
*/ | |||||
public class MethodUtilTest { | |||||
@Before | |||||
public void setUp() { | |||||
MethodUtil.clearMethodMap(); | |||||
} | |||||
@After | |||||
public void cleanUp() { | |||||
MethodUtil.clearMethodMap(); | |||||
} | |||||
@Test | |||||
public void testResolveMethodName() { | |||||
Method fooMethod = null; | |||||
for (Method m : GoodClass.class.getMethods()) { | |||||
if (m.getName().contains("foo")) { | |||||
fooMethod = m; | |||||
break; | |||||
} | |||||
} | |||||
assertNotNull(fooMethod); | |||||
assertEquals("com.alibaba.csp.sentinel.util.MethodUtilTest$GoodClass:foo(long[],java.lang.String,java.lang.Integer[])", | |||||
MethodUtil.resolveMethodName(fooMethod)); | |||||
Method bazMethod = null; | |||||
for (Method m : GoodClass.class.getMethods()) { | |||||
if (m.getName().contains("baz")) { | |||||
bazMethod = m; | |||||
break; | |||||
} | |||||
} | |||||
assertNotNull(bazMethod); | |||||
assertEquals("com.alibaba.csp.sentinel.util.MethodUtilTest$GoodClass:baz(double)", | |||||
MethodUtil.resolveMethodName(bazMethod)); | |||||
} | |||||
interface GoodClass { | |||||
void foo(long[] p1, String p2, Integer[] p3); | |||||
String baz(double a); | |||||
} | |||||
@Test(expected = IllegalArgumentException.class) | |||||
public void testResolveNullMethod() { | |||||
MethodUtil.resolveMethodName(null); | |||||
} | |||||
} |
@@ -26,6 +26,7 @@ import com.alibaba.csp.sentinel.annotation.SentinelResource; | |||||
import com.alibaba.csp.sentinel.context.ContextUtil; | import com.alibaba.csp.sentinel.context.ContextUtil; | ||||
import com.alibaba.csp.sentinel.slots.block.BlockException; | import com.alibaba.csp.sentinel.slots.block.BlockException; | ||||
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; | import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; | ||||
import com.alibaba.csp.sentinel.util.MethodUtil; | |||||
import com.alibaba.csp.sentinel.util.StringUtil; | import com.alibaba.csp.sentinel.util.StringUtil; | ||||
import org.aspectj.lang.ProceedingJoinPoint; | import org.aspectj.lang.ProceedingJoinPoint; | ||||
@@ -76,28 +77,16 @@ public class SentinelResourceAspect { | |||||
ContextUtil.exit(); | ContextUtil.exit(); | ||||
} | } | ||||
} | } | ||||
private String getResourceName(String resourceName, Method method) { | private String getResourceName(String resourceName, Method method) { | ||||
if(StringUtil.isNotBlank(resourceName)){ | |||||
// If resource name is present in annotation, use this value. | |||||
if (StringUtil.isNotBlank(resourceName)) { | |||||
return resourceName; | return resourceName; | ||||
} | } | ||||
StringBuilder buf = new StringBuilder(64); | |||||
buf.append(method.getDeclaringClass().getName()) | |||||
.append(":") | |||||
.append(method.getName()) | |||||
.append("("); | |||||
boolean isFirst = true; | |||||
for (Class<?> clazz : method.getParameterTypes()) { | |||||
if (!isFirst) { | |||||
buf.append(","); | |||||
} | |||||
buf.append(clazz.getName()); | |||||
isFirst = false; | |||||
} | |||||
buf.append(")"); | |||||
return buf.toString(); | |||||
// Parse name of target method. | |||||
return MethodUtil.resolveMethodName(method); | |||||
} | } | ||||
private Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex) | private Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex) | ||||
throws Exception { | throws Exception { | ||||
// Execute fallback for degrading if configured. | // Execute fallback for degrading if configured. | ||||
@@ -224,7 +213,8 @@ public class SentinelResourceAspect { | |||||
MethodSignature signature = (MethodSignature)joinPoint.getSignature(); | MethodSignature signature = (MethodSignature)joinPoint.getSignature(); | ||||
Class<?> targetClass = joinPoint.getTarget().getClass(); | Class<?> targetClass = joinPoint.getTarget().getClass(); | ||||
Method method = getDeclaredMethodFor(targetClass, signature.getName(), signature.getMethod().getParameterTypes()); | |||||
Method method = getDeclaredMethodFor(targetClass, signature.getName(), | |||||
signature.getMethod().getParameterTypes()); | |||||
if (method == null) { | if (method == null) { | ||||
throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName()); | throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName()); | ||||
} | } | ||||
@@ -235,8 +225,8 @@ public class SentinelResourceAspect { | |||||
* Get declared method with provided name and parameterTypes in given class and its super classes. | * Get declared method with provided name and parameterTypes in given class and its super classes. | ||||
* All parameters should be valid. | * All parameters should be valid. | ||||
* | * | ||||
* @param clazz class where the method is located | |||||
* @param name method name | |||||
* @param clazz class where the method is located | |||||
* @param name method name | |||||
* @param parameterTypes method parameter type list | * @param parameterTypes method parameter type list | ||||
* @return resolved method, null if not found | * @return resolved method, null if not found | ||||
*/ | */ | ||||