Чи можу я це зробити з відображенням чи щось подібне?
Чи можу я це зробити з відображенням чи щось подібне?
Відповіді:
Я шукав якийсь час, і, схоже, існують різні підходи, ось короткий виклад:
бібліотека роздумів досить популярна, якщо ви не проти додати залежність. Це виглядало б так:
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
як 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 ви можете запитати свої метадані щодо:
- отримати всі підтипи певного типу
- отримати всі типи анотованих з деякими анотаціями
- отримати всі типи анотованих з деякими анотаціями, включаючи відповідність параметрів анотації
- отримати всі методи, анотовані деякими
new Reflections("my.package").getSubTypesOf(MyInterface.class)
Загалом, робити це дорого. Щоб використовувати рефлексію, клас потрібно завантажити. Якщо ви хочете завантажити кожен клас, доступний на шляху до класу, це займе час і пам’ять, і не рекомендується.
Якщо ви хочете цього уникнути, вам доведеться застосувати власний синтаксичний аналізатор класу, який працював ефективніше, замість відображення. Бібліотека інженерного проектування байт може допомогти з цим підходом.
Механізм постачальника послуг є звичайним засобом для перерахування реалізацій підключається служби і став більш усталеним завдяки введенню Jigsaw проекту (модулів) в Java 9. Використовуйте ServiceLoader
в Java 6 або впроваджуйте власні в попередніх версіях. Я подав приклад в іншій відповіді.
META-INF/services/java.sql.Driver
Весна має досить простий спосіб досягти цього:
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 дозволяє читати класи, не завантажуючи їх. Я вірю, що це буде швидше, тому що ви зможете пропустити крок перевірки. Інша проблема завантаження всіх класів за допомогою завантажувача класів полягає в тому, що ви зазнаєте величезного впливу на пам’ять, а також ненавмисно запустите будь-які статичні блоки коду, чого ви, мабуть, не хочете робити.
Посилання на бібліотеку Apache 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 { ... }
в Groovy або скористатися try-with-resources на Java: 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, але тепер повертається те, що запитує користувач: реалізації ...
Весна має досить простий спосіб досягти цього:
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, усуваючи ті, які не були методами createXXX (), що повертають екземпляр одного з моїх пов'язаних POJO. Кожен виявлений таким чином клас додається до масиву Class [], який потім передається виклику екземпляру JAXBContext. Це працює добре, потрібно лише завантажити клас ObjectFactory, який і так мав знадобитися. Мені потрібно лише підтримувати клас ObjectFactory, завдання, яке виконується вручну (у моєму випадку, тому що я починав з POJO і використовував схему), або може бути згенеровано за потреби xjc. У будь-якому випадку, це ефективно, просто та ефективно.