Ответ 1
Хорошо, что я сделал, просто перечислял все файлы в пути к классам. Это может быть не славное решение, но оно работает надежно и дает мне все, что я хочу, и многое другое.
Я хотел бы получить список всех классов, принадлежащих определенному пакету, а также всем своим детям. Классы могут или не могут быть уже загружены в JVM.
Хорошо, что я сделал, просто перечислял все файлы в пути к классам. Это может быть не славное решение, но оно работает надежно и дает мне все, что я хочу, и многое другое.
Это не программное решение, но вы можете запустить
java -verbose:class ....
и JVM выгрузит загрузку и откуда.
[Opened /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/sunrsasign.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jsse.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/jce.jar]
[Opened /usr/java/j2sdk1.4.1/jre/lib/charsets.jar]
[Loaded java.lang.Object from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.io.Serializable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.Comparable from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.CharSequence from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
[Loaded java.lang.String from /usr/java/j2sdk1.4.1/jre/lib/rt.jar]
Подробнее см. здесь.
используя библиотеку Reflections, это легко:
Reflections reflections = new Reflections("my.pkg", new SubTypesScanner(false));
Это сканирует все классы в url/s, которые содержат пакет my.pkg.
Итак, получение всех классов эффективно получает все подтипы Object, транзитивно:
Set<String> allClasses =
reflections.getStore().getSubTypesOf(Object.class.getName());
(Обычный способ reflections.getSubTypesOf(Object.class)
приведет к загрузке классов all в PermGen и, вероятно, вызовет OutOfMemoryError, вы не захотите это делать...)
Если вы хотите получить все прямые подтипы объекта (или любого другого типа), не получая его транзитивных подтипов за один раз, используйте это:
Collection<String> directSubtypes =
reflections.getStore().get(SubTypesScanner.class).get(Object.class.getName());
Есть несколько ответов на этот вопрос, отчасти из-за неоднозначного вопроса - заголовок говорит о классах, загружаемых JVM, тогда как содержание вопроса говорит "может или не может быть загружено JVM".
Предполагая, что OP требует классов, загружаемых JVM заданным загрузчиком классов, и только эти классы - моя потребность - есть решение (разработанный здесь), который выглядит следующим образом:
import java.net.URL;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Vector;
public class CPTest {
private static Iterator list(ClassLoader CL)
throws NoSuchFieldException, SecurityException,
IllegalArgumentException, IllegalAccessException {
Class CL_class = CL.getClass();
while (CL_class != java.lang.ClassLoader.class) {
CL_class = CL_class.getSuperclass();
}
java.lang.reflect.Field ClassLoader_classes_field = CL_class
.getDeclaredField("classes");
ClassLoader_classes_field.setAccessible(true);
Vector classes = (Vector) ClassLoader_classes_field.get(CL);
return classes.iterator();
}
public static void main(String args[]) throws Exception {
ClassLoader myCL = Thread.currentThread().getContextClassLoader();
while (myCL != null) {
System.out.println("ClassLoader: " + myCL);
for (Iterator iter = list(myCL); iter.hasNext();) {
System.out.println("\t" + iter.next());
}
myCL = myCL.getParent();
}
}
}
Одна из опрятных вещей заключается в том, что вы можете выбрать произвольный загрузчик классов, который хотите проверить. Тем не менее, вероятно, он будет разрушен, если внутренние классы класса classload меняются, поэтому он должен использоваться как одноразовый диагностический инструмент.
Альтернативный подход к описанным выше может заключаться в создании внешнего агента, использующего java.lang.instrument
, чтобы узнать, какие классы загружены и запустить вашу программу с помощью переключателя -javaagent
:
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
public class SimpleTransformer implements ClassFileTransformer {
public SimpleTransformer() {
super();
}
public byte[] transform(ClassLoader loader, String className, Class redefiningClass, ProtectionDomain domain, byte[] bytes) throws IllegalClassFormatException {
System.out.println("Loading class: " + className);
return bytes;
}
}
Этот подход имеет дополнительное преимущество, предоставляя вам информацию о том, какой ClassLoader загружал данный класс.
Я также предлагаю вам написать агент -javagent
, но используйте getAllLoadedClasses вместо преобразования любых классов.
Чтобы синхронизировать с вашим кодом клиента (обычный код Java), создайте сокет и обменивайтесь с ним агентом. Затем вы можете запускать метод "список всех классов", когда вам нужно.
В одном случае, если вы уже знаете путь верхнего уровня пакета, используйте OpenPojo
final List<PojoClass> pojoClasses = PojoClassFactory.getPojoClassesRecursively("my.package.path", null);
Затем вы можете просмотреть список и выполнить любую функциональность, которую вы хотите.
Возможно, вы сможете получить список классов, загружаемых через загрузчик классов, но это не будет включать классы, которые вы еще не загрузили, но находятся в вашем пути к классам.
Чтобы получить ВСЕ классы в вашем пути к классам, вы должны сделать что-то вроде своего второго решения. Если вам действительно нужны классы, которые в настоящее время "загружены" (другими словами, классы, на которые вы уже ссылались, обращались к ним или инстанцировались), вы должны уточнить свой вопрос, чтобы указать это.
Запустите свой код под JRockit JVM, затем используйте JRCMD <PID> print_class_summary
Это приведет к выводу всех загруженных классов, по одному в каждой строке.
Эта программа будет печатать все классы с ее физическим путем. использование может просто скопировать это в любой JSP, если вам нужно проанализировать загрузку класса с любого веб-сервера приложений.
import java.lang.reflect.Field;
import java.util.Vector;
public class TestMain {
public static void main(String[] args) {
Field f;
try {
f = ClassLoader.class.getDeclaredField("classes");
f.setAccessible(true);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Vector<Class> classes = (Vector<Class>) f.get(classLoader);
for(Class cls : classes){
java.net.URL location = cls.getResource('/' + cls.getName().replace('.',
'/') + ".class");
System.out.println("<p>"+location +"<p/>");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}