Czy mogę to zrobić z refleksją lub czymś w tym rodzaju?
Czy mogę to zrobić z refleksją lub czymś w tym rodzaju?
Odpowiedzi:
Szukałem od jakiegoś czasu i wydaje się, że są różne podejścia, oto podsumowanie:
Biblioteka odbić jest dość popularna, jeśli nie masz nic przeciwko dodaniu zależności. Wyglądałoby to tak:
Reflections reflections = new Reflections("firstdeveloper.examples.reflections");
Set<Class<? extends Pet>> classes = reflections.getSubTypesOf(Pet.class);
ServiceLoader (zgodnie z odpowiedzią Ericksona) i wyglądałoby to tak:
ServiceLoader<Pet> loader = ServiceLoader.load(Pet.class);
for (Pet implClass : loader) {
System.out.println(implClass.getClass().getSimpleName()); // prints Dog, Cat
}
Zauważ, że aby to zadziałało, musisz zdefiniować Petjako ServiceProviderInterface (SPI) i zadeklarować jego implementacje. to zrobić tworząc plik resources/META-INF/servicesz nazwą examples.reflections.Peti zadeklarować wszystkie implementacje Petw nim
examples.reflections.Dog
examples.reflections.Cat
Adnotacja na poziomie pakietu . Oto przykład:
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());
}
}
}
i definicja adnotacji:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PACKAGE)
public @interface MyPackageAnnotation {
Class<?>[] implementationsOfPet() default {};
}
i musisz zadeklarować adnotację na poziomie pakietu w pliku o nazwie package-info.javawewnątrz tego pakietu. oto przykładowa zawartość:
@MyPackageAnnotation(implementationsOfPet = {Dog.class, Cat.class})
package examples.reflections;
Należy zauważyć, że tylko pakiety, które są w danym momencie znane ClassLoader, zostaną załadowane przez wywołanie funkcji Package.getPackages().
Ponadto istnieją inne podejścia oparte na URLClassLoader, które zawsze będą ograniczone do klas, które zostały już załadowane, chyba że wykonasz wyszukiwanie w oparciu o katalog.
Co powiedział Erickson, ale jeśli nadal chcesz to zrobić, spójrz na Refleksje . Z ich strony:
Korzystając z refleksji, możesz wyszukiwać metadane pod kątem:
- pobierz wszystkie podtypy określonego typu
- pobierz wszystkie typy z adnotacjami
- pobierz wszystkie typy z adnotacjami, w tym z dopasowaniem parametrów adnotacji
- pobierz wszystkie metody z adnotacjami
new Reflections("my.package").getSubTypesOf(MyInterface.class)
Ogólnie rzecz biorąc, jest to kosztowne. Aby użyć odbicia, klasa musi zostać załadowana. Jeśli chcesz załadować każdą klasę dostępną w ścieżce klas, zajmie to trochę czasu i pamięci i nie jest zalecane.
Jeśli chcesz tego uniknąć, musisz zaimplementować własny parser plików klas, który działałby wydajniej, zamiast odbicia. Biblioteka inżynierii kodu bajtowego może pomóc w tym podejściu.
Mechanizm Service Provider jest konwencjonalne środki wyliczyć implementacje wtykowym usługi, a stała się bardziej powstała z wprowadzeniem projektu Jigsawa (moduły) w Java 9. Użyj ServiceLoaderw Javie 6, lub zaimplementować własną w poprzednich wersjach. Podałem przykład w innej odpowiedzi.
META-INF/services/java.sql.Driver
Wiosna ma na to całkiem prosty sposób:
public interface ITask {
void doStuff();
}
@Component
public class MyTask implements ITask {
public void doStuff(){}
}
Następnie możesz autowiretować listę typów, ITaska Spring zapełni ją wszystkimi implementacjami:
@Service
public class TaskService {
@Autowired
private List<ITask> tasks;
}
Najbardziej niezawodnym mechanizmem wyświetlania wszystkich klas implementujących dany interfejs jest obecnie ClassGraph , ponieważ obsługuje on najszerszy możliwy zestaw mechanizmów specyfikacji ścieżek klas , w tym nowy system modułów JPMS. (Jestem autorem.)
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
}
}
To, co powiedział Erikson, jest najlepsze. Oto powiązany wątek z pytaniami i odpowiedziami - http://www.velocityreviews.com/forums/t137693-find-all-implementing-classes-in-classpath.html
Biblioteka Apache BCEL umożliwia odczytywanie klas bez ich ładowania. Uważam, że będzie to szybsze, ponieważ powinieneś móc pominąć krok weryfikacji. Innym problemem związanym z ładowaniem wszystkich klas za pomocą modułu ładującego klasy jest to, że będziesz miał ogromny wpływ na pamięć, a także nieumyślnie uruchomisz jakiekolwiek statyczne bloki kodu, których prawdopodobnie nie chcesz robić.
Link do biblioteki Apache BCEL - http://jakarta.apache.org/bcel/
Dzięki ClassGraph jest to całkiem proste:
Groovy kod, aby znaleźć implementacje 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 { ... }w Groovy lub użyć try-with-resources w Javie: github.com/classgraph/classgraph/wiki/ ... Ponadto ostatnia część powinna być .name, nie .className, ponieważ .getName()jest to właściwa metoda na uzyskanie nazwy klasy z ClassInfoobiektu.
Tak, pierwszym krokiem jest zidentyfikowanie „wszystkich” zajęć, na których Ci zależało. Jeśli masz już te informacje, możesz wyliczyć każdy z nich i użyć instanceof, aby sprawdzić poprawność relacji. Powiązany artykuł jest tutaj: https://web.archive.org/web/20100226233915/www.javaworld.com/javaworld/javatips/jw-javatip113.html
Nowa wersja odpowiedzi @ kaybee99, ale teraz zwraca to, o co pyta użytkownik: implementacje ...
Wiosna ma na to całkiem prosty sposób:
public interface ITask {
void doStuff();
default ITask getImplementation() {
return this;
}
}
@Component
public class MyTask implements ITask {
public void doStuff(){}
}
Następnie możesz autowiretować listę typów, ITaska Spring zapełni ją wszystkimi implementacjami:
@Service
public class TaskService {
@Autowired(required = false)
private List<ITask> tasks;
if ( tasks != null)
for (ITask<?> taskImpl: tasks) {
taskImpl.doStuff();
}
}
Napotkałem ten sam problem. Moim rozwiązaniem było użycie odbicia do zbadania wszystkich metod w klasie ObjectFactory, eliminując te, które nie były metodami createXXX (), zwracając wystąpienie jednego z moich powiązanych obiektów POJO. Każda tak odkryta klasa jest dodawana do tablicy Class [], która jest następnie przekazywana do wywołania instancji JAXBContext. Działa to dobrze, wystarczy załadować klasę ObjectFactory, która i tak miała być potrzebna. Muszę tylko utrzymywać klasę ObjectFactory, zadanie wykonywane ręcznie (w moim przypadku, ponieważ zacząłem od POJO i użyłem schematu) lub może być wygenerowane w razie potrzeby przez xjc. Tak czy inaczej, jest wydajny, prosty i skuteczny.