portaldacalheta.pt
  • Основен
  • Kpi И Анализ
  • Инвеститори И Финансиране
  • Други
  • Финансови Процеси
Уеб Интерфейс

Урок за напреднали Java Class: Ръководство за презареждане на клас



В Проекти за разработка на Java , типичният работен процес включва рестартиране на сървъра с всяка промяна на класа и никой не се оплаква от това. Това е факт за разработката на Java. Ние работим така от първия ни ден с Java. Но дали презареждането на Java клас е толкова трудно за постигане? И може ли този проблем да бъде едновременно предизвикателен и вълнуващ за решаване опитни разработчици на Java ? В този урок за Java клас ще се опитам да се справя с проблема, да ви помогна да се възползвате от всички предимства на презареждането на клас в движение и да повишите вашата производителност неимоверно.

Презареждането на Java клас не се обсъжда често и има много малко документация, изследваща този процес. Тук съм, за да променя това. Този урок за уроци по Java ще предостави стъпка по стъпка обяснение на този процес и ще ви помогне да овладеете тази невероятна техника. Имайте предвид, че внедряването на презареждане на Java клас изисква много внимание, но научаването как да го направите ще ви постави в големите лиги, както като разработчик на Java, така и като софтуерен архитект. Също така няма да навреди да се разбере как да избегнем 10-те най-често срещани грешки в Java .



Настройка на работното пространство

Целият изходен код за този урок се качва на GitHub тук .



За да стартирате кода, докато следвате този урок, ще ви трябва Мейвън , Отивам и или Затъмнение или IntelliJ IDEA .



Ако използвате Eclipse:

  • Изпълнете командата mvn eclipse:eclipse за генериране на проектни файлове на Eclipse.
  • Заредете генерирания проект.
  • Задайте изходния път на target/classes.

Ако използвате IntelliJ:

  • Импортиране на проекта pom файл.
  • IntelliJ няма да се компилира автоматично, когато използвате какъвто и да е пример, така че трябва да:
  • Изпълнете примерите в IntelliJ, след това всеки път, когато искате да компилирате, ще трябва да натискате Alt+B E
  • Изпълнете примерите извън IntelliJ с run_example*.bat. Задайте автоматично компилиране на компилатора на IntelliJ на вярно. След това, всеки път, когато промените който и да е java файл, IntelliJ ще го компилира автоматично.

Пример 1: Презареждане на клас с Java Class Loader

Първият пример ще ви даде общо разбиране за Java loader товара. Ето изходния код.

Като се има предвид следното User определение на класа:



public static class User { public static int age = 10; }

Можем да направим следното:

public static void main(String[] args) { Class userClass1 = User.class; Class userClass2 = new DynamicClassLoader('target/classes') .load('qj.blog.classreloading.example1.StaticInt$User'); ...

В този пример на урок ще има две User класове, заредени в паметта. userClass1 ще се зареди от зареждащия по подразбиране клас на JVM и userClass2 използвайки DynamicClassLoader, потребителски зареждащ клас, чийто изходен код също е предоставен в проекта GitHub и който ще опиша подробно по-долу.



Ето и останалата част от main метод:

out.println('Seems to be the same class:'); out.println(userClass1.getName()); out.println(userClass2.getName()); out.println(); out.println('But why there are 2 different class loaders:'); out.println(userClass1.getClassLoader()); out.println(userClass2.getClassLoader()); out.println(); User.age = 11; out.println('And different age values:'); out.println((int) ReflectUtil.getStaticFieldValue('age', userClass1)); out.println((int) ReflectUtil.getStaticFieldValue('age', userClass2)); }

И изходът:



Seems to be the same class: qj.blog.classreloading.example1.StaticInt$User qj.blog.classreloading.example1.StaticInt$User But why there are 2 different class loaders: [email protected] [email protected] And different age values: 11 10

Както можете да видите тук, въпреки че User класовете имат едно и също име, те всъщност са два различни класа и те могат да бъдат управлявани и манипулирани независимо. Стойността на възрастта, макар и декларирана като статична, съществува в две версии, прикрепена отделно към всеки клас и може да бъде променяна и независимо.

В нормална Java програма, ClassLoader е порталът, въвеждащ класове в JVM. Когато един клас изисква друг клас да бъде зареден, задачата ClassLoader е да извърши зареждането.



Бутонът на Twitter не се показва като стилизиран

В този пример за Java клас обаче потребителският ClassLoader с име DynamicClassLoader се използва за зареждане на втората версия на User клас. Ако вместо DynamicClassLoader, трябваше отново да използваме зареждащия клас по подразбиране (с командата StaticInt.class.getClassLoader()), тогава същото User клас ще се използва, тъй като всички заредени класове се кешират.

Изследването на начина, по който Java ClassLoader по подразбиране работи спрямо DynamicClassLoader, е от ключово значение за извличането на полза от този урок за Java класове.



DynamicClassLoader

В нормална програма на Java може да има няколко зареждащи класа. Този, който зарежда вашия основен клас, ClassLoader, е този по подразбиране и от вашия код можете да създавате и използвате толкова много зареждащи класа, колкото искате. Това е ключът към презареждането на класа в Java. DynamicClassLoader е може би най-важната част от целия урок, така че трябва да разберем как работи динамичното зареждане на класа, преди да можем да постигнем целта си.

За разлика от поведението по подразбиране на ClassLoader, нашите DynamicClassLoader наследява по-агресивна стратегия. Нормален loadloader ще даде на родителя си ClassLoader приоритетните и само класове зареждане, които родителят му не може да зареди. Това е подходящо за нормални обстоятелства, но не и в нашия случай. Вместо това, DynamicClassLoader ще се опита да прегледа всички пътища на неговия клас и да разреши целевия клас, преди той да се откаже от правото на своя родител.

В нашия пример по-горе, DynamicClassLoader се създава само с един път на класа: 'target/classes' (в текущата ни директория), така че е в състояние да зареди всички класове, които се намират на това място. За всички класове, които не са там, той ще трябва да се позовава на родителския loadloader. Например трябва да заредим String клас в нашия StaticInt клас, а нашият клас за зареждане няма достъп до rt.jar в нашата папка JRE, така че String клас на зареждащия клас родител ще бъде използван.

Следният код е от AggressiveClassLoader , родителският клас на DynamicClassLoader и показва къде е дефинирано това поведение.

byte[] newClassData = loadNewClass(name); if (newClassData != null) { loadedClasses.add(name); return loadClass(newClassData, name); } else { unavaiClasses.add(name); return parent.loadClass(name); }

Обърнете внимание на следните свойства на DynamicClassLoader:

  • Заредените класове имат същата производителност и други атрибути като другите класове, заредени от зареждащия клас по подразбиране.
  • DynamicClassLoader може да се събира боклук заедно с всичките му заредени класове и обекти.

С възможността да заредим и използваме две версии от един и същи клас, сега мислим да изхвърлим старата версия и да заредим новата, за да я заменим. В следващия пример ще правим точно това ... непрекъснато.

Пример 2: Непрекъснато презареждане на клас

Този следващ пример за Java ще ви покаже, че JRE може да зарежда и презарежда класове завинаги, като старите класове се изхвърлят и се събират боклуци, и чисто нов клас се зарежда от твърдия диск и се използва. Ето изходния код.

Ето основния цикъл:

public static void main(String[] args) { for (;;) { Class userClass = new DynamicClassLoader('target/classes') .load('qj.blog.classreloading.example2.ReloadingContinuously$User'); ReflectUtil.invokeStatic('hobby', userClass); ThreadUtil.sleep(2000); } }

На всеки две секунди, старата User клас ще бъде изхвърлен, ще бъде зареден нов и неговият метод hobby призовани.

Тук е User определение на класа:

как да проверите дали имате изтичане на памет
@SuppressWarnings('UnusedDeclaration') public static class User { public static void hobby() { playFootball(); // will comment during runtime // playBasketball(); // will uncomment during runtime } // will comment during runtime public static void playFootball() { System.out.println('Play Football'); } // will uncomment during runtime // public static void playBasketball() { // System.out.println('Play Basketball'); // } }

Когато стартирате това приложение, трябва да се опитате да коментирате и да коментирате кода, посочен в User клас. Ще видите, че винаги ще се използва най-новата дефиниция.

Ето някои примерни резултати:

... Play Football Play Football Play Football Play Basketball Play Basketball Play Basketball

Всеки път, когато се появи нов екземпляр на DynamicClassLoader е създаден, той ще зареди User клас от target/classes папка, където сме настроили Eclipse или IntelliJ да извеждат най-новия клас файл. Всички стари DynamicClassLoader и стари User класовете ще бъдат прекратени и ще бъдат подложени на сметосъбирача.

От решаващо значение е напредналите разработчици на Java да разбират динамично презареждане на клас, независимо дали е активно или несвързано.

Ако сте запознати с JVM HotSpot, тук трябва да се отбележи, че структурата на класовете също може да бъде променена и презаредена: playFootball методът трябва да бъде премахнат и playBasketball добавен метод. Това е различно от HotSpot, което позволява да се променя само съдържанието на метода или класът не може да се презареди.

Сега, когато сме в състояние да презаредим клас, е време да опитаме да презаредим много класове наведнъж. Нека изпробваме в следващия пример.

Пример 3: Презареждане на множество класове

Резултатът от този пример ще бъде същият с пример 2, но ще покаже как да се приложи това поведение в по-подобна на приложение структура с обекти на контекст, услуга и модел. Изходният код на този пример е доста голям, така че тук съм показал само части от него. Пълният изходен код е тук .

пенсионни общности с нестопанска цел

Ето това е main метод:

public static void main(String[] args) { for (;;) { Object context = createContext(); invokeHobbyService(context); ThreadUtil.sleep(2000); } }

И методът createContext:

private static Object createContext() { Class contextClass = new DynamicClassLoader('target/classes') .load('qj.blog.classreloading.example3.ContextReloading$Context'); Object context = newInstance(contextClass); invoke('init', context); return context; }

Методът invokeHobbyService:

private static void invokeHobbyService(Object context) { Object hobbyService = getFieldValue('hobbyService', context); invoke('hobby', hobbyService); }

И тук е Context клас:

public static class Context { public HobbyService hobbyService = new HobbyService(); public void init() { // Init your services here hobbyService.user = new User(); } }

И HobbyService клас:

public static class HobbyService { public User user; public void hobby() { user.hobby(); } }

Context клас в този пример е много по-сложен от User клас в предишните примери: има връзки към други класове и има init метод, който да бъде извикан всеки, когато е създаден. По принцип е много подобен на контекстните класове на приложението в реалния свят (който проследява модулите на приложението и прави инжектиране на зависимост). Така че може да презаредите това Context клас заедно с всички свързани класове е чудесна стъпка към прилагането на тази техника в реалния живот.

Презареждането на Java клас е трудно дори за напреднали Java инженери.

С нарастването на броя на класовете и обектите, нашата стъпка за „пускане на стари версии“ ​​също ще се усложни. Това е и най-голямата причина, поради която презареждането на класа е толкова трудно. За евентуално отпадане на стари версии ще трябва да се уверим, че след като бъде създаден новият контекст, всичко препратките към старите класове и обекти отпадат. Как да се справим елегантно с това?

main метод тук ще има задържане на обекта контекст и това е единствената връзка към всички неща, които трябва да отпаднат. Ако прекъснем тази връзка, обектът на контекста и класът на контекста и обектът на услугата ... ще бъдат подложени на събирача на боклук.

Малко обяснение защо нормално класовете са толкова упорити и не събират боклука:

  • Обикновено зареждаме всички наши класове в Java по подразбиране за зареждане на класове.
  • Взаимоотношението клас-клас е двупосочна връзка, като зареждачът на класове също кешира всички класове, които е заредил.
  • Така че докато loadloaderът все още е свързан с която и да е жива нишка, всичко (всички заредени класове) ще бъде имунизирано срещу събирача на боклук.
  • Това каза, освен ако не можем да отделим кода, който искаме да презаредим, от кода, който вече е зареден от зареждащия клас по подразбиране, новите ни промени в кода никога няма да бъдат приложени по време на изпълнение.

С този пример виждаме, че презареждането на всички класове на приложения всъщност е доста лесно. Целта е просто да се запази тънка връзка с възможност за пускане от активната нишка към използвания динамичен зареждащ клас. Но какво, ако искаме някои обекти (и техните класове) да не да се презареди и да се използва повторно между циклите на презареждане? Нека разгледаме следващия пример.

Пример 4: Разделяне на постоянни и презаредени пространства на клас

Ето изходния код. .

main метод:

public static void main(String[] args) { ConnectionPool pool = new ConnectionPool(); for (;;) { Object context = createContext(pool); invokeService(context); ThreadUtil.sleep(2000); } }

Така че можете да видите, че трикът тук е зареждането на ConnectionPool клас и да го създаде екземпляр извън цикъла на презареждане, като го запази в постоянното пространство и предаде препратката към Context обекти

createContext методът също е малко по-различен:

private static Object createContext(ConnectionPool pool) { ExceptingClassLoader classLoader = new ExceptingClassLoader( (className) -> className.contains('.crossing.'), 'target/classes'); Class contextClass = classLoader.load('qj.blog.classreloading.example4.reloadable.Context'); Object context = newInstance(contextClass); setFieldValue(pool, 'pool', context); invoke('init', context); return context; }

Отсега нататък ще наричаме обектите и класовете, които се презареждат с всеки цикъл, „презареждаемото пространство“, а други - обектите и класовете, които не са рециклирани и не се подновяват по време на циклите на презареждане - „постоянно пространство“. Ще трябва да сме много ясни кои обекти или класове остават в кое пространство, като по този начин очертаваме разделителна линия между тези две пространства.

Ако не се работи правилно, това разделяне на зареждането на Java клас може да доведе до неуспех.

Както се вижда от снимката, не само Context обект и UserService обект, отнасящ се до ConnectionPool обект, но Context и UserService класовете също се отнасят до ConnectionPool клас. Това е много опасна ситуация, която често води до объркване и провал. ConnectionPool класът не трябва да се зарежда от нашия DynamicClassLoader, трябва да има само един ConnectionPool клас в паметта, който е този, зареден по подразбиране ClassLoader. Това е един пример за това защо е толкова важно да бъдете внимателни при проектирането на архитектура за презареждане на клас в Java.

Ами ако нашите DynamicClassLoader случайно зарежда ConnectionPool клас? Тогава ConnectionPool обект от постоянното пространство не може да бъде предаден на Context обект, защото Context object очаква обект от различен клас, който също е наречен ConnectionPool, но всъщност е различен клас!

как да проектирам API

И така, как да предотвратим нашите DynamicClassLoader от зареждането на ConnectionPool клас? Вместо да използва DynamicClassLoader, този пример използва подклас от него, наречен: ExceptingClassLoader, който ще предаде зареждането на super classloader въз основа на функция за състояние:

(className) -> className.contains('$Connection')

Ако не използваме ExceptingClassLoader тук, тогава DynamicClassLoader би заредил ConnectionPool клас, защото този клас се намира в „target/classes“ папка. Друг начин за предотвратяване на ConnectionPool клас, вдигнат от нашия DynamicClassLoader е да се компилира ConnectionPool клас в различна папка, може би в различен модул, и тя ще бъде компилирана отделно.

Правила за избор на пространство

Сега заданието за зареждане на Java клас става наистина объркващо. Как да определим кои класове трябва да са в постоянното пространство и кои класове в презареждаемото пространство? Ето правилата:

  1. Клас в презареждаемото пространство може да се позовава на клас в постоянното пространство, но на клас в постоянното пространство може никога препратка към клас в презареждаемото пространство. В предишния пример презареждаемият Context клас се позовава на постоянния ConnectionPool клас, но ConnectionPool няма препратка към Context
  2. Клас може да съществува във всяко пространство, ако не препраща към нито един клас в другото пространство. Например клас на помощна програма с всички статични методи като StringUtils може да се зареди веднъж в постоянното пространство и да се зареди отделно в презареждаемото пространство.

Така че можете да видите, че правилата не са много ограничителни. С изключение на пресичащите класове, които имат обекти, посочени в двете пространства, всички останали класове могат да се използват свободно или в постоянното пространство, или в презареждаемото пространство, или и в двата. Разбира се, само класовете в презареждаемото пространство ще се радват да бъдат презареждани с цикли за презареждане.

Така че е решен най-трудният проблем с презареждането на класа. В следващия пример ще се опитаме да приложим тази техника към просто уеб приложение и ще се насладим на презареждане на класове Java точно като всеки скриптов език.

настройка на производителността в sql server 2012 стъпка по стъпка

Пример 5: Малък телефонен указател

Ето изходния код. .

Този пример ще бъде много подобен на това как трябва да изглежда нормално уеб приложение. Това е приложение с една страница с AngularJS, SQLite, Maven и Вграден уеб сървър на Jetty .

Ето мястото за презареждане в структурата на уеб сървъра:

Цялостното разбиране на презареждаемото пространство в структурата на уеб сървъра ще ви помогне да овладеете зареждането на Java клас.

Уеб сървърът няма да съдържа препратки към реалните сървлети, които трябва да останат в презареждаемото пространство, за да бъдат презаредени. Това, което държи, са сърблети за заглушаване, които при всяко извикване на неговия метод на обслужване ще разрешат действителния сървлет в действителния контекст да се изпълни.

Този пример също въвежда нов обект ReloadingWebContext, който предоставя на уеб сървъра всички стойности като нормален контекст, но вътрешно съдържа препратки към действителен контекстен обект, който може да бъде презареден от DynamicClassLoader. Това е това ReloadingWebContext които предоставят сърблети за заглушаване на уеб сървъра.

ReloadingWebContext обработва сърблети за заглушаване на уеб сървъра в процеса на презареждане на класа Java.

ReloadingWebContext ще бъде обвивката на действителния контекст и:

  • Ще презареди действителния контекст, когато се извика HTTP GET до „/“.
  • Ще предоставя сърблети за заглушаване на уеб сървъра.
  • Ще задава стойности и ще извиква методи всеки път, когато действителният контекст се инициализира или унищожи.
  • Може да бъде конфигуриран да презареди контекста или не и какъв loadloader се използва за презареждане. Това ще помогне при стартиране на приложението в производствения процес.

Тъй като е много важно да разберем как изолираме постоянното пространство и презареждаемото пространство, ето двата класа, които се пресичат между двете пространства:

Клас qj.util.funct.F0 за обект public F0 connF в Context

  • Функционалният обект ще връща връзка при всяко извикване на функцията. Този клас се намира в пакета qj.util, който е изключен от DynamicClassLoader.

Клас java.sql.Connection за обект public F0 connF в Context

  • Нормален обект на SQL връзка. Този клас не се намира в пътя на нашия DynamicClassLoader клас, така че няма да бъде взет.

Обобщение

В този урок за Java класове видяхме как да презаредим един клас, да презаредим непрекъснато един клас, да презаредим цяло пространство от множество класове и да презаредим множество класове отделно от класовете, които трябва да се запазят. С тези инструменти ключовият фактор за постигане на надеждно презареждане на класа е да има супер изчистен дизайн. След това можете свободно да манипулирате класовете си и цялата JVM.

Внедряването на презареждане на Java клас не е най-лесното нещо в света. Но ако опитате и в даден момент откриете, че класовете ви се зареждат в движение, значи вече сте почти там. Ще остане много малко да се направи, преди да можете да постигнете напълно превъзходен изчистен дизайн на вашата система.

Успех мои приятели и се насладете на вашата новооткрита суперсила!

Наближаващата революция на търговския робот

Финансови Процеси

Наближаващата революция на търговския робот
Пиксели на влияние - разбиване на убедителните принципи на дизайна

Пиксели на влияние - разбиване на убедителните принципи на дизайна

Ux Дизайн

Популярни Публикации
Първи стъпки с модули и модулна разработка отпред
Първи стъпки с модули и модулна разработка отпред
Най-добри практики за уеб оформление: Анализирани са 12 вечни модела на потребителския интерфейс
Най-добри практики за уеб оформление: Анализирани са 12 вечни модела на потребителския интерфейс
H-1B: Пътешествие на разработчика на iOS от Хондурас до Силициевата долина
H-1B: Пътешествие на разработчика на iOS от Хондурас до Силициевата долина
Agile UX: Как да включите UX и продуктовия дизайн в Agile
Agile UX: Как да включите UX и продуктовия дизайн в Agile
Съвети за привличане, управление и задържане на разработчици на софтуер
Съвети за привличане, управление и задържане на разработчици на софтуер
 
Въведение в търговията с дълбоко обучение в хедж фондове
Въведение в търговията с дълбоко обучение в хедж фондове
Magento 2: Ревизия или революция?
Magento 2: Ревизия или революция?
Кутия с инструменти на Forecaster’s: Как да извършите симулации на Монте Карло
Кутия с инструменти на Forecaster’s: Как да извършите симулации на Монте Карло
Ръководител на растежа
Ръководител на растежа
Вицепрезидент по комуникациите
Вицепрезидент по комуникациите
Популярни Публикации
  • как да стартирам raspberry pi
  • как да надстроя до python 3
  • фонд за търсене срещу частен капитал
  • цветовете и тяхното влияние върху настроението
  • използвайте селен за изстъргване на данни
  • как да напиша медийна заявка
  • какъв е езикът за програмиране c
Категории
  • Kpi И Анализ
  • Инвеститори И Финансиране
  • Други
  • Финансови Процеси
  • © 2022 | Всички Права Запазени

    portaldacalheta.pt