diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspect.java b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspect.java new file mode 100644 index 00000000..7a10ab08 --- /dev/null +++ b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/AbstractSentinelAspect.java @@ -0,0 +1,203 @@ +/* + * Copyright 1999-2018 Alibaba Group Holding Ltd. + * + * 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 com.alibaba.csp.sentinel.annotation.aspectj; + +import com.alibaba.csp.sentinel.annotation.SentinelResource; +import com.alibaba.csp.sentinel.slots.block.BlockException; +import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; +import com.alibaba.csp.sentinel.util.MethodUtil; +import com.alibaba.csp.sentinel.util.StringUtil; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.reflect.MethodSignature; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.util.Arrays; + +/** + * @author chua + * @date 2018/10/31 + */ +public abstract class AbstractSentinelAspect { + + private final Logger logger = LoggerFactory.getLogger(AbstractSentinelAspect.class); + + protected String getResourceName(String resourceName, Method method) { + // If resource name is present in annotation, use this value. + if (StringUtil.isNotBlank(resourceName)) { + return resourceName; + } + // Parse name of target method. + return MethodUtil.resolveMethodName(method); + } + + protected Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex) + throws Exception { + // Execute fallback for degrading if configured. + Object[] originArgs = pjp.getArgs(); + if (isDegradeFailure(ex)) { + Method method = extractFallbackMethod(pjp, annotation.fallback()); + if (method != null) { + return method.invoke(pjp.getTarget(), originArgs); + } + } + // Execute block handler if configured. + Method blockHandler = extractBlockHandlerMethod(pjp, annotation.blockHandler(), annotation.blockHandlerClass()); + if (blockHandler != null) { + // Construct args. + Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1); + args[args.length - 1] = ex; + if (isStatic(blockHandler)) { + return blockHandler.invoke(null, args); + } + return blockHandler.invoke(pjp.getTarget(), args); + } + // If no block handler is present, then directly throw the exception. + throw ex; + } + + private boolean isDegradeFailure(/*@NonNull*/ BlockException ex) { + return ex instanceof DegradeException; + } + + private Method extractFallbackMethod(ProceedingJoinPoint pjp, String fallbackName) { + if (StringUtil.isBlank(fallbackName)) { + return null; + } + Class clazz = pjp.getTarget().getClass(); + MethodWrapper m = ResourceMetadataRegistry.lookupFallback(clazz, fallbackName); + if (m == null) { + // First time, resolve the fallback. + Method method = resolveFallbackInternal(pjp, fallbackName); + // Cache the method instance. + ResourceMetadataRegistry.updateFallbackFor(clazz, fallbackName, method); + return method; + } + if (!m.isPresent()) { + return null; + } + return m.getMethod(); + } + + private Method resolveFallbackInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name) { + Method originMethod = resolveMethod(pjp); + Class[] parameterTypes = originMethod.getParameterTypes(); + return findMethod(false, pjp.getTarget().getClass(), name, originMethod.getReturnType(), parameterTypes); + } + + private Method extractBlockHandlerMethod(ProceedingJoinPoint pjp, String name, Class[] locationClass) { + if (StringUtil.isBlank(name)) { + return null; + } + + boolean mustStatic = locationClass != null && locationClass.length >= 1; + Class clazz; + if (mustStatic) { + clazz = locationClass[0]; + } else { + // By default current class. + clazz = pjp.getTarget().getClass(); + } + MethodWrapper m = ResourceMetadataRegistry.lookupBlockHandler(clazz, name); + if (m == null) { + // First time, resolve the block handler. + Method method = resolveBlockHandlerInternal(pjp, name, clazz, mustStatic); + // Cache the method instance. + ResourceMetadataRegistry.updateBlockHandlerFor(clazz, name, method); + return method; + } + if (!m.isPresent()) { + return null; + } + return m.getMethod(); + } + + private Method resolveBlockHandlerInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name, Class clazz, + boolean mustStatic) { + Method originMethod = resolveMethod(pjp); + Class[] originList = originMethod.getParameterTypes(); + Class[] parameterTypes = Arrays.copyOf(originList, originList.length + 1); + parameterTypes[parameterTypes.length - 1] = BlockException.class; + return findMethod(mustStatic, clazz, name, originMethod.getReturnType(), parameterTypes); + } + + private boolean checkStatic(boolean mustStatic, Method method) { + return !mustStatic || isStatic(method); + } + + private Method findMethod(boolean mustStatic, Class clazz, String name, Class returnType, + Class... parameterTypes) { + Method[] methods = clazz.getDeclaredMethods(); + for (Method method : methods) { + if (name.equals(method.getName()) && checkStatic(mustStatic, method) + && returnType.isAssignableFrom(method.getReturnType()) + && Arrays.equals(parameterTypes, method.getParameterTypes())) { + + logger.info("Resolved method [{}] in class [{}]", name, clazz.getCanonicalName()); + return method; + } + } + // Current class not found, find in the super classes recursively. + Class superClass = clazz.getSuperclass(); + if (superClass != null && !Object.class.equals(superClass)) { + return findMethod(mustStatic, superClass, name, returnType, parameterTypes); + } else { + String methodType = mustStatic ? " static" : ""; + logger.error("Cannot find{} method [{}] in class [{}] with parameters {}", + methodType, name, clazz.getCanonicalName(), Arrays.toString(parameterTypes)); + return null; + } + } + + private boolean isStatic(Method method) { + return Modifier.isStatic(method.getModifiers()); + } + + protected Method resolveMethod(ProceedingJoinPoint joinPoint) { + MethodSignature signature = (MethodSignature)joinPoint.getSignature(); + Class targetClass = joinPoint.getTarget().getClass(); + + Method method = getDeclaredMethodFor(targetClass, signature.getName(), + signature.getMethod().getParameterTypes()); + if (method == null) { + throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName()); + } + return method; + } + + /** + * Get declared method with provided name and parameterTypes in given class and its super classes. + * All parameters should be valid. + * + * @param clazz class where the method is located + * @param name method name + * @param parameterTypes method parameter type list + * @return resolved method, null if not found + */ + private Method getDeclaredMethodFor(Class clazz, String name, Class... parameterTypes) { + try { + return clazz.getDeclaredMethod(name, parameterTypes); + } catch (NoSuchMethodException e) { + Class superClass = clazz.getSuperclass(); + if (superClass != null) { + return getDeclaredMethodFor(superClass, name, parameterTypes); + } + } + return null; + } +} diff --git a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java index 0c3ae621..c7cadf40 100644 --- a/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java +++ b/sentinel-extension/sentinel-annotation-aspectj/src/main/java/com/alibaba/csp/sentinel/annotation/aspectj/SentinelResourceAspect.java @@ -15,35 +15,28 @@ */ package com.alibaba.csp.sentinel.annotation.aspectj; -import java.lang.reflect.Method; -import java.lang.reflect.Modifier; -import java.util.Arrays; - import com.alibaba.csp.sentinel.Entry; import com.alibaba.csp.sentinel.EntryType; import com.alibaba.csp.sentinel.SphU; import com.alibaba.csp.sentinel.annotation.SentinelResource; import com.alibaba.csp.sentinel.context.ContextUtil; import com.alibaba.csp.sentinel.slots.block.BlockException; -import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException; -import com.alibaba.csp.sentinel.util.MethodUtil; -import com.alibaba.csp.sentinel.util.StringUtil; - import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; -import org.aspectj.lang.reflect.MethodSignature; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.lang.reflect.Method; + /** * Aspect for methods with {@link SentinelResource} annotation. * * @author Eric Zhao */ @Aspect -public class SentinelResourceAspect { +public class SentinelResourceAspect extends AbstractSentinelAspect { private final Logger logger = LoggerFactory.getLogger(SentinelResourceAspect.class); @@ -77,168 +70,4 @@ public class SentinelResourceAspect { ContextUtil.exit(); } } - - private String getResourceName(String resourceName, Method method) { - // If resource name is present in annotation, use this value. - if (StringUtil.isNotBlank(resourceName)) { - return resourceName; - } - // Parse name of target method. - return MethodUtil.resolveMethodName(method); - } - - private Object handleBlockException(ProceedingJoinPoint pjp, SentinelResource annotation, BlockException ex) - throws Exception { - // Execute fallback for degrading if configured. - Object[] originArgs = pjp.getArgs(); - if (isDegradeFailure(ex)) { - Method method = extractFallbackMethod(pjp, annotation.fallback()); - if (method != null) { - return method.invoke(pjp.getTarget(), originArgs); - } - } - // Execute block handler if configured. - Method blockHandler = extractBlockHandlerMethod(pjp, annotation.blockHandler(), annotation.blockHandlerClass()); - if (blockHandler != null) { - // Construct args. - Object[] args = Arrays.copyOf(originArgs, originArgs.length + 1); - args[args.length - 1] = ex; - if (isStatic(blockHandler)) { - return blockHandler.invoke(null, args); - } - return blockHandler.invoke(pjp.getTarget(), args); - } - // If no block handler is present, then directly throw the exception. - throw ex; - } - - private boolean isDegradeFailure(/*@NonNull*/ BlockException ex) { - return ex instanceof DegradeException; - } - - private Method extractFallbackMethod(ProceedingJoinPoint pjp, String fallbackName) { - if (StringUtil.isBlank(fallbackName)) { - return null; - } - Class clazz = pjp.getTarget().getClass(); - MethodWrapper m = ResourceMetadataRegistry.lookupFallback(clazz, fallbackName); - if (m == null) { - // First time, resolve the fallback. - Method method = resolveFallbackInternal(pjp, fallbackName); - // Cache the method instance. - ResourceMetadataRegistry.updateFallbackFor(clazz, fallbackName, method); - return method; - } - if (!m.isPresent()) { - return null; - } - return m.getMethod(); - } - - private Method resolveFallbackInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name) { - Method originMethod = resolveMethod(pjp); - Class[] parameterTypes = originMethod.getParameterTypes(); - return findMethod(false, pjp.getTarget().getClass(), name, originMethod.getReturnType(), parameterTypes); - } - - private Method extractBlockHandlerMethod(ProceedingJoinPoint pjp, String name, Class[] locationClass) { - if (StringUtil.isBlank(name)) { - return null; - } - - boolean mustStatic = locationClass != null && locationClass.length >= 1; - Class clazz; - if (mustStatic) { - clazz = locationClass[0]; - } else { - // By default current class. - clazz = pjp.getTarget().getClass(); - } - MethodWrapper m = ResourceMetadataRegistry.lookupBlockHandler(clazz, name); - if (m == null) { - // First time, resolve the block handler. - Method method = resolveBlockHandlerInternal(pjp, name, clazz, mustStatic); - // Cache the method instance. - ResourceMetadataRegistry.updateBlockHandlerFor(clazz, name, method); - return method; - } - if (!m.isPresent()) { - return null; - } - return m.getMethod(); - } - - private Method resolveBlockHandlerInternal(ProceedingJoinPoint pjp, /*@NonNull*/ String name, Class clazz, - boolean mustStatic) { - Method originMethod = resolveMethod(pjp); - Class[] originList = originMethod.getParameterTypes(); - Class[] parameterTypes = Arrays.copyOf(originList, originList.length + 1); - parameterTypes[parameterTypes.length - 1] = BlockException.class; - return findMethod(mustStatic, clazz, name, originMethod.getReturnType(), parameterTypes); - } - - private boolean checkStatic(boolean mustStatic, Method method) { - return !mustStatic || isStatic(method); - } - - private Method findMethod(boolean mustStatic, Class clazz, String name, Class returnType, - Class... parameterTypes) { - Method[] methods = clazz.getDeclaredMethods(); - for (Method method : methods) { - if (name.equals(method.getName()) && checkStatic(mustStatic, method) - && returnType.isAssignableFrom(method.getReturnType()) - && Arrays.equals(parameterTypes, method.getParameterTypes())) { - - logger.info("Resolved method [{}] in class [{}]", name, clazz.getCanonicalName()); - return method; - } - } - // Current class not found, find in the super classes recursively. - Class superClass = clazz.getSuperclass(); - if (superClass != null && !Object.class.equals(superClass)) { - return findMethod(mustStatic, superClass, name, returnType, parameterTypes); - } else { - String methodType = mustStatic ? " static" : ""; - logger.error("Cannot find{} method [{}] in class [{}] with parameters {}", - methodType, name, clazz.getCanonicalName(), Arrays.toString(parameterTypes)); - return null; - } - } - - private boolean isStatic(Method method) { - return Modifier.isStatic(method.getModifiers()); - } - - private Method resolveMethod(ProceedingJoinPoint joinPoint) { - MethodSignature signature = (MethodSignature)joinPoint.getSignature(); - Class targetClass = joinPoint.getTarget().getClass(); - - Method method = getDeclaredMethodFor(targetClass, signature.getName(), - signature.getMethod().getParameterTypes()); - if (method == null) { - throw new IllegalStateException("Cannot resolve target method: " + signature.getMethod().getName()); - } - return method; - } - - /** - * Get declared method with provided name and parameterTypes in given class and its super classes. - * All parameters should be valid. - * - * @param clazz class where the method is located - * @param name method name - * @param parameterTypes method parameter type list - * @return resolved method, null if not found - */ - private Method getDeclaredMethodFor(Class clazz, String name, Class... parameterTypes) { - try { - return clazz.getDeclaredMethod(name, parameterTypes); - } catch (NoSuchMethodException e) { - Class superClass = clazz.getSuperclass(); - if (superClass != null) { - return getDeclaredMethodFor(superClass, name, parameterTypes); - } - } - return null; - } }