PowerJob的OhMyClassLoader工作流程源碼解讀
序
本文主要研究一下PowerJob的OhMyClassLoader
OhMyClassLoader
tech/powerjob/worker/container/OhMyClassLoader.java
@Slf4j public class OhMyClassLoader extends URLClassLoader { public OhMyClassLoader(URL[] urls, ClassLoader parent) { super(urls, parent); } /** * 主動加載類,否則類加載器內(nèi)空空如也,Spring IOC容器初始化不到任何東西 * @param packageName 包路徑,主動加載用戶寫的類 * @throws Exception 加載異常 */ public void load(String packageName) throws Exception { URL[] urLs = getURLs(); for (URL jarURL : urLs) { JarFile jarFile = new JarFile(new File(jarURL.toURI())); Enumeration<JarEntry> entries = jarFile.entries(); while (entries.hasMoreElements()) { JarEntry jarEntry = entries.nextElement(); if (jarEntry.isDirectory()) { continue; } String name = jarEntry.getName(); if (!name.endsWith(".class")) { continue; } // 轉(zhuǎn)換 org/spring/AAA.class -> org.spring.AAA String tmp = name.substring(0, name.length() - 6); String res = StringUtils.replace(tmp, "/", "."); if (res.startsWith(packageName)) { loadClass(res); log.info("[OhMyClassLoader] load class({}) successfully.", res); } } } } }
OhMyClassLoader繼承了URLClassLoader,它定義了load方法,遍歷urls,挨個根據(jù)url創(chuàng)建JarFile,然后遍歷jarFile.entries(),找到.class結(jié)尾的entry,判斷是否是packageName開頭的,是則執(zhí)行父類的loadClass方法
OmsJarContainer
tech/powerjob/worker/container/OmsJarContainer.java
@Slf4j public class OmsJarContainer implements OmsContainer { private final Long containerId; private final String name; private final String version; private final File localJarFile; private final Long deployedTime; // 引用計數(shù)器 private final AtomicInteger referenceCount = new AtomicInteger(0); private OhMyClassLoader containerClassLoader; private ClassPathXmlApplicationContext container; private final Map<String, BasicProcessor> processorCache = Maps.newConcurrentMap(); public OmsJarContainer(Long containerId, String name, String version, File localJarFile) { this.containerId = containerId; this.name = name; this.version = version; this.localJarFile = localJarFile; this.deployedTime = System.currentTimeMillis(); } @Override public void init() throws Exception { log.info("[OmsJarContainer-{}] start to init container(name={},jarPath={})", containerId, name, localJarFile.getPath()); URL jarURL = localJarFile.toURI().toURL(); // 創(chuàng)建類加載器(父類加載為 Worker 的類加載) this.containerClassLoader = new OhMyClassLoader(new URL[]{jarURL}, this.getClass().getClassLoader()); // 解析 Properties Properties properties = new Properties(); try (InputStream propertiesURLStream = containerClassLoader.getResourceAsStream(ContainerConstant.CONTAINER_PROPERTIES_FILE_NAME)) { if (propertiesURLStream == null) { log.error("[OmsJarContainer-{}] can't find {} in jar {}.", containerId, ContainerConstant.CONTAINER_PROPERTIES_FILE_NAME, localJarFile.getPath()); throw new PowerJobException("invalid jar file because of no " + ContainerConstant.CONTAINER_PROPERTIES_FILE_NAME); } properties.load(propertiesURLStream); log.info("[OmsJarContainer-{}] load container properties successfully: {}", containerId, properties); } String packageName = properties.getProperty(ContainerConstant.CONTAINER_PACKAGE_NAME_KEY); if (StringUtils.isEmpty(packageName)) { log.error("[OmsJarContainer-{}] get package name failed, developer should't modify the properties file!", containerId); throw new PowerJobException("invalid jar file"); } // 加載用戶類 containerClassLoader.load(packageName); // 創(chuàng)建 Spring IOC 容器(Spring配置文件需要填相對路徑) // 需要切換線程上下文類加載器以加載 JDBC 類驅(qū)動(SPI) ClassLoader oldCL = Thread.currentThread().getContextClassLoader(); Thread.currentThread().setContextClassLoader(containerClassLoader); try { this.container = new ClassPathXmlApplicationContext(new String[]{ContainerConstant.SPRING_CONTEXT_FILE_NAME}, false); this.container.setClassLoader(containerClassLoader); this.container.refresh(); }finally { Thread.currentThread().setContextClassLoader(oldCL); } log.info("[OmsJarContainer-{}] init container(name={},jarPath={}) successfully", containerId, name, localJarFile.getPath()); } //...... }
OmsJarContainer的init方法會使用構(gòu)造器傳入的localJarFile作為url創(chuàng)建OhMyClassLoader,然后讀取oms-worker-container.properties,以其中的PACKAGE_NAME屬性作為packageName,然后使用containerClassLoader.load進行加載
deployContainer
tech/powerjob/worker/container/OmsContainerFactory.java
public static synchronized void deployContainer(ServerDeployContainerRequest request) { Long containerId = request.getContainerId(); String containerName = request.getContainerName(); String version = request.getVersion(); log.info("[OmsContainer-{}] start to deploy container(name={},version={},downloadUrl={})", containerId, containerName, version, request.getDownloadURL()); OmsContainer oldContainer = CARGO.get(containerId); if (oldContainer != null && version.equals(oldContainer.getVersion())) { log.info("[OmsContainer-{}] version={} already deployed, so skip this deploy task.", containerId, version); return; } String filePath = CONTAINER_DIR + containerId + "/" + version + ".jar"; // 下載Container到本地 File jarFile = new File(filePath); try { if (!jarFile.exists()) { FileUtils.forceMkdirParent(jarFile); FileUtils.copyURLToFile(new URL(request.getDownloadURL()), jarFile, 5000, 300000); log.info("[OmsContainer-{}] download jar successfully, path={}", containerId, jarFile.getPath()); } // 創(chuàng)建新容器 OmsContainer newContainer = new OmsJarContainer(containerId, containerName, version, jarFile); newContainer.init(); // 替換容器 CARGO.put(containerId, newContainer); log.info("[OmsContainer-{}] deployed new version:{} successfully!", containerId, version); if (oldContainer != null) { // 銷毀舊容器 oldContainer.destroy(); } } catch (Exception e) { log.error("[OmsContainer-{}] deployContainer(name={},version={}) failed.", containerId, containerName, version, e); // 如果部署失敗,則刪除該 jar(本次失敗可能是下載jar出錯導(dǎo)致,不刪除會導(dǎo)致這個版本永久無法重新部署) CommonUtils.executeIgnoreException(() -> FileUtils.forceDelete(jarFile)); } }
OmsContainerFactory提供了deployContainer方法,它會根據(jù)containerId以及version去下載指定的jar包,然后創(chuàng)建OmsJarContainer,執(zhí)行init方法
小結(jié)
OhMyClassLoader繼承了URLClassLoader,它定義了load方法,遍歷urls,挨個根據(jù)url創(chuàng)建JarFile,然后遍歷jarFile.entries(),找到.class結(jié)尾的entry,判斷是否是packageName開頭的,是則執(zhí)行父類的loadClass方法。不過這里貌似沒有對JarFile進行關(guān)閉,可能會導(dǎo)致資源泄露。
以上就是PowerJob的OhMyClassLoader工作流程源碼解讀的詳細(xì)內(nèi)容,更多關(guān)于PowerJob OhMyClassLoader的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Hadoop?MapReduce實現(xiàn)單詞計數(shù)(Word?Count)
這篇文章主要為大家詳細(xì)介紹了如何利用Hadoop實現(xiàn)單詞計數(shù)(Word?Count)的MapReduce,文中的示例代碼講解詳細(xì),感興趣的可以跟隨小編一起學(xué)習(xí)一下2023-05-05Spring Security自定義登錄原理及實現(xiàn)詳解
這篇文章主要介紹了Spring Security自定義登錄原理及實現(xiàn)詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09Spring Data JPA實現(xiàn)動態(tài)查詢的兩種方法
本篇文章主要介紹了Spring Data JPA實現(xiàn)動態(tài)查詢的兩種方法,具有一定的參考價值,有興趣的可以了解一下。2017-04-04Java實現(xiàn)將文件或者文件夾壓縮成zip的詳細(xì)代碼
這篇文章主要介紹了Java實現(xiàn)將文件或者文件夾壓縮成zip的詳細(xì)代碼,本文通過實例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-11-11IDEA 自帶的數(shù)據(jù)庫工具真的很牛(收藏版)
這篇文章主要介紹了IDEA 自帶的數(shù)據(jù)庫工具真的很牛(收藏版),本文以 IntelliJ IDEA/ Mac 版本作為演示,其他版本的應(yīng)該也差距不大,需要的朋友可以參考下2021-04-04Java中不得不知的Collection接口與Iterator迭代器
這篇文章主要介紹了Java中的Collection接口與Iterator迭代器,文中有詳細(xì)的代碼示例供大家參考,對我們的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-06-06Java并發(fā)編程之Semaphore(信號量)詳解及實例
這篇文章主要介紹了Java并發(fā)編程之Semaphore(信號量)詳解及實例的相關(guān)資料,需要的朋友可以參考下2017-06-06java動態(tài)代理(jdk與cglib)詳細(xì)解析
靜態(tài)代理:由程序員創(chuàng)建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經(jīng)存在了2013-09-09