Мне нужно прочитать классы, содержащиеся в пакете Java. Эти классы находятся в пути к классам. Мне нужно выполнить эту задачу напрямую из программы Java. Вы знаете простой способ сделать?
List<Class> classes = readClassesFrom("my.package")
Мне нужно прочитать классы, содержащиеся в пакете Java. Эти классы находятся в пути к классам. Мне нужно выполнить эту задачу напрямую из программы Java. Вы знаете простой способ сделать?
List<Class> classes = readClassesFrom("my.package")
Ответы:
Если у вас есть Spring в пути к классам, то это сделает следующее.
Найдите все классы в пакете, помеченные 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;
}
Вы можете использовать проект Reflections, описанный здесь
Он довольно полный и простой в использовании.
Краткое описание с указанного выше сайта:
Reflections сканирует ваш путь к классам, индексирует метаданные, позволяет запрашивать их во время выполнения и может сохранять и собирать эту информацию для многих модулей в вашем проекте.
Пример:
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.setUrls(ClasspathHelper.forJavaClassPath())
);
Set<Class<?>> types = reflections.getTypesAnnotatedWith(Scannable.class);
Я использую этот, он работает с файлами или 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 реализовал отличную функцию поиска пути к классам в PathMatchingResourcePatternResolver
. Если вы используете classpath*
префикс:, вы можете найти все ресурсы, включая классы в данной иерархии, и даже отфильтровать их, если хотите. Затем вы можете использовать ребенок AbstractTypeHierarchyTraversingFilter
, AnnotationTypeFilter
и AssignableTypeFilter
фильтровать эти ресурсы либо на аннотациях на уровне класса или интерфейс они реализуют.
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[]{});
}
Это решение было протестировано в среде EJB .
Scannotation и Reflections используют подход сканирования пути к классам:
Reflections reflections = new Reflections("my.package");
Set<Class<? extends Object>> classes = reflections.getSubTypesOf(Object.class);
Другой подход - использовать Java Pluggable Annotation Processing API для написания процессора аннотаций, который будет собирать все аннотированные классы во время компиляции и создавать индексный файл для использования во время выполнения. Этот механизм реализован в библиотеке ClassIndex :
Iterable<Class> classes = ClassIndex.getPackageClasses("my.package");
Насколько мне известно, эта функциональность все еще подозрительно отсутствует в API отражения Java. Вы можете получить объект пакета, просто сделав следующее:
Package packageObj = Package.getPackage("my.package");
Но, как вы, наверное, заметили, это не позволит вам перечислить классы в этом пакете. На данный момент вы должны использовать подход, более ориентированный на файловую систему.
Я нашел несколько примеров реализации в этом посте
Я не уверен на 100%, что эти методы будут работать, когда ваши классы похоронены в файлах JAR, но я надеюсь, что один из них сделает это за вас.
Я согласен с @skaffman ... если у вас есть другой способ сделать это, я бы рекомендовал сделать это вместо этого.
Наиболее надежным механизмом для перечисления всех классов в данном пакете в настоящее время является ClassGraph , поскольку он обрабатывает максимально широкий спектр механизмов спецификации пути к классам , включая новую модульную систему JPMS. (Я автор.)
List<String> classNames;
try (ScanResult scanResult = new ClassGraph().whitelistPackages("my.package")
.enableClassInfo().scan()) {
classNames = scanResult.getAllClasses().getNames();
}
eXtcos выглядит многообещающе. Представьте, что вы хотите найти все классы, которые:
С eXtcos это так же просто, как
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));
}
});
Билл Берк написал (красивую статью о сканировании классов), а затем написал Scannotation .
В Hibernate уже написано:
CDI может решить эту проблему, но не знаю - еще не исследовал полностью
.
@Inject Instance< MyClass> x;
...
x.iterator()
Также для аннотаций:
abstract class MyAnnotationQualifier
extends AnnotationLiteral<Entity> implements Entity {}
Я реализовал его, и в большинстве случаев он работает. Так как он длинный, я положил его сюда в файл .
Идея состоит в том, чтобы найти местоположение исходного файла класса, который доступен в большинстве случаев (известным исключением являются файлы классов JVM - насколько я тестировал). Если код находится в каталоге, просканируйте все файлы и найдите только файлы классов. Если код находится в файле JAR, просканируйте все записи.
Этот метод можно использовать только когда:
У вас есть класс, который находится в том же пакете, который вы хотите обнаружить. Этот класс называется SeedClass. Например, если вы хотите перечислить все классы в 'java.io', начальный класс может быть java.io.File
.
Ваши классы находятся в каталоге или в файле JAR, в котором есть информация об исходном файле (не файл исходного кода, а только исходный файл). Насколько я пробовал, он работает почти на 100%, за исключением класса JVM (эти классы поставляются с JVM).
Ваша программа должна иметь разрешение на доступ к ProtectionDomain этих классов. Если ваша программа загружается локально, проблем быть не должно.
Я тестировал программу только для регулярного использования, поэтому проблема все еще может быть.
Надеюсь, это поможет.
Вот еще один вариант, небольшая модификация другого ответа выше / ниже:
Reflections reflections = new Reflections("com.example.project.package",
new SubTypesScanner(false));
Set<Class<? extends Object>> allClasses =
reflections.getSubTypesOf(Object.class);
Когда-то апплеты были обычным явлением, в пути к классам мог быть URL-адрес. Когда загрузчику классов требовался класс, он выполнял поиск во всех местах пути к классам, включая ресурсы http. Поскольку в пути к классам могут быть такие вещи, как URL-адреса и каталоги, нет простого способа получить окончательный список классов.
Однако можно подойти довольно близко. Некоторые библиотеки Spring делают это сейчас. Вы можете получить все файлы jar в пути к классам и открывать их как файлы. Затем вы можете взять этот список файлов и создать структуру данных, содержащую ваши классы.
использовать maven зависимости:
groupId: net.sf.extcos
artifactId: extcos
version: 0.4b
затем используйте этот код:
ComponentScanner scanner = new ComponentScanner();
Set classes = scanner.getClasses(new ComponentQuery() {
@Override
protected void query() {
select().from("com.leyton").returning(allExtending(DynamicForm.class));
}
});
Brent - причина, по которой ассоциация является одним из способов, связана с тем фактом, что любой класс любого компонента вашего CLASSPATH может объявить себя в любом пакете (кроме java / javax). Таким образом, просто нет отображения ВСЕХ классов в данном «пакете», потому что никто не знает и не может знать. Вы можете обновить файл jar завтра и удалить или добавить классы. Это все равно, что пытаться получить список всех людей по имени Джон / Джон / Йохан во всех странах мира - никто из нас не всеведущ, поэтому ни у кого из нас никогда не будет правильного ответа.