Благодаря анализу предыдущих статей, мы знаем, что класс прокси -класса генерируется через ProxyClassFactory Factory класса Proxy. Этот фабричный класс будет вызвать метод GenerateProxyClass () класса проксигенератора для генерации байт -кода класса прокси. Класс проксигенератора хранится в пакете Sun.misc. Мы можем найти этот класс через исходный код OpenJDK. Основным содержанием статического метода genedproxyclass () этого класса является вызов метода экземпляра GeneratedClassFile () для генерации файлов класса. Давайте посмотрим на то, что делается в методе GenerateClassFile ().
Private Byte [] generateClassfile () {// Первый шаг - собрать все методы в объекты ProxyMethod // Сначала генерировать методы прокси, такие как ToString, Hashcode, Equals и т. Д. AddProxyMethod (hashcodemethod, object.class); addProxyMethod (equalsmethod, object.class); addProxyMethod (ToStringMethod, Object.Class); // Передача каждый метод каждого интерфейса и генерируйте проксиметод объект для него для (int i = 0; i <интерфейсы. for (int j = 0; j <methods.length; j ++) {addProxyMethod (методы [j], интерфейсы [i]); }} // Для методов прокси с той же подписью проверьте, совместно ли возвращаемое значение метода для (list <proxymethod> подписи: proxymethods.values ()) {checkreturntypes (sigmethods); } // Шаг 2, Собирайте всю информацию о поле и метод информации файла класса, который будет сгенерирован, try {// Добавить метод конструктора метода. // Передача метода прокси в кэше для (list <proxymethod> signmethods: proxymethods.values ()) {for (proxymethod pm: signmethods) {// Добавить статические поля класса прокси, например: частный статический метод M1; fields.add (new FieldInfo (pm.methodfieldname, "ljava/lang/Refert/method;", acc_private | acc_static)); // Добавить прокси -методы методов прокси -класса. }} // Добавить методы инициализации статического поля. Методы. } catch (ioException e) {бросить новый InternalError ("Неожиданное исключение ввода/вывода"); } // Метод проверки и сбор поля не могут быть больше 65535 if (methods.size ()> 65535) {бросить новое allosalargumentException («предел метода превышен»); } if (fields.size ()> 65535) {бросить новое allosalargumentException («Предел поля превышен»); } if (fields.size ()> 65535) {бросить новое allosalargumentException («Предел поля превышен»); } // Шаг 3, запишите в окончательный файл класса // Убедитесь, что есть полностью квалифицированное имя класса прокси в постоянном пуле CP.GetClass (Dottoslash (classname)); // Убедитесь, что есть полностью квалифицированное имя родительского класса Proxy Class в постоянном пуле, а имя родительского класса: «Java/Lang/Refert/Proxy» CP.GetClass (SuperClassName); // Убедитесь, что полное квалифицированное имя интерфейса класса Proxy для (int i = 0; i <интерфейсы. } // рядом с началом написания файла установите постоянный пул для чтения только cp.setreadonly (); BytearRayOutputStream BOUT = новый BytearRayOutputStream (); DataOutputStream dout = new DataOutputStream (BOUT); Попробуйте {// 1. Написать в магический номер dout.writeint (0xcafebabe); // 2. Записать на вторичный номер версии dout.writeshort (classfile_minor_version); // 3. Напишите в основной версию номер dout.writeshort (classfile_major_version); // 4. Написать в постоянный бассейн cp.write (dout); // 5. Модификатор доступа к записи dout.writeshort (acc_public | acc_final | acc_super); // 6. Write class index dout.writeshort (cp.getclass (dottoslash (classname))); // 7. Напишите индекс родительского класса, сгенерированные прокси -классы унаследованы от Proxy dout.writeshort (cp.getClass (SuperClassName)); // 8. Значение количества интерфейсов записи dout.writeshort (interfaces.length); // 9. Набор интерфейса записи для (int i = 0; i <interfaces.length; i ++) {dout.writeShort (cp.getClass (dottoslash (интерфейсы [i] .getName ()))); } // 10. Значение поля записи dout.writeshort (fields.size ()); // 11. Коллекция поля записи для (FieldInfo F: Fields) {F.Write (Dout); } // 12. Значение метода записи dout.writeshort (methods.size ()); // 13. Сбор методов записи для (MethodInfo M: Methods) {M.Write (Dout); } // 14. Значение количества свойств записи, файл класса прокси не имеет атрибутов, поэтому это 0 dout.writeshort (0); } catch (ioException e) {бросить новый InternalError ("Неожиданное исключение ввода/вывода"); } // конвертировать в двоичный массив в вывод возврата bout.tobytearray ();}Вы можете видеть, что метод GenerateClassFile () динамически сплачивается в соответствии со структурой файла класса. Что такое файл класса? Здесь мы сначала объясним, что файл Java, который мы обычно пишем, заканчивается .java. После написания его составьте через компилятор и создайте файл .class. Этот файл .class является файлом класса. Выполнение Java -программ зависит только от файлов класса и не имеет ничего общего с файлами Java. Этот файл класса описывает информацию класса. Когда нам нужно использовать класс, виртуальная машина Java заранее загрузит файл класса этого класса и выполнит инициализацию и соответствующую проверку. Виртуальная машина Java может убедиться, что эти задачи будут выполнены перед использованием этого класса. Нам просто нужно использовать его с душевным спокойствием, не заботясь о том, как ее загружает виртуальная машина Java. Конечно, файлы классов не обязательно должны быть скомпилированы путем компиляции файлов Java. Вы даже можете написать файлы классов непосредственно через текстовый редактор. Здесь динамический прокси JDK динамически генерирует файлы классов с помощью программ. Давайте снова вернемся к вышеуказанному коду и увидим, что генерирование файла класса в основном разделено на три шага:
Шаг 1: Соберите все прокси -методы, которые будут сгенерированы, оберните их в объекты проксиметода и зарегистрируйте их в коллекцию карт.
Шаг 2: Соберите всю информацию о поле и информацию о методе, которые будут сгенерированы для файла класса.
Шаг 3: После завершения вышеуказанной работы начните сборку файла класса.
Мы знаем, что основной частью класса являются его поля и методы. Давайте сосредоточимся на втором шаге, чтобы увидеть, какие поля и методы он генерирует для класса прокси. На втором этапе следующие четыре вещи были сделаны по порядку.
1. Сгенерируйте конструктор параметров для класса прокси, передайте ссылку на экземпляр InvocationHandler и вызовите конструктор параметров родительского класса.
2. Итерация над сбором карты прокси -методов, генерируйте соответствующий статический домен типа метода для каждого метода прокси и добавьте его в сбору полей.
3. Итерация над сбором карты прокси -методов, генерируйте соответствующий объект MethodInfo для каждого прокси -метода и добавьте его в сбор методов.
4. Сгенерируйте метод статической инициализации для класса прокси. Этот метод статической инициализации в основном присваивает ссылку каждого прокси -метода на соответствующее статическое поле.
Благодаря вышеуказанному анализу мы можем приблизительно знать, что динамический прокси JDK в конечном итоге генерирует класс прокси со следующей структурой для нас:
Public Class Proxy0 Extends Proxy реализует userdao {// step 1, генерировать конструктор Proxy0 (vococationHandler h) {super (h); } // Шаг 2, генерируйте статический домен частный статический метод M1; // метод HashCode Private Static Method M2; // равняется методу частный статический метод M3; // метод ToString Private Static Method M4; // ... // Шаг 3, генерируйте прокси -метод @Override public int hashcode () {try {return (int) h.invoke (this, m1, null); } catch (throwable e) {бросить новый UndeclaredThableException (e); }} @Override public boolean equals (Object obj) {try {object [] args = new Object [] {obj}; return (boolean) h.invoke (this, m2, args); } catch (throwable e) {бросить новый UndeclaredThableException (e); }} @Override public String toString () {try {return (string) h.invoke (this, m3, null); } catch (throwable e) {бросить новый UndeclaredThableException (e); }} @Override public void said (пользователь пользователь) {try {// Создать массив параметров, если есть несколько параметров, добавленных позже, просто объект [] args = new Object [] {user}; H.invoke (это, M4, Args); } catch (throwable e) {бросить новый UndeclaredThableException (e); }} // Шаг 4, генерируйте метод статической инициализации static {try {class c1 = class.forname (object.class.getname ()); Класс C2 = class.forname (userDao.class.getName ()); m1 = c1.getmethod ("hashcode", null); m2 = c1.getmethod ("equals", new class [] {object.class}); m3 = c1.getmethod ("tostring", null); m4 = c2.getMethod ("Сохранить", новый класс [] {user.class}); // ...} catch (Exception e) {e.printstackTrace (); }}}На этом этапе, после многоуровневого анализа и углубленного исследования исходного кода JDK, мы восстановили исходный внешний вид динамически сгенерированного класса прокси, и некоторые из предыдущих вопросов также были хорошо объяснены.
1. Прокси -класс наследует класс Porxy по умолчанию. Поскольку Java поддерживает только единичное наследование, Dynamic Dynamic Proxy JDK может реализовать только интерфейсы.
2. Методы прокси назовут метод invoke () vocationHandler, поэтому нам нужно переписать метод invoke () inpocationHandler.
3. При вызове метода Invoke () сам экземпляр прокси, целевой метод и параметры целевого метода будут переданы. Объясните, как поступают параметры метода invoke ().
Используйте вновь построенный Proxy0 в качестве класса прокси, чтобы снова протестировать, и вы можете увидеть, что конечный результат такой же, как и класс проксина, динамически сгенерированный с использованием JDK. Еще раз, наш анализ является надежным и точным. На данный момент были объявлены статьи серии Dynamic Dynamic Proxy. Благодаря анализу этой серии автор решил давние сомнения в своем сердце, и я считаю, что понимание читателей динамического прокси-сервера JDK стало на один шаг дальше. Тем не менее, знания на бумаге всегда мелкие. Если вы хотите лучше освоить технологию динамического прокси -сервера JDK, читатели могут ссылаться на эту серию статей, чтобы самостоятельно проверить исходный код JDK или обмен опытом обучения с автором, указать на неуместный анализ автора, учиться вместе и продвигать прогресс вместе.
Выше всего содержание этой статьи. Я надеюсь, что это будет полезно для каждого обучения, и я надеюсь, что все будут поддерживать Wulin.com больше.