Muszę przeczytać klasy zawarte w pakiecie Java. Te klasy znajdują się w ścieżce klas. Muszę wykonać to zadanie bezpośrednio z programu Java. Czy znasz prosty sposób?
List<Class> classes = readClassesFrom("my.package")
Muszę przeczytać klasy zawarte w pakiecie Java. Te klasy znajdują się w ścieżce klas. Muszę wykonać to zadanie bezpośrednio z programu Java. Czy znasz prosty sposób?
List<Class> classes = readClassesFrom("my.package")
Odpowiedzi:
Jeśli masz wiosnę w swojej ścieżce klas, zrobią to poniższe czynności.
Znajdź wszystkie klasy w pakiecie, które są oznaczone adnotacją XmlRootElement:
private List<Class> findMyTypes(String basePackage) throws IOException, ClassNotFoundException
{
ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resourcePatternResolver);
List<Class> candidates = new ArrayList<Class>();
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + "/" + "**/*.class";
Resource[] resources = resourcePatternResolver.getResources(packageSearchPath);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(resource);
if (isCandidate(metadataReader)) {
candidates.add(Class.forName(metadataReader.getClassMetadata().getClassName()));
}
}
}
return candidates;
}
private String resolveBasePackage(String basePackage) {
return ClassUtils.convertClassNameToResourcePath(SystemPropertyUtils.resolvePlaceholders(basePackage));
}
private boolean isCandidate(MetadataReader metadataReader) throws ClassNotFoundException
{
try {
Class c = Class.forName(metadataReader.getClassMetadata().getClassName());
if (c.getAnnotation(XmlRootElement.class) != null) {
return true;
}
}
catch(Throwable e){
}
return false;
}
Możesz użyć opisanego tutaj projektu Reflections
Jest całkiem kompletny i łatwy w użyciu.
Krótki opis z powyższej strony internetowej:
Reflections skanuje ścieżkę klas, indeksuje metadane, umożliwia tworzenie zapytań w czasie wykonywania oraz może zapisywać i zbierać te informacje dla wielu modułów w projekcie.
Przykład:
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);
Używam tego, działa z plikami lub archiwami jar
public static ArrayList<String>getClassNamesFromPackage(String packageName) throws IOException{
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
URL packageURL;
ArrayList<String> names = new ArrayList<String>();;
packageName = packageName.replace(".", "/");
packageURL = classLoader.getResource(packageName);
if(packageURL.getProtocol().equals("jar")){
String jarFileName;
JarFile jf ;
Enumeration<JarEntry> jarEntries;
String entryName;
// build jar file name, then loop through zipped entries
jarFileName = URLDecoder.decode(packageURL.getFile(), "UTF-8");
jarFileName = jarFileName.substring(5,jarFileName.indexOf("!"));
System.out.println(">"+jarFileName);
jf = new JarFile(jarFileName);
jarEntries = jf.entries();
while(jarEntries.hasMoreElements()){
entryName = jarEntries.nextElement().getName();
if(entryName.startsWith(packageName) && entryName.length()>packageName.length()+5){
entryName = entryName.substring(packageName.length(),entryName.lastIndexOf('.'));
names.add(entryName);
}
}
// loop through files in classpath
}else{
URI uri = new URI(packageURL.toString());
File folder = new File(uri.getPath());
// won't work with path which contains blank (%20)
// File folder = new File(packageURL.getFile());
File[] contenuti = folder.listFiles();
String entryName;
for(File actual: contenuti){
entryName = actual.getName();
entryName = entryName.substring(0, entryName.lastIndexOf('.'));
names.add(entryName);
}
}
return names;
}
Spring zaimplementował doskonałą funkcję wyszukiwania ścieżek klas w PathMatchingResourcePatternResolver
. Jeśli użyjesz classpath*
przedrostka:, możesz znaleźć wszystkie zasoby, w tym klasy w danej hierarchii, a nawet je przefiltrować, jeśli chcesz. Następnie można użyć dzieci AbstractTypeHierarchyTraversingFilter
, AnnotationTypeFilter
i AssignableTypeFilter
filtrować te zasoby zarówno na poziomie klasy lub adnotacji na interfejsach ich wdrożenia.
Java 1.6.0_24:
public static File[] getPackageContent(String packageName) throws IOException{
ArrayList<File> list = new ArrayList<File>();
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader()
.getResources(packageName);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
File dir = new File(url.getFile());
for (File f : dir.listFiles()) {
list.add(f);
}
}
return list.toArray(new File[]{});
}
To rozwiązanie zostało przetestowane w środowisku EJB .
Scannotation and Reflections używają metody skanowania ścieżki klas:
Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
Innym podejściem jest użycie Java Pluggable Annotation Processing API do napisania procesora adnotacji, który zbierze wszystkie klasy z adnotacjami w czasie kompilacji i zbuduje plik indeksu do użycia w czasie wykonywania. Ten mechanizm jest zaimplementowany w bibliotece ClassIndex :
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
Najbardziej niezawodnym mechanizmem wyświetlania wszystkich klas w danym pakiecie 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.)
List<String> classNames;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
.enableClassInfo().scan()) {
classNames = scanResult.getAllClasses().getNames();
}
O ile wiem, tej funkcji nadal podejrzanie brakuje w Java Reflection API. Możesz uzyskać obiekt pakietu, robiąc to:
Package packageObj = Package.getPackage("my.package");
Ale jak zapewne zauważyłeś, nie pozwoli ci to wyświetlić klas w tym pakiecie. W tej chwili musisz przyjąć podejście bardziej zorientowane na system plików.
W tym poście znalazłem kilka przykładowych implementacji
Nie jestem w 100% pewien, że te metody zadziałają, gdy twoje klasy są zakopane w plikach JAR, ale mam nadzieję, że jedna z nich zrobi to za Ciebie.
Zgadzam się z @skaffman ... jeśli masz inny sposób, polecam to zrobić.
eXtcos wygląda obiecująco. Wyobraź sobie, że chcesz znaleźć wszystkie zajęcia, które:
Z eXtcos jest to tak proste, jak
ClasspathScanner scanner = new ClasspathScanner();
final Set<Class> classStore = new ArraySet<Class>();
Set<Class> classes = scanner.getClasses(new ClassQuery() {
protected void query() {
select().
from(“common”).
andStore(thoseExtending(Component.class).into(classStore)).
returning(allAnnotatedWith(MyComponent.class));
}
});
Bill Burke napisał (fajny artykuł o skanowaniu klas), a następnie napisał Scannotation .
Hibernate ma już napisane:
CDI może rozwiązać ten problem, ale nie wiem - nie zostało jeszcze w pełni zbadane
.
@Inject Instance< MyClass> x;
...
x.iterator()
Również w przypadku adnotacji:
abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}
Tak się składa, że zaimplementowałem to i działa w większości przypadków. Ponieważ jest długi, umieściłem go tutaj w pliku .
Chodzi o to, aby znaleźć lokalizację pliku źródłowego klasy, która jest dostępna w większości przypadków (znanym wyjątkiem są pliki klas JVM - o ile testowałem). Jeśli kod znajduje się w katalogu, przejrzyj wszystkie pliki i tylko znajdź pliki klas. Jeśli kod znajduje się w pliku JAR, przeskanuj wszystkie wpisy.
Tej metody można użyć tylko wtedy, gdy:
Masz klasę, która znajduje się w tym samym pakiecie, który chcesz odkryć. Ta klasa nazywa się SeedClass. Na przykład, jeśli chcesz wyświetlić wszystkie klasy w „java.io”, klasą źródłową może być java.io.File
.
Twoje klasy znajdują się w katalogu lub w pliku JAR, który zawiera informacje o pliku źródłowym (nie plik z kodem źródłowym, ale tylko plik źródłowy). O ile próbowałem, działa prawie w 100% z wyjątkiem klasy JVM (te klasy są dostarczane z JVM).
Twój program musi mieć uprawnienia dostępu do ProtectionDomain tych klas. Jeśli twój program jest ładowany lokalnie, nie powinno być problemu.
Przetestowałem program tylko do mojego zwykłego użytku, więc nadal może występować problem.
Mam nadzieję, że to pomoże.
Oto kolejna opcja, niewielka modyfikacja innej odpowiedzi powyżej / poniżej:
Reflections reflections = new Reflections("com.example.project.package",
new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses =
reflections.getSubTypesOf(Object.class);
W czasach, gdy aplety były powszechne, można było mieć adres URL w ścieżce klas. Gdy classloader wymagał klasy, przeszukiwał wszystkie lokalizacje na ścieżce klas, w tym zasoby http. Ponieważ w ścieżce klas możesz mieć takie rzeczy, jak adresy URL i katalogi, nie ma łatwego sposobu na uzyskanie ostatecznej listy klas.
Możesz jednak podejść całkiem blisko. Niektóre biblioteki Spring robią to teraz. Możesz pobrać wszystkie słoiki na ścieżce klas i otworzyć je jak pliki. Następnie możesz wziąć tę listę plików i utworzyć strukturę danych zawierającą twoje klasy.
użyj narzędzia zależności:
groupId: net.sf.extcos
artifactId: extcos
version: 0.4b
następnie użyj tego kodu:
ComponentScanner scanner = new ComponentScanner();
Set classes = scanner.getClasses(new ComponentQuery() {
@Override
protected void query() {
select().from("com.leyton").returning(allExtending(DynamicForm.class));
}
});
Brent - powód, dla którego asocjacja jest jednym ze sposobów, ma związek z faktem, że każda klasa w dowolnym komponencie CLASSPATH może zadeklarować się w dowolnym pakiecie (z wyjątkiem java / javax). Dlatego po prostu nie ma mapowania WSZYSTKICH klas w danym „pakiecie”, ponieważ nikt nie wie ani nie może wiedzieć. Możesz jutro zaktualizować plik jar i usunąć lub dodać klasy. To tak, jakby próbować uzyskać listę wszystkich osób o imieniu John / Jon / Johan we wszystkich krajach świata - nikt z nas nie jest wszechwiedzący, więc nikt z nas nigdy nie będzie miał prawidłowej odpowiedzi.