Связанное чтение:
Основа JAVA (1) Простое и полное понимание внутренних классов и статических внутренних классов.
Основы JAVA (2) Оптимизация памяти — использование ссылок Java для кэша
Основы JAVA (4) Перечисление (enum) и определение констант, сравнение фабричных классов
Основы JAVA (5) функциональный интерфейс - повторное использование, острая грань разделения
Идеи программирования JAVA (1) Повышение масштабируемости за счет внедрения зависимостей
Идеи программирования JAVA (2) как программировать интерфейс
Идеи программирования на языке JAVA (3) Удалите неуклюжее «если», и режим стратегии саморегистрации элегантно удовлетворяет принципу «открыто-закрыто»
Идеи программирования JAVA (4) Как выбрать классическую парадигму режима Builder и режима factory?
Исходный код HikariPool (2) Идеи дизайна для справки
Люди на рабочем месте (1) Правила выживания для крупных ИТ-компаний
1. Сценарии применения
- Исправьте ошибки без перезапуска службы и динамически загружайте измененные классы ошибок.
- Динамическое обновление, в системе Android вы можете обойти стратегию обновления рынка приложений, динамически загружая APK, и самостоятельно настроить стратегию обновления.
2. Примеры
В интернете есть много статей, описывающих загрузку ClassLoader, которые здесь подробно описывать не будем.Следует отметить, что: классы, которые нужно динамически подгружать, вынести в отдельный jar-файл, и загружать их динамической загрузкой с самого начала , а не в банке основного процесса.пакета, он будет загружен загрузчиком по умолчанию и не будет перезагружаться после обновления.
2.1. Основной код/модуль проекта
Этот модуль помещает классы, которые не нужно динамически загружать.
2.1.1. HotClassLoader.java
Он используется для реализации функции динамической записи.
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
// 热加载器
public class HotClassLoader {
private static final long LOADER_INTERVAL = 3;
// 指向动态加载module的jar文件
private static final String HOT_UPDATE_JAR_PATH = "D:\\ClassLoaderDemo-Service\\target\\ClassLoaderDemo-Service-1.0-SNAPSHOT.jar";
static URLClassLoader classLoader; //类加载器
private static long lastModifiedTime = 0; // jar文件最后更新时间
// 开始监听jar文件是否有更新
public void startListening() {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(()->{
if (isHotUpdate()) {
reload();
}
}, 0, LOADER_INTERVAL, TimeUnit.SECONDS);
}
// 动态获取新实例,注意返回值可能为null,调用者要加判断
public static Object newInstance(String className) {
if (classLoader != null) {
try {
synchronized (HotClassLoader.class) {
Object newInstance = Class.forName(className, true, classLoader).newInstance();
return newInstance;
}
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
return null;
}
// 判断是否有更新
private boolean isHotUpdate() {
File hotLoaderFile = new File(HOT_UPDATE_JAR_PATH);
boolean isHotUpdate = false;
if (hotLoaderFile.exists()) {
long newModifiedTime = hotLoaderFile.lastModified();
isHotUpdate = lastModifiedTime != newModifiedTime;
lastModifiedTime = newModifiedTime;
} else {
System.out.println(hotLoaderFile.getAbsolutePath() + " is not found.");
}
System.out.println("isHotUpdate:" + isHotUpdate);
return isHotUpdate;
}
// 重新加载jar文件
private void reload() {
File jarPath = new File(HOT_UPDATE_JAR_PATH);
System.out.println("jar lastModified xxxxxxxxxxxxxxxxxx: " + jarPath.lastModified());
if (jarPath.exists()) {
try {
synchronized (HotClassLoader.class) {
classLoader = new URLClassLoader(new URL[]{jarPath.toURI().toURL()});
}
} catch (Exception e) {
e.printStackTrace();
}
} else {
System.out.println("Hot update jar is not found.");
}
}
}
2.1.2. Service.java
Макет динамически загружаемого интерфейса класса.
package com.javageektour.classloaderdemo;
public interface Service {
void printVersion();
}
2.1.3. HotLoadTest.java
тестовый класс.
package com.javageektour.classloaderdemo;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class HotLoadTest {
public static void main(String[] args) {
HotClassLoader hotClassLoader = new HotClassLoader();
hotClassLoader.startListening();
// 休眠一会,等加载完
sleep(3000);
mockCaller();
sleep(50000000);
}
// 模拟调用者
private static void mockCaller() {
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(()->{
try {
Service mockService = (Service) HotClassLoader.newInstance("com.javageektour.classloaderdemo.MockService");
mockService.printVersion();
} catch (Exception e) {
e.printStackTrace();
}
}, 0, 5, TimeUnit.SECONDS);
}
private static void sleep(long timeMillis) {
try {
Thread.sleep(timeMillis);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
2.2 Динамическая загрузка проектов/модулей
Классы, которые необходимо динамически загружать, помещаются в этот модуль, и даже несколько модулей могут быть разделены в зависимости от бизнеса.
Обратите внимание, что этому модулю необходимо скомпилировать классы интерфейса, которые зависят от основного модуля.
2.2.1. MockService.java
package com.javageektour.classloaderdemo;
public class MockService implements Service {
@Override
public void printVersion() {
System.out.println("11.0");
}
}
2.3. Процесс проверки
1. Скомпилируйте динамически загружаемый модуль, сгенерируйте файл jar и измените путь к файлу jar тестовой программы в основном модуле.
2. Запустите тестовую демонстрацию.После печати номера версии измените номер версии в MockService.java, чтобы повторно сгенерировать файл jar.
3. Подождите некоторое время, чтобы распечатать новый номер версии.
Выходной журнал:
isHotUpdate:true
jar lastModified xxxxxxxxxxxxxxxxxx: 1587832288706
isHotUpdate:false
1.0
isHotUpdate:false
1.0
isHotUpdate:false
isHotUpdate:true
jar lastModified xxxxxxxxxxxxxxxxxx: 1587832303617
2.0
isHotUpdate:false
3. Резюме
- ClassLoader — это базовая точка знаний о Java, которую следует в целом понять и освоить.
- В некоторых сценариях, когда требования SLA высоки и служба не может быть остановлена, исправление с помощью горячей перезагрузки также является решением, но планирование и проектирование должны быть выполнены заранее.
- Логика динамической загрузки apk для Android на самом деле довольно сложна, она должна учитывать ряд операций, таких как загрузка, установка, загрузка, анализ управления и т. д. Горячая загрузка — это всего лишь небольшой шаг.
.end