リフレクションなどでできますか?
リフレクションなどでできますか?
回答:
私はしばらく探していましたが、さまざまなアプローチがあるようです。要約は次のとおりです。
依存関係を追加してもかまわない場合は、リフレクションライブラリが非常に人気があります。次のようになります。
Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
ServiceLoader(エリクソンの回答による)とそれは次のようになります:
ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
for (Pet implClass : loader) {
System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
}
これを機能させるにはPet
、ServiceProviderInterface(SPI)として定義し、その実装を宣言する必要があることに注意してください。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に基づく他のアプローチがあり、常にすでにロードされているクラスに制限されます。
エリクソンが言ったことですが、それでもやりたい場合は、Reflectionsを見てください。彼らのページから:
Reflectionsを使用すると、メタデータに次のクエリを実行できます。
- あるタイプのすべてのサブタイプを取得する
- いくつかの注釈で注釈を付けられたすべてのタイプを取得します
- 一致する注釈パラメータを含む、いくつかの注釈で注釈が付けられたすべてのタイプを取得します
- いくつかの注釈が付けられたすべてのメソッドを取得する
new Reflections("my.package").getSubTypesOf(MyInterface.class)
一般に、これを行うには費用がかかります。リフレクションを使用するには、クラスをロードする必要があります。クラスパスで使用可能なすべてのクラスをロードする場合は、時間とメモリが必要になるため、お勧めしません。
これを回避したい場合は、リフレクションではなく、より効率的に動作する独自のクラスファイルパーサーを実装する必要があります。バイトコードエンジニアリングライブラリは、このアプローチに役立つ場合があります。
サービスプロバイダのメカニズムは、プラグイン可能なサービスの実装を列挙するための従来の手段であり、Javaの9使用中のプロジェクトジグソーパズル(モジュール)の導入により、より確立されているServiceLoader
Javaで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
}
}
エリクソンが言ったことは最高です。これが関連する質問と回答のスレッドです-://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html
Apache BCELライブラリを使用すると、クラスをロードせずに読み取ることができます。検証手順をスキップできるはずなので、もっと速くなると思います。クラスローダーを使用してすべてのクラスをロードする際のもう1つの問題は、メモリに大きな影響を与えるだけでなく、おそらく実行したくない静的コードブロックを誤って実行することです。
ApacheBCELライブラリリンク-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 { ... }
Groovyで呼び出すか、Javaでtry-with-resourcesを使用する必要があります:github.com/classgraph/classgraph/wiki/…また、クラスの名前を取得するための正しいメソッドであるため、最後の部分はで.name
はなく.className
、である必要があります。オブジェクト.getName()
から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の1つのインスタンスを返すcreateXXX()メソッドではないメソッドを削除することでした。そのように検出された各クラスは、Class []配列に追加され、JAXBContextインスタンス化呼び出しに渡されます。これはうまく機能し、とにかく必要になろうとしていたObjectFactoryクラスをロードするだけで済みます。ObjectFactoryクラスを維持する必要があるのは、手動で実行されるタスク(私の場合、POJOから始めてschemagenを使用したため)、またはxjcで必要に応じて生成できるタスクだけです。いずれにせよ、それはパフォーマンスが高く、シンプルで、効果的です。