這裏從源碼學習一下rocksdb是如何加載本地類庫的。java
<!-- https://mvnrepository.com/artifact/org.rocksdb/rocksdbjni --> <dependency> <groupId>org.rocksdb</groupId> <artifactId>rocksdbjni</artifactId> <version>5.5.1</version> </dependency>
private enum LibraryState { NOT_LOADED, LOADING, LOADED } private static AtomicReference<LibraryState> libraryLoaded = new AtomicReference<>(LibraryState.NOT_LOADED); static { RocksDB.loadLibrary(); } /** * Loads the necessary library files. * Calling this method twice will have no effect. * By default the method extracts the shared library for loading at * java.io.tmpdir, however, you can override this temporary location by * setting the environment variable ROCKSDB_SHAREDLIB_DIR. */ public static void loadLibrary() { if (libraryLoaded.get() == LibraryState.LOADED) { return; } if (libraryLoaded.compareAndSet(LibraryState.NOT_LOADED, LibraryState.LOADING)) { final String tmpDir = System.getenv("ROCKSDB_SHAREDLIB_DIR"); // loading possibly necessary libraries. for (final CompressionType compressionType : CompressionType.values()) { try { if (compressionType.getLibraryName() != null) { System.loadLibrary(compressionType.getLibraryName()); } } catch (UnsatisfiedLinkError e) { // since it may be optional, we ignore its loading failure here. } } try { NativeLibraryLoader.getInstance().loadLibrary(tmpDir); } catch (IOException e) { libraryLoaded.set(LibraryState.NOT_LOADED); throw new RuntimeException("Unable to load the RocksDB shared library" + e); } libraryLoaded.set(LibraryState.LOADED); return; } while (libraryLoaded.get() == LibraryState.LOADING) { try { Thread.sleep(10); } catch(final InterruptedException e) { //ignore } } }
這裏設計了一個AtomicReference<LibraryState>,用來避免重複加載linux
/** * This class is used to load the RocksDB shared library from within the jar. * The shared library is extracted to a temp folder and loaded from there. */ public class NativeLibraryLoader { //singleton private static final NativeLibraryLoader instance = new NativeLibraryLoader(); private static boolean initialized = false; private static final String sharedLibraryName = Environment.getSharedLibraryName("rocksdb"); private static final String jniLibraryName = Environment.getJniLibraryName("rocksdb"); private static final String jniLibraryFileName = Environment.getJniLibraryFileName("rocksdb"); private static final String tempFilePrefix = "librocksdbjni"; private static final String tempFileSuffix = Environment.getJniLibraryExtension(); /** * Get a reference to the NativeLibraryLoader * * @return The NativeLibraryLoader */ public static NativeLibraryLoader getInstance() { return instance; } /** * Firstly attempts to load the library from <i>java.library.path</i>, * if that fails then it falls back to extracting * the library from the classpath * {@link org.rocksdb.NativeLibraryLoader#loadLibraryFromJar(java.lang.String)} * * @param tmpDir A temporary directory to use * to copy the native library to when loading from the classpath. * If null, or the empty string, we rely on Java's * {@link java.io.File#createTempFile(String, String)} * function to provide a temporary location. * The temporary file will be registered for deletion * on exit. * * @throws java.io.IOException if a filesystem operation fails. */ public synchronized void loadLibrary(final String tmpDir) throws IOException { try { System.loadLibrary(sharedLibraryName); } catch(final UnsatisfiedLinkError ule1) { try { System.loadLibrary(jniLibraryName); } catch(final UnsatisfiedLinkError ule2) { loadLibraryFromJar(tmpDir); } } } /** * Attempts to extract the native RocksDB library * from the classpath and load it * * @param tmpDir A temporary directory to use * to copy the native library to. If null, * or the empty string, we rely on Java's * {@link java.io.File#createTempFile(String, String)} * function to provide a temporary location. * The temporary file will be registered for deletion * on exit. * * @throws java.io.IOException if a filesystem operation fails. */ void loadLibraryFromJar(final String tmpDir) throws IOException { if (!initialized) { System.load(loadLibraryFromJarToTemp(tmpDir).getAbsolutePath()); initialized = true; } } File loadLibraryFromJarToTemp(final String tmpDir) throws IOException { final File temp; if (tmpDir == null || tmpDir.isEmpty()) { temp = File.createTempFile(tempFilePrefix, tempFileSuffix); } else { temp = new File(tmpDir, jniLibraryFileName); if (temp.exists() && !temp.delete()) { throw new RuntimeException("File: " + temp.getAbsolutePath() + " already exists and cannot be removed."); } if (!temp.createNewFile()) { throw new RuntimeException("File: " + temp.getAbsolutePath() + " could not be created."); } } if (!temp.exists()) { throw new RuntimeException("File " + temp.getAbsolutePath() + " does not exist."); } else { temp.deleteOnExit(); } // attempt to copy the library from the Jar file to the temp destination try (final InputStream is = getClass().getClassLoader(). getResourceAsStream(jniLibraryFileName)) { if (is == null) { throw new RuntimeException(jniLibraryFileName + " was not found inside JAR."); } else { Files.copy(is, temp.toPath(), StandardCopyOption.REPLACE_EXISTING); } } return temp; } /** * Private constructor to disallow instantiation */ private NativeLibraryLoader() { } }
這個採用了單例的模式,另外加載的代碼結構比較清晰maven
/** * Firstly attempts to load the library from <i>java.library.path</i>, * if that fails then it falls back to extracting * the library from the classpath * {@link org.rocksdb.NativeLibraryLoader#loadLibraryFromJar(java.lang.String)} * * @param tmpDir A temporary directory to use * to copy the native library to when loading from the classpath. * If null, or the empty string, we rely on Java's * {@link java.io.File#createTempFile(String, String)} * function to provide a temporary location. * The temporary file will be registered for deletion * on exit. * * @throws java.io.IOException if a filesystem operation fails. */ public synchronized void loadLibrary(final String tmpDir) throws IOException { try { System.loadLibrary(sharedLibraryName); } catch(final UnsatisfiedLinkError ule1) { try { System.loadLibrary(jniLibraryName); } catch(final UnsatisfiedLinkError ule2) { loadLibraryFromJar(tmpDir); } } }
首先從java.library.path加載,加載失敗的話,從classpath的jar包加載ide
public static String getJniLibraryName(final String name) { if (isUnix()) { final String arch = is64Bit() ? "64" : "32"; if(isPowerPC()) { return String.format("%sjni-linux-%s", name, ARCH); } else { return String.format("%sjni-linux%s", name, arch); } } else if (isMac()) { return String.format("%sjni-osx", name); } else if (isAix() && is64Bit()) { return String.format("%sjni-aix64", name); } else if (isSolaris()) { final String arch = is64Bit() ? "64" : "32"; return String.format("%sjni-solaris%s", name, arch); } else if (isWindows() && is64Bit()) { return String.format("%sjni-win64", name); } throw new UnsupportedOperationException(String.format("Cannot determine JNI library name for ARCH='%s' OS='%s' name='%s'", ARCH, OS, name)); }
單獨的一個Environment來構造跨平臺的類庫路徑學習