Czy można znaleźć wszystkie klasy lub interfejsy w danym pakiecie? (Szybko patrząc np Package
. Wydaje się, że nie.)
java
reflection
packages
Jonik
źródło
źródło
Odpowiedzi:
Ze względu na dynamiczny charakter ładowarek klasowych nie jest to możliwe. Programy ładujące klasy nie są zobowiązane do powiedzenia maszynie wirtualnej, jakie klasy może ona zapewnić, zamiast tego są to tylko przekazywane żądania klas i muszą zwrócić klasę lub zgłosić wyjątek.
Jeśli jednak napiszesz własne moduły ładujące klasy lub przyjrzysz się ścieżkom klas i słojom, możesz znaleźć te informacje. Stanie się tak za pośrednictwem operacji systemu plików, a nie refleksji. Mogą nawet istnieć biblioteki, które mogą ci w tym pomóc.
Jeśli istnieją klasy, które są generowane lub dostarczane zdalnie, nie będzie można ich odkryć.
Normalną metodą jest zamiast tego zarejestrować gdzieś klasy, do których potrzebujesz dostępu w pliku, lub odwołać się do nich w innej klasie. Lub po prostu używaj konwencji, jeśli chodzi o nazewnictwo.
Dodatek: Biblioteka Refleksji pozwoli ci wyszukać klasy w bieżącej ścieżce klas. Można go użyć, aby uzyskać wszystkie klasy w pakiecie:
źródło
Prawdopodobnie powinieneś rzucić okiem na bibliotekę Reflections typu open source . Dzięki niemu możesz łatwo osiągnąć to, co chcesz.
Najpierw skonfiguruj indeks odbić (jest trochę nieporządny, ponieważ wyszukiwanie wszystkich klas jest domyślnie wyłączone):
Następnie możesz zapytać o wszystkie obiekty w danym pakiecie:
źródło
.addUrls(ClasspathHelper.forJavaClassPath())
zamiast powyższego rozwiązało je dla mnie. Również mniej kodu!Google Guava 14 zawiera nową klasę
ClassPath
z trzema metodami skanowania w poszukiwaniu klas najwyższego poziomu:getTopLevelClasses()
getTopLevelClasses(String packageName)
getTopLevelClassesRecursive(String packageName)
Zobacz
ClassPath
javadocs aby uzyskać więcej informacji.źródło
ClassPath
jest oznaczony@Beta
, więc może nie być dobrym pomysłem dla niektórych ...Możesz użyć tej metody 1, która używa
ClassLoader
.__________
1 Ta metoda została pierwotnie zaczerpnięta z http://snippets.dzone.com/posts/show/4831 , który został zarchiwizowany przez Internet Archive, tak jak teraz. Fragment jest również dostępny na stronie https://dzone.com/articles/get-all-classes-within-package .
źródło
%20
, alenew File()
konstruktor traktował to jako dosłowny znak procentu dwa zero. Naprawiłem to, zmieniającdirs.add(...)
linię na to:dirs.add(new File(resource.toURI()));
Oznaczało to również, że musiałem dodaćURISyntaxException
do klauzuligetClasses
Wiosna
Ten przykład dotyczy Spring 4, ale skaner ścieżek klasy można znaleźć również we wcześniejszych wersjach.
Google Guava
Uwaga: W wersji 14 interfejs API jest nadal oznaczony jako @Beta , więc należy zachować ostrożność w kodzie produkcyjnym.
źródło
ClassPath
klasa w Guava jest również oznaczona@Beta
: „Interfejsy API oznaczone adnotacją @Beta na poziomie klasy lub metody mogą ulec zmianie. Można je zmodyfikować w dowolny sposób, a nawet usunąć w dowolnej ważniejszej wersji. Jeśli Twój kod jest samą biblioteką (tzn. jest używana na CLASSPATH użytkowników poza Twoją kontrolą), nie powinieneś używać beta API, chyba że je przepakujeszgetAllClasses()
można zastosować metodę.Witaj. Zawsze miałem problemy z powyższymi rozwiązaniami (i na innych stronach).
Jako programista programuję dodatek do interfejsu API. Interfejs API uniemożliwia korzystanie z zewnętrznych bibliotek lub narzędzi innych firm. Konfiguracja składa się również z mieszanki kodu w plikach jar lub zip oraz plików klas znajdujących się bezpośrednio w niektórych katalogach. Tak więc mój kod musiał być w stanie działać w każdej konfiguracji. Po wielu badaniach wymyśliłem metodę, która będzie działać w co najmniej 95% wszystkich możliwych konfiguracji.
Poniższy kod jest w zasadzie metodą nadmiaru, która zawsze będzie działać.
Kod:
Ten kod skanuje dany pakiet pod kątem wszystkich klas, które są w nim zawarte. Będzie działać tylko dla wszystkich klas w bieżącym
ClassLoader
.Te trzy metody umożliwiają znalezienie wszystkich klas w danym pakiecie.
Używasz go w ten sposób:
Wyjaśnienie:
Metoda najpierw pobiera prąd
ClassLoader
. Następnie pobiera wszystkie zasoby zawierające wspomniany pakiet i iteruje jeURL
. Następnie tworzy aURLConnection
i określa, jaki typ UR1 mamy. Może to być katalog (FileURLConnection
) lub katalog w pliku jar lub zip (JarURLConnection
). W zależności od rodzaju połączenia będziemy wywoływać dwie różne metody.Najpierw zobaczmy, co się stanie, jeśli jest to
FileURLConnection
.Najpierw sprawdza, czy przekazany plik istnieje i jest katalogiem. W takim przypadku sprawdza, czy jest to plik klasy. Jeśli tak,
Class
obiekt zostanie utworzony i umieszczony wArrayList
. Jeśli nie jest to plik klasy, ale katalog, po prostu iterujemy w nim i robimy to samo. Wszystkie pozostałe przypadki / pliki zostaną zignorowane.Jeśli
URLConnection
jest toJarURLConnection
inna metoda prywatnej pomocy zostanie wywołana. Ta metoda iteruje wszystkie wpisy w archiwum zip / jar. Jeśli jeden wpis jest plikiem klasy i znajduje się w pakiecie,Class
obiekt zostanie utworzony i zapisany w plikuArrayList
.Po przeanalizowaniu wszystkich zasobów (metoda główna) zwraca
ArrayList
zawartość wszystkich klas w danym pakiecie, o której aktualnieClassLoader
wie.Jeśli proces zakończy się niepowodzeniem w dowolnym momencie,
ClassNotFoundException
zostanie wyświetlony komunikat zawierający szczegółowe informacje na temat dokładnej przyczyny.źródło
sun.net.www.protocol.file.FileURLConnection
, który generuje ostrzeżenie w czasie kompilacji („ostrzeżenie: sun.net.www.protocol.file.FileURLConnection jest zastrzeżonym API firmy Sun i może zostać usunięty w przyszłej wersji”). Czy istnieje alternatywa dla korzystania z tej klasy, czy też można ostrzec to za pomocą adnotacji?if ( ... instanceof JarURLConnecton) { ... } else { // Asume that the Connection is valid and points to a File }
To właśnie zrobiłem na moim kodzie, aby przeszukiwać klasy z komentarzami JPABez użycia dodatkowych bibliotek:
źródło
upackage
jestnull
... :(String pack = getPackage().getName();
, musisz dodaćpack = pack.replaceAll("[.]", "/");
Generalnie programy ładujące klasy nie pozwalają na skanowanie wszystkich klas w ścieżce klas. Ale zwykle jedynym używanym modułem ładującym jest UrlClassLoader, z którego możemy pobrać listę katalogów i plików jar (patrz getURL ) i otwierać je jeden po drugim, aby wyświetlić listę dostępnych klas. To podejście, zwane skanowaniem ścieżki klasy, jest zaimplementowane w Scannotation and Reflections .
Innym podejściem jest użycie Java Pluggable Annotation Processing API do napisania procesora adnotacji, który zbierze wszystkie klasy adnotacji w czasie kompilacji i zbuduje plik indeksu na potrzeby środowiska wykonawczego. Ten mechanizm jest zaimplementowany w bibliotece ClassIndex :
Zauważ, że nie jest wymagana dodatkowa konfiguracja, ponieważ skanowanie jest w pełni zautomatyzowane, dzięki kompilatorowi Java automatycznie wykrywającemu wszystkie procesory znalezione w ścieżce klasy.
źródło
Najsolidniejszym mechanizmem wyświetlania wszystkich klas w danym pakiecie jest obecnie ClassGraph , ponieważ obsługuje on możliwie najszerszy zestaw mechanizmów specyfikacji ścieżek klas , w tym nowy system modułów JPMS. (Jestem autorem.)
źródło
Oto jak to robię. Skanuję wszystkie podfoldery (paczki) i nie próbuję ładować anonimowych klas:
źródło
Przygotowałem prosty projekt github, który rozwiązuje ten problem:
https://github.com/ddopson/java-class-enumerator
Powinno to działać zarówno ZARÓWNO dla ścieżek klas ORAZ dla plików jar.
Jeśli uruchomisz „make” po sprawdzeniu projektu, wydrukuje to:
Zobacz także moją inną odpowiedź
źródło
Tak, używając kilku interfejsów API, które możesz, oto, jak lubię to robić, zmierzyłem się z tym problemem, którego użyłem hibernacji rdzenia i musiałem znaleźć klasy, które opatrzone były adnotacjami.
Zrób z nich niestandardową adnotację, za pomocą której zaznaczysz, które klasy chcesz odebrać.
Następnie zaznacz swoją klasę w ten sposób
Utwórz tę klasę narzędzia, która ma następującą metodę
Wywołaj metodę allFoundClassesAnnotatedWithEntityToBeScanned () , aby uzyskać znaleziony zestaw klas.
Będziesz potrzebował podanych poniżej bibliotek
źródło
Musisz wyszukać każdą pozycję modułu ładującego klasy na ścieżce klasy:
Jeśli wpis jest katalogiem, po prostu wyszukaj w odpowiednim podkatalogu:
Jeśli wpis jest plikiem i jest słoikiem, sprawdź jego wpisy ZIP:
Teraz, gdy masz już wszystkie nazwy klas z pakietem, możesz spróbować załadować je z refleksją i przeanalizować, czy są to klasy, czy interfejsy itp.
źródło
Próbowałem użyć biblioteki Reflections, ale miałem pewne problemy z jej użyciem, a było zbyt wiele słoików, które powinienem uwzględnić, aby po prostu uzyskać klasy z pakietu.
Opublikuję rozwiązanie, które znalazłem w tym zduplikowanym pytaniu: Jak uzyskać nazwy wszystkich klas w pakiecie?
Odpowiedź została napisana przez sp00m ; Dodałem kilka poprawek, aby działało:
Aby go użyć, po prostu wywołaj metodę find jako sp00n wspomnianą w tym przykładzie: Dodałem tworzenie instancji klas w razie potrzeby.
źródło
Właśnie napisałem klasę util, zawiera metody testowe, możesz sprawdzić ~
IteratePackageUtil.java:
źródło
Rozwiązanie Aleksandra Blomskølda nie działało dla mnie w przypadku sparametryzowanych testów
@RunWith(Parameterized.class)
podczas korzystania z Maven. Testy zostały poprawnie nazwane, a także znalezione, ale nie wykonane:Podobny problem został zgłoszony tutaj .
W moim przypadku
@Parameters
jest tworzenie instancji każdej klasy w pakiecie. Testy działały dobrze, gdy są uruchamiane lokalnie w IDE. Jednak podczas uruchamiania Maven nie znaleziono klas z rozwiązaniem Aleksandra Blomskølda.Sprawiłem, że zadziałało z poniższym wycinkiem, zainspirowanym komentarzem Davida Pärssona do odpowiedzi Aleksandra Blomskølda:
źródło
A co z tym:
Następnie możesz przeciążyć funkcję:
Jeśli musisz to przetestować:
Jeśli twoje IDE nie ma pomocnika importu:
To działa:
z twojego IDE
dla pliku JAR
bez zewnętrznych zależności
źródło
Prawie wszystkie odpowiedzi wykorzystują
Reflections
lub odczytują pliki klas z systemu plików. Jeśli spróbujesz odczytać klasy z systemu plików, możesz napotkać błędy podczas pakowania aplikacji jako JAR lub innej. Możesz także nie chcieć używać do tego celu oddzielnej biblioteki.Oto inne podejście, które jest czystą Javą i nie zależy od systemu plików.
Java 8 nie jest koniecznością . Możesz używać pętli zamiast strumieni. I możesz to przetestować w ten sposób
źródło
ToolProvider.getSystemJavaCompiler()
, ten kod nie skanuje zagnieżdżonych pakietów.Pod warunkiem, że nie używasz żadnych dynamicznych programów ładujących klasy, możesz przeszukiwać ścieżkę klasy i dla każdej pozycji przeszukiwać katalog lub plik JAR.
źródło
Warte wspomnienia
Jeśli chcesz mieć listę wszystkich klas w ramach jednego pakietu, możesz użyć
Reflection
następującego sposobu:Spowoduje to utworzenie listy klas, z których będziesz mógł później korzystać według własnego uznania.
źródło
Jest to bardzo możliwe, ale bez dodatkowych bibliotek, takich jak
Reflections
to jest trudne ...Jest trudne, ponieważ nie masz pełnego instrumentu do uzyskania nazwy klasy.
I biorę kod mojej
ClassFinder
klasy:źródło
W oparciu o odpowiedź @ Staale i próbując nie polegać na bibliotekach stron trzecich, wdrożyłbym podejście do systemu plików, sprawdzając fizyczną lokalizację pierwszego pakietu za pomocą:
źródło
Jeśli chcesz tylko załadować grupę pokrewnych klas, Spring może ci pomóc.
Spring może utworzyć instancję listy lub mapy wszystkich klas, które implementują dany interfejs w jednym wierszu kodu. Lista lub mapa będą zawierać wystąpienia wszystkich klas, które implementują ten interfejs.
To powiedziawszy, jako alternatywa dla ładowania listy klas z systemu plików, zamiast tego po prostu zaimplementuj ten sam interfejs we wszystkich klasach, które chcesz załadować, niezależnie od pakietu i użyj Springa, aby zapewnić wystąpienie wszystkich z nich. W ten sposób możesz załadować (i utworzyć) wszystkie pożądane klasy, niezależnie od tego, w jakim pakiecie się znajdują.
Z drugiej strony, jeśli chcesz mieć je wszystkie w pakiecie, to po prostu wszystkie klasy w tym pakiecie implementują dany interfejs.
źródło
zwykła java: FindAllClassesUsingPlainJavaReflectionTest.java
PS: aby uprościć plansze do obsługi błędów itp., Używam tutaj
vavr
ilombok
bibliotekinne implementacje można znaleźć w moim repozytorium GitHub daggerok / java-reflection-find-annotated-class-or-method
źródło
Nie mogłem znaleźć krótkiej pracy wyciętej na coś tak prostego. A więc oto zrobiłem to sam po tym, jak trochę się przekręciłem:
źródło
Jeśli jesteś w Spring Spring, możesz użyć
PathMatchingResourcePatternResolver
;źródło
Nie jest to możliwe, ponieważ wszystkie klasy w pakiecie mogą nie zostać załadowane, podczas gdy zawsze znasz pakiet klasy.
źródło