在 Spring Boot 应用程序中,如何使用返回类型注释作为过滤器来扫描方法返回类型和注释值?

发布于 2025-01-13 03:19:55 字数 1285 浏览 0 评论 0原文

尝试扫描使用自定义注释进行注释的返回类型对象,但 ClassPathScanningCandidateComponentProvider 找不到它们,即使包是正确的并且元数据显示使用它们注释的方法。

我已向现有的 ClassPathScanningCandidateComponentProvider 添加了一个过滤器,以包含使用自定义注释注释的方法的返回类型,即

@Procedure(name="PROC_MAPPING_NAME")
@CursorList(cursorType=CursorObject.class)CursorHolder<CursorObject> getCursor(@Param("col_1") @NonNull String param);

像这样添加过滤器

ClassPathScanningCandidateComponentProvider scanner =
   new ClassPathScanningCandidateComponentProvider(false);
   //existing filters
   ....
   //new filter
   scanner.addIncludeFilter(new AnnotationTypeFilter(CursorList.class));
   //scan packages
return Arrays.stream(packages)
  .map(scanner::findCandidateComponents)
  .flatMap(Collection::stream)
  .map(BeanDefinition::getBeanClassName)
  .map(name -> {
    try {
      return Class.forName(name);
    } 
    catch (ClassNotFoundException e) {
      //handle exception
    }
  }).collect(Collectors.toList());

适用于所有现有过滤器,并且使用新注释的包肯定包含在内,因为我'已经进入代码了。它只是不符合被视为匹配并返回的标准。

我还在类级别测试了这个注释,然后发现它没有问题。但是,它似乎不适用于属性级别或方法或参数级别。

给出类名如此愚蠢的问题。当注释包含在类级别时,ClassPathScanningCandidateComponentProvider 是否仅适合扫描注释使用情况?即只找到带注释的组件?

如果是这种情况,有没有人对查找注释以及它们注释的对象(其中对象是返回类型)有任何建议?或者这是不可能的?

谢谢

trying to scan for return type objects that are annoated with a custom annoation but the ClassPathScanningCandidateComponentProvider does not find them even though package is correct and metadata shows the methods annoated with them.

I've added a filter to my existing ClassPathScanningCandidateComponentProvider to include return types on methods annotated with a custom annoation, i.e.

@Procedure(name="PROC_MAPPING_NAME")
@CursorList(cursorType=CursorObject.class)CursorHolder<CursorObject> getCursor(@Param("col_1") @NonNull String param);

Filter is added like so

ClassPathScanningCandidateComponentProvider scanner =
   new ClassPathScanningCandidateComponentProvider(false);
   //existing filters
   ....
   //new filter
   scanner.addIncludeFilter(new AnnotationTypeFilter(CursorList.class));
   //scan packages
return Arrays.stream(packages)
  .map(scanner::findCandidateComponents)
  .flatMap(Collection::stream)
  .map(BeanDefinition::getBeanClassName)
  .map(name -> {
    try {
      return Class.forName(name);
    } 
    catch (ClassNotFoundException e) {
      //handle exception
    }
  }).collect(Collectors.toList());

Works fine for all the existing filters and the package where the new annotation is used is definitely included as I've stepped into the code. It just fails to meet the criteria required to be considered a match and returned.

I've also tested this annotation at class level and it is then found without issue. But, it does not seem to work at attribute level or method or param level.

So stupid question given the class name. Is the ClassPathScanningCandidateComponentProvider only suitable for scanning for annoation usage when the annotation is included at class level? i.e. only finds annoated components?

If that is the case has anyone any suggestions for finding annotations and the objects they annoate where the object is a return type? Or is this just not possible?

Thanks

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

甜嗑 2025-01-20 03:19:56

据我调查, AnnotationTypeFilter 似乎仅匹配类 javadoc实施

幸运的是,我们可以自定义AnnotationTypeFilter以使其匹配方法。

下面是一个例子。

public class CustomAnnotationTypeFilter extends AnnotationTypeFilter {

    public CustomAnnotationTypeFilter(
        Class<? extends Annotation> annotationType) {
        super(annotationType);
    }

    @Override
    protected boolean matchSelf(MetadataReader metadataReader) {
        AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
        return metadata.hasAnnotatedMethods(getAnnotationType().getName());
    }
}

....

ClassPathScanningCandidateComponentProvider scanner =
    new ClassPathScanningCandidateComponentProvider(false);

scanner.addIncludeFilter(new CustomAnnotationTypeFilter(MyAnnotationA.class));

....

我们的 CustomAnnotationTypeFilter 匹配这样的类:

public class Hoge {

    @MyAnnotationA String doSomething() {
        return "";
    }
}

NOTE

在前面声明带有注释的方法意味着用以下方式注释方法(而不是返回类型)注释。

@Procedure(name="PROC_MAPPING_NAME")
@CursorList(cursorType=CursorObject.class)CursorHolder<CursorObject> getCursor(...) {
 ....
};

上面的示例意味着您使用 ProcedureCursorList 注释 getCursor 方法。在这种情况下,您需要在创建自定义过滤器时检查方法是否具有注释。

EDIT_1 (2022/3/16)

我考虑到下面的评论添加了一些示例。以下 CustomAnnotationTypeFilter 匹配 BazFooFugaHoge

public interface SomeInterface {
    @MyAnnotationA
    void doSomething();
}

public class Baz {

    public SomeInterface doSomething() {
        return null;
    }
}

public class Foo implements SomeInterface {

    @Override
    public void doSomething() {
    }
}

public class Fuga implements IFuga {
    @Override
    public SomeInterface doSomething() {
        return null;
    }
}

public class Hoge {

    @MyAnnotationA String doSomething() {
        return "";
    }
}

public class CustomAnnotationTypeFilter extends AnnotationTypeFilter {

    public CustomAnnotationTypeFilter(
        Class<? extends Annotation> annotationType) {
        super(annotationType);
    }

    @Override
    protected boolean matchSelf(MetadataReader metadataReader) {
        if (super.matchSelf(metadataReader)) {
            return true;
        }
        String className = metadataReader.getClassMetadata().getClassName();
        try {
            Class<?> clazz = ClassUtils.forName(className, getClass().getClassLoader());
            if (annotationExistsOnAMethod(clazz)) {
                System.out.printf("1: matches %s\n", className);
                return true; // matches 'Hoge.java' and 'Foo.java'
            }

            for (Method m : clazz.getDeclaredMethods()) {
                if (annotationExistsOnAMethod(m.getReturnType())) {
                    System.out.printf("2: matches %s\n", className);
                    return true; // matches 'Baz.java' and 'Fuga.java'
                }
            }

        } catch (ClassNotFoundException e) {
            // ignore
        }
        return false;
    }

    private boolean annotationExistsOnAMethod(Class<?> clazz) {
        for (Method m : clazz.getDeclaredMethods()) {
            if (AnnotationUtils.findAnnotation(m, this.getAnnotationType()) != null) {
                return true;
            }
        }
        return false;
    }
}

扫描组件以查找注释是一个复杂的问题。它不等同于/类似于使用 grep 命令进行搜索。

您必须明确指定目标注释的放置位置。例如,如果方法可以使用注释进行注释,则必须显式检查方法,如 AnnotationUtils.findAnnotation(method, this.getAnnotationType()) (如上面的示例所示)。此外,在某些情况下,您可能必须检查返回类型,如上面的示例。

在搜索注释时,使用 org.springframework.core.annotation.AnnotationUtils 等实用程序类会很有帮助。因为此类实用程序的某些方法可以找到
注释,即使它们没有直接出现在给定目标本身上(另请参阅其 javadoc)。

It seems that AnnotationTypeFilter matches only classes as far as I investigated its javadoc and implementation.

Fortunately, we can customize AnnotationTypeFilter in order to make it match methods.

The following is an example.

public class CustomAnnotationTypeFilter extends AnnotationTypeFilter {

    public CustomAnnotationTypeFilter(
        Class<? extends Annotation> annotationType) {
        super(annotationType);
    }

    @Override
    protected boolean matchSelf(MetadataReader metadataReader) {
        AnnotationMetadata metadata = metadataReader.getAnnotationMetadata();
        return metadata.hasAnnotatedMethods(getAnnotationType().getName());
    }
}

....

ClassPathScanningCandidateComponentProvider scanner =
    new ClassPathScanningCandidateComponentProvider(false);

scanner.addIncludeFilter(new CustomAnnotationTypeFilter(MyAnnotationA.class));

....

Our CustomAnnotationTypeFilter matches a class like:

public class Hoge {

    @MyAnnotationA String doSomething() {
        return "";
    }
}

NOTE

Declaring a method with annotations in front of it means to annotate the method (not the return type) with the annotations.

@Procedure(name="PROC_MAPPING_NAME")
@CursorList(cursorType=CursorObject.class)CursorHolder<CursorObject> getCursor(...) {
 ....
};

The example above means that you annotate getCursor method with Procedure and CursorList. In such cases, you need to check whether methods have an annotation when you create a custom filter.

EDIT_1 (2022/3/16)

I added some examples considering the comments below. The following CustomAnnotationTypeFilter matches Baz, Foo, Fuga and Hoge.

public interface SomeInterface {
    @MyAnnotationA
    void doSomething();
}

public class Baz {

    public SomeInterface doSomething() {
        return null;
    }
}

public class Foo implements SomeInterface {

    @Override
    public void doSomething() {
    }
}

public class Fuga implements IFuga {
    @Override
    public SomeInterface doSomething() {
        return null;
    }
}

public class Hoge {

    @MyAnnotationA String doSomething() {
        return "";
    }
}

public class CustomAnnotationTypeFilter extends AnnotationTypeFilter {

    public CustomAnnotationTypeFilter(
        Class<? extends Annotation> annotationType) {
        super(annotationType);
    }

    @Override
    protected boolean matchSelf(MetadataReader metadataReader) {
        if (super.matchSelf(metadataReader)) {
            return true;
        }
        String className = metadataReader.getClassMetadata().getClassName();
        try {
            Class<?> clazz = ClassUtils.forName(className, getClass().getClassLoader());
            if (annotationExistsOnAMethod(clazz)) {
                System.out.printf("1: matches %s\n", className);
                return true; // matches 'Hoge.java' and 'Foo.java'
            }

            for (Method m : clazz.getDeclaredMethods()) {
                if (annotationExistsOnAMethod(m.getReturnType())) {
                    System.out.printf("2: matches %s\n", className);
                    return true; // matches 'Baz.java' and 'Fuga.java'
                }
            }

        } catch (ClassNotFoundException e) {
            // ignore
        }
        return false;
    }

    private boolean annotationExistsOnAMethod(Class<?> clazz) {
        for (Method m : clazz.getDeclaredMethods()) {
            if (AnnotationUtils.findAnnotation(m, this.getAnnotationType()) != null) {
                return true;
            }
        }
        return false;
    }
}

Scanning components to find annotations is a complex problem. It is not equivalent to / similar to searching with grep command.

You have to explicitly specify the location that the target annotation is placed on. For example, if methods can be annotated with the annotation, you have to check methods explicitly like AnnotationUtils.findAnnotation(method, this.getAnnotationType()) (as the example above shows). Also you may have to check the return types like the example above in a certain situation.

It is helpful to use the utility classes like org.springframework.core.annotation.AnnotationUtils when searching for annotations. Because some methods of such utilities can find
annotations even when they are not directly present on the given target itself(See also its javadoc).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文