SpringBoot/Java中如何直接获取指定注解标注的非静态方法
如何直接获取被指定注解标注的非静态方法?
你希望不用遍历所有方法,直接获取某个类中被特定注解标注的非静态方法——比如在切面代码里,想要拿到InputRequest类中被@MyValidator标注的customMyValidator()方法,而不是手动遍历所有方法检查注解。这确实是个很实用的需求,咱们来看看怎么实现:
方案1:封装反射工具方法(最直接)
Java标准库确实没有内置getMethodAnnotatedWith()这种方法,但我们可以把遍历逻辑封装成工具方法,用起来就像“直接获取”一样。
先写一个通用的工具类:
import java.lang.reflect.Method; import java.util.Optional; public class AnnotationMethodUtils { // 获取类中第一个被指定注解标注的非静态方法 public static Optional<Method> getMethodAnnotatedWith(Class<?> clazz, Class<? extends java.lang.annotation.Annotation> annotationClass) { for (Method method : clazz.getDeclaredMethods()) { // 检查方法是否有目标注解,并且不是静态方法 if (method.isAnnotationPresent(annotationClass) && !java.lang.reflect.Modifier.isStatic(method.getModifiers())) { method.setAccessible(true); // 确保可以访问私有方法(如果你的注解方法是私有的话) return Optional.of(method); } } // 找不到匹配方法时返回空Optional,避免空指针 return Optional.empty(); } }
然后修改你的切面代码,替换掉那个虚拟方法:
@Aspect @Component @Slf4j public class AspectClass { @Before(" @annotation(com.example.ValidateAction)") public void validateAspect(JoinPoint joinPoint) throws Throwable { log.info(" MethodName : " + joinPoint.getSignature().getName()); Object[] args = joinPoint.getArgs(); log.info("args[0]==>"+args[0]+", args[1]==>"+args[1]); MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); Parameter[] parameters = method.getParameters(); // 使用工具类获取被@MyValidator标注的方法 Optional<Method> customMethodOpt = AnnotationMethodUtils.getMethodAnnotatedWith(parameters[0].getType(), MyValidator.class); if (customMethodOpt.isPresent()) { Method customMyValidator = customMethodOpt.get(); customMyValidator.invoke(args[0]); } else { // 处理找不到方法的情况,比如打日志或者抛出异常 log.warn("No method annotated with @MyValidator found in class: {}", parameters[0].getType().getName()); } } }
方案2:提前缓存方法(优化性能)
如果你的代码会频繁调用这个查找逻辑,每次遍历反射会有一定性能开销,那可以提前把每个类对应的注解方法缓存起来:
import java.lang.reflect.Method; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; public class AnnotationMethodCache { private static final Map<Class<?>, Map<Class<? extends java.lang.annotation.Annotation>, Method>> METHOD_CACHE = new ConcurrentHashMap<>(); public static Optional<Method> getCachedMethodAnnotatedWith(Class<?> clazz, Class<? extends java.lang.annotation.Annotation> annotationClass) { // 先从缓存取,避免重复遍历 Map<Class<? extends java.lang.annotation.Annotation>, Method> classCache = METHOD_CACHE.get(clazz); if (classCache != null && classCache.containsKey(annotationClass)) { return Optional.ofNullable(classCache.get(annotationClass)); } // 缓存没有的话,遍历查找并放入缓存 for (Method method : clazz.getDeclaredMethods()) { if (method.isAnnotationPresent(annotationClass) && !java.lang.reflect.Modifier.isStatic(method.getModifiers())) { method.setAccessible(true); METHOD_CACHE.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>()).put(annotationClass, method); return Optional.of(method); } } // 找不到的话,缓存空值避免重复查找 METHOD_CACHE.computeIfAbsent(clazz, k -> new ConcurrentHashMap<>()).put(annotationClass, null); return Optional.empty(); } }
之后在切面里调用这个缓存版的方法即可,第一次查找后,后续调用就直接从缓存拿取,不用再遍历类的方法了。
为什么Java没有直接提供这个方法?
其实是因为注解的使用场景太灵活:一个类可能有多个方法被同一个注解标注,注解也可能被继承(如果设置了@Inherited),遍历是最稳妥的方式——能让你自己决定如何处理多个匹配方法的情况(比如取第一个、收集所有,或者抛出异常)。把遍历逻辑封装成工具方法后,用起来就和你想象中的getMethodAnnotatedWith()一样方便啦!
内容的提问来源于stack exchange,提问作者Ashish Sharma
相关产品推荐
相关产品推荐

