1. Обзор
В этом уроке мы проанализируем метод , чтобы перечислить все классы, загруженные определенным загрузчиком классов в Java, используя Java Instrumentation API . Мы также увидим, как создать и загрузить агент Java, чтобы получить экземпляр Instrumentation и вызвать необходимые методы для выполнения нашей задачи.
2. Загрузчики классов в Java
Загрузчики классов являются неотъемлемой частью JRE (среды выполнения Java). Их задача состоит в том, чтобы динамически загружать классы в виртуальную машину Java . Другими словами, они загружают классы в память по требованию, когда это требуется приложением. В статье о загрузчиках классов Java рассказывается об их различных типах и дается подробное понимание того, как они работают.
3. Использование API инструментария
Интерфейс Instrumentation предоставляет метод get Initiated Classes(загрузчик загрузчика классов) , который может быть вызван для | возврата массива, содержащего все классы, загруженные конкретным загрузчиком . Давайте посмотрим, как это работает.
Во-первых, нам нужно создать и загрузить агент, чтобы получить экземпляр интерфейса Instrumentation . Агент Java – это инструмент для управления программами, запущенными на виртуальной машине Java (JVM).
Другими словами, он может добавлять или изменять байт-код методов с целью сбора данных. Нам потребуется агент , чтобы получить дескриптор экземпляра Instrumentation и вызвать необходимый метод.
Существует несколько способов создания и загрузки агента . В этом уроке мы будем использовать подход статической загрузки с использованием метода pre main и опции -javaagent .
3.1. Создание агента Java
Чтобы создать агент Java, нам нужно определить метод premain , которому будет передан экземпляр Instrumentation при загрузке агента . Теперь давайте создадим Список загруженных классов Агента класса:
public class ListLoadedClassesAgent { private static Instrumentation instrumentation; public static void premain(String agentArgs, Instrumentation instrumentation) { ListLoadedClassesAgent.instrumentation = instrumentation; } }
3.2. Определение списка методов загружаемых классов
В дополнение к определению агента мы определим и представим статический метод для возврата массива загруженных классов для данного загрузчика классов.
Обратите внимание, что если мы передаем загрузчик классов со значением null в метод get Initiated Classes , он возвращает классы, загруженные загрузчиком классов начальной загрузки .
Давайте посмотрим на код в действии:
public static Class>[] listLoadedClasses(String classLoaderType) { return instrumentation.getInitiatedClasses( getClassLoader(classLoaderType)); } private static ClassLoader getClassLoader(String classLoaderType) { ClassLoader classLoader = null; switch (classLoaderType) { case "SYSTEM": classLoader = ClassLoader.getSystemClassLoader(); break; case "EXTENSION": classLoader = ClassLoader.getSystemClassLoader().getParent(); break; case "BOOTSTRAP": break; default: break; } return classLoader; }
Обратите внимание, что если мы используем Java 9 или выше, мы можем использовать метод get PlatformClassLoader . Это будет список классов, загруженных загрузчиком классов платформы. В этом случае корпус переключателя также будет содержать:
case "PLATFORM": classLoader = ClassLoader.getPlatformClassLoader(); break;
3.3. Создание файла Манифеста Агента
Теперь давайте создадим файл манифеста MANIFEST.MF с соответствующими атрибутами для запуска нашего агента, включая:
Premain-Class: com.baeldung.loadedclasslisting.ListLoadedClassesAgent
Полный список атрибутов манифеста для файла JAR агента доступен в официальной документации пакета java.lang.instrument .
3.4. Загрузка Агента и запуск приложения
Теперь давайте загрузим агент и запустим приложение. Во-первых, нам нужен файл JAR агента с файлом манифеста, содержащим информацию Premain-Class . Кроме того, нам нужен файл JAR приложения с файлом манифеста, содержащим информацию Main-Class . Класс Launcher , содержащий метод main , запустит наше приложение. Затем мы сможем распечатать классы, загруженные различными типами загрузчиков классов:
public class Launcher { public static void main(String[] args) { printClassesLoadedBy("BOOTSTRAP"); printClassesLoadedBy("SYSTEM"); printClassesLoadedBy("EXTENSION"); } private static void printClassesLoadedBy(String classLoaderType) { System.out.println(classLoaderType + " ClassLoader : "); Class>[] classes = ListLoadedClassesAgent.listLoadedClasses(classLoaderType); Arrays.asList(classes) .forEach(clazz -> System.out.println(clazz.getCanonicalName())); } }
Далее, давайте статически загрузим агент Java и запустим наше приложение:
java -javaagent:agent.jar -jar app.jar
После выполнения приведенной выше команды мы увидим результат:
BOOTSTRAP ClassLoader : java.lang.ClassValue.Entry[] java.util.concurrent.ConcurrentHashMap.Segment java.util.concurrent.ConcurrentHashMap.Segment[] java.util.StringTokenizer .............. SYSTEM ClassLoader : java.lang.Object[] java.lang.Object[][] java.lang.Class java.lang.Class[] .............. EXTENSION ClassLoader : byte[] char[] int[] int[][] short[]
4. Заключение
В этом уроке мы узнали о методе перечисления всех классов, загруженных в определенный загрузчик классов.
Во-первых, мы создали агент Java. После этого мы определили метод для перечисления загруженных классов с помощью Java Instrumentation API. Наконец, мы создали файлы манифеста агента, загрузили агент и запустили наше приложение.
Как всегда, полный исходный код примера можно найти на GitHub .