반사 나 그런 식으로 할 수 있습니까?
반사 나 그런 식으로 할 수 있습니까?
답변:
나는 잠시 동안 찾고 있었고 다른 접근 방식이있는 것 같습니다. 여기에 요약이 있습니다.
리플렉션 라이브러리는 종속성을 추가해도 괜찮다면 꽤 인기가 있습니다. 다음과 같이 표시됩니다.
Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
ServiceLoader (erickson 답변에 따라) 그리고 다음과 같습니다.
ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
for (Pet implClass : loader) {
System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
}
이 작업을 수행하려면 Pet
SPI (ServiceProviderInterface) 로 정의 하고 해당 구현을 선언해야합니다. resources/META-INF/services
이름 으로 파일을 만들고 그 안에 examples.reflections.Pet
모든 구현을 선언 Pet
하면됩니다.
examples.reflections.Dog
examples.reflections.Cat
패키지 수준 주석 . 다음은 예입니다.
Package[] packages = Package.getPackages();
for (Package p : packages) {
MyPackageAnnotation annotation = p.getAnnotation(MyPackageAnnotation.class);
if (annotation != null) {
Class<?>[] implementations = annotation.implementationsOfPet();
for (Class<?> impl : implementations) {
System.out.println(impl.getSimpleName());
}
}
}
및 주석 정의 :
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PACKAGE)
public @interface MyPackageAnnotation {
Class<?>[] implementationsOfPet() default {};
}
패키지 package-info.java
내부의 이름이 지정된 파일에서 패키지 수준 주석을 선언해야합니다 . 다음은 샘플 내용입니다.
@MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
package examples.reflections;
그 당시 ClassLoader에 알려진 패키지 만를 호출하여로드됩니다 Package.getPackages()
.
또한 디렉토리 기반 검색을 수행하지 않는 한 항상 이미로드 된 클래스로 제한되는 URLClassLoader 기반의 다른 접근 방식이 있습니다.
erickson이 말한 것, 그러나 여전히하고 싶다면 Reflections를 살펴보십시오 . 페이지에서 :
Reflections를 사용하여 메타 데이터를 쿼리 할 수 있습니다.
- 특정 유형의 모든 하위 유형 가져 오기
- 일부 주석으로 주석이 달린 모든 유형 가져 오기
- 주석 매개 변수 일치를 포함하여 일부 주석으로 주석이 달린 모든 유형을 가져옵니다.
- 모든 메소드에 일부 주석을 달다
new Reflections("my.package").getSubTypesOf(MyInterface.class)
일반적으로 이렇게하는 데 비용이 많이 듭니다. 리플렉션을 사용하려면 클래스를로드해야합니다. 클래스 경로에서 사용 가능한 모든 클래스를로드하려면 시간과 메모리가 필요하므로 권장되지 않습니다.
이것을 피하려면 리플렉션 대신 더 효율적으로 작동하는 자체 클래스 파일 파서를 구현해야합니다. 바이트 코드 엔지니어링 라이브러리가이 접근 방식에 도움이 될 수 있습니다.
서비스 제공자 메커니즘은 플러그 서비스의 구현을 열거하는 기존의 방법이며, 자바 9. 프로젝트 직소 (모듈)의 도입으로 더 설립되었다 ServiceLoader
자바 6 또는 이전 버전에서 자신을 구현합니다. 다른 답변에 예 를 제공 했습니다 .
META-INF/services/java.sql.Driver
Spring에는 이것을 달성하는 매우 간단한 방법이 있습니다.
public interface ITask {
void doStuff();
}
@Component
public class MyTask implements ITask {
public void doStuff(){}
}
그런 다음 유형 목록을 자동으로 연결할 수 ITask
있으며 Spring은 모든 구현으로이를 채울 것입니다.
@Service
public class TaskService {
@Autowired
private List<ITask> tasks;
}
주어진 인터페이스를 구현하는 모든 클래스를 나열하는 가장 강력한 메커니즘은 현재 ClassGraph 입니다. 새로운 JPMS 모듈 시스템을 포함하여 가능한 가장 광범위한 클래스 경로 사양 메커니즘을 처리하기 때문 입니다. (저는 저자입니다.)
try (ScanResult scanResult = new ClassGraph().whitelistPackages("x.y.z")
.enableClassInfo().scan()) {
for (ClassInfo ci : scanResult.getClassesImplementing("x.y.z.SomeInterface")) {
foundImplementingClass(ci); // Do something with the ClassInfo object
}
}
에릭슨이 말한 것이 가장 좋습니다. 다음은 관련 질문 및 답변 스레드입니다-http: //www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html
Apache BCEL 라이브러리를 사용하면 클래스를로드하지 않고도 읽을 수 있습니다. 확인 단계를 건너 뛸 수 있기 때문에 더 빨라질 것이라고 생각합니다. 클래스 로더를 사용하여 모든 클래스를로드 할 때의 또 다른 문제는 막대한 메모리 영향을받을뿐만 아니라 원하지 않는 정적 코드 블록을 실수로 실행한다는 것입니다.
아파치 BCEL 라이브러리 링크 - http://jakarta.apache.org/bcel/
ClassGraph를 사용하면 매우 간단합니다.
구현을 찾는 Groovy 코드 my.package.MyInterface
:
@Grab('io.github.classgraph:classgraph:4.6.18')
import io.github.classgraph.*
new ClassGraph().enableClassInfo().scan().withCloseable { scanResult ->
scanResult.getClassesImplementing('my.package.MyInterface').findAll{!it.abstract}*.name
}
scan().withCloseable { ... }
: 그루비에서, 또는 사용 시도 -과 - 자원 자바 github.com/classgraph/classgraph/wiki/...는 또한, 마지막 부분은해야 .name
하지 .className
때문에, .getName()
클래스의 이름을 얻을 수있는 올바른 방법이다 A로부터 ClassInfo
객체입니다.
예, 첫 번째 단계는 관심이있는 "모든"클래스를 식별하는 것입니다. 이 정보가 이미있는 경우 각 정보를 열거하고 instanceof를 사용하여 관계를 확인할 수 있습니다. 관련 기사 : https://web.archive.org/web/20100226233915/www.javaworld.com/javaworld/javatips/jw-javatip113.html
@ kaybee99의 답변의 새 버전이지만 이제 사용자가 묻는 내용을 반환합니다.
Spring에는 이것을 달성하는 매우 간단한 방법이 있습니다.
public interface ITask {
void doStuff();
default ITask getImplementation() {
return this;
}
}
@Component
public class MyTask implements ITask {
public void doStuff(){}
}
그런 다음 유형 목록을 자동으로 연결할 수 ITask
있으며 Spring은 모든 구현으로이를 채울 것입니다.
@Service
public class TaskService {
@Autowired(required = false)
private List<ITask> tasks;
if ( tasks != null)
for (ITask<?> taskImpl: tasks) {
taskImpl.doStuff();
}
}
나는 같은 문제에 부딪쳤다. 내 솔루션은 리플렉션을 사용하여 ObjectFactory 클래스의 모든 메서드를 검사하여 바인딩 된 POJO 중 하나의 인스턴스를 반환하는 createXXX () 메서드가 아닌 메서드를 제거하는 것입니다. 이렇게 발견 된 각 클래스는 Class [] 배열에 추가되고 JAXBContext 인스턴스화 호출에 전달됩니다. 어쨌든 필요했던 ObjectFactory 클래스를로드하기 만하면됩니다. 저는 ObjectFactory 클래스 만 유지하면됩니다. 작업은 수작업으로 수행하거나 (제 경우에는 POJO로 시작하고 schemagen을 사용했기 때문에) xjc에서 필요에 따라 생성 할 수 있습니다. 어느 쪽이든 성능이 뛰어나고 간단하며 효과적입니다.