[java] Java에서 주어진 클래스의 모든 서브 클래스를 어떻게 찾습니까?

Java에서 주어진 클래스의 모든 서브 클래스 (또는 주어진 인터페이스의 모든 구현 자)를 찾으려고 어떻게합니까? 현재로서는이 작업을 수행 할 수있는 방법이 있지만 매우 비효율적입니다 (최소하게 말하면). 방법은 다음과 같습니다

  1. 클래스 경로에 존재하는 모든 클래스 이름 목록을 가져옵니다.
  2. 각 클래스를로드하고 테스트하여 해당 클래스 또는 인터페이스의 서브 클래스 또는 구현 자인지 확인하십시오.

이클립스에는 Type Hierarchy라는 멋진 기능이 있는데이를 효율적으로 보여줍니다. 프로그래밍 방식으로 어떻게 진행합니까?



답변

설명 한 것 이외의 다른 방법은 없습니다. 생각해보십시오-클래스 패스에서 각 클래스를 스캔하지 않고 클래스가 어떤 클래스를 클래스 X로 확장하는지 알 수 있습니까?

Eclipse는 “유형 계층에 표시”단추를 누른 시점에 이미 모든 유형 데이터가로드되어 있기 때문에 “효율적인”시간으로 보이는 수퍼 클래스와 서브 클래스에 대해서만 알려줄 수 있습니다. 지속적으로 클래스를 컴파일하고 클래스 패스의 모든 것에 대해 알고 있습니다.)


답변

순수 Java를 사용하면 클래스 스캔이 쉽지 않습니다.

스프링 프레임 워크는 필요한 것을 수행 할 수있는 ClassPathScanningCandidateComponentProvider 클래스를 제공 합니다. 다음 예제는 org.example.package 패키지에서 MyClass의 모든 서브 클래스를 찾습니다.

ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false);
provider.addIncludeFilter(new AssignableTypeFilter(MyClass.class));

// scan in org.example.package
Set<BeanDefinition> components = provider.findCandidateComponents("org/example/package");
for (BeanDefinition component : components)
{
    Class cls = Class.forName(component.getBeanClassName());
    // use class cls found
}

이 방법은 후보를 찾기 위해 바이트 코드 분석기를 사용하는 추가 이점이 있습니다. 즉 , 스캔하는 모든 클래스를로드 하지는 않습니다 .


답변

내장 Java Reflections API 만 사용하는 것은 불가능합니다.

이 정보에 액세스 할 수 있도록 클래스 경로의 필요한 스캔 및 인덱싱을 수행하는 프로젝트가 있습니다 …

반사

Scannotations의 정신에 따른 Java 런타임 메타 데이터 분석

Reflections는 클래스 경로를 스캔하고 메타 데이터를 색인화하며 런타임시이를 쿼리하고 프로젝트 내의 많은 모듈에 대한 정보를 저장하고 수집 할 수 있습니다.

Reflections를 사용하여 메타 데이터를 쿼리하여 다음을 수행 할 수 있습니다.

  • 특정 유형의 모든 하위 유형을 가져옵니다
  • 주석으로 주석이 달린 모든 유형을 얻습니다.
  • 주석 매개 변수 일치를 포함하여 주석으로 주석이 달린 모든 유형을 가져옵니다.
  • 모든 메소드에 주석이 달린

(면책 조항 : 나는 그것을 사용하지 않았지만 프로젝트 설명은 귀하의 요구에 정확히 맞는 것 같습니다.)


답변

클래스에 대해 생성 된 Javadoc 에는 알려진 서브 클래스 (및 인터페이스, 알려진 구현 클래스)의 목록이 포함 된다는 것을 잊지 마십시오 .


답변

ClassGraph 사용해보십시오 . (면책 조항, 저자입니다). ClassGraph는 주어진 클래스의 서브 클래스, 런타임 또는 빌드 타임에 대한 스캔을 지원합니다. ClassGraph는 메모리, 클래스 경로의 모든 클래스 또는 허용 된 패키지의 클래스에 대한 전체 클래스 그래프 (모든 클래스, 주석, 메소드, 메소드 매개 변수 및 필드)의 추상 표현을 빌드 할 수 있지만 해당 클래스 그래프를 조회 할 수 있습니다. 당신이 원합니다. ClassGraph는 다른 스캐너보다 더 많은 클래스 경로 지정 메커니즘과 클래스 로더를 지원 하며 새로운 JPMS 모듈 시스템과 완벽하게 작동하므로 ClassGraph를 기반으로 코드를 작성하면 코드를 최대한 이식 할 수 있습니다. 여기 API를 참조하십시오.


답변

나는 몇 년 전에 이것을했습니다. 이를 수행하는 가장 신뢰할 수있는 방법 (예 : 공식 Java API 및 외부 종속성 없음)은 사용자 정의 doclet을 작성하여 런타임시 읽을 수있는 목록을 작성하는 것입니다.

다음과 같이 명령 행에서 실행할 수 있습니다.

javadoc -d build -doclet com.example.ObjectListDoclet -sourcepath java/src -subpackages com.example

또는 다음과 같이 개미에서 실행하십시오.

<javadoc sourcepath="${src}" packagenames="*" >
  <doclet name="com.example.ObjectListDoclet" path="${build}"/>
</javadoc>

기본 코드는 다음과 같습니다.

public final class ObjectListDoclet {
    public static final String TOP_CLASS_NAME =  "com.example.MyClass";

    /** Doclet entry point. */
    public static boolean start(RootDoc root) throws Exception {
        try {
            ClassDoc topClassDoc = root.classNamed(TOP_CLASS_NAME);
            for (ClassDoc classDoc : root.classes()) {
                if (classDoc.subclassOf(topClassDoc)) {
                    System.out.println(classDoc);
                }
            }
            return true;
        }
        catch (Exception ex) {
            ex.printStackTrace();
            return false;
        }
    }
}

간단히하기 위해 명령 줄 인수 구문 분석을 제거하고 파일이 아닌 System.out에 작성하고 있습니다.


답변

나는이 파티에 몇 년 늦었다는 것을 알고 있지만, 같은 문제를 해결하려고이 질문을 겪었습니다. Eclipse 플러그인을 작성하고 (따라서 캐싱 등을 활용하는 경우) 프로그래밍 방식으로 Eclipse의 내부 검색을 사용하여 인터페이스를 구현하는 클래스를 찾을 수 있습니다. 내 (매우 거친) 첫 컷은 다음과 같습니다.

  protected void listImplementingClasses( String iface ) throws CoreException
  {
    final IJavaProject project = <get your project here>;
    try
    {
      final IType ifaceType = project.findType( iface );
      final SearchPattern ifacePattern = SearchPattern.createPattern( ifaceType, IJavaSearchConstants.IMPLEMENTORS );
      final IJavaSearchScope scope = SearchEngine.createWorkspaceScope();
      final SearchEngine searchEngine = new SearchEngine();
      final LinkedList<SearchMatch> results = new LinkedList<SearchMatch>();
      searchEngine.search( ifacePattern,
      new SearchParticipant[]{ SearchEngine.getDefaultSearchParticipant() }, scope, new SearchRequestor() {

        @Override
        public void acceptSearchMatch( SearchMatch match ) throws CoreException
        {
          results.add( match );
        }

      }, new IProgressMonitor() {

        @Override
        public void beginTask( String name, int totalWork )
        {
        }

        @Override
        public void done()
        {
          System.out.println( results );
        }

        @Override
        public void internalWorked( double work )
        {
        }

        @Override
        public boolean isCanceled()
        {
          return false;
        }

        @Override
        public void setCanceled( boolean value )
        {
        }

        @Override
        public void setTaskName( String name )
        {
        }

        @Override
        public void subTask( String name )
        {
        }

        @Override
        public void worked( int work )
        {
        }

      });

    } catch( JavaModelException e )
    {
      e.printStackTrace();
    }
  }

지금까지 본 첫 번째 문제는 모든 하위 클래스가 아닌 인터페이스를 직접 구현하는 클래스 만 잡는다는 것입니다.