| Artifact | io.github.microsphere-projects:microsphere-java-core (0.3.20-SNAPSHOT, release branch) |
| Packages | io.microsphere.util (34 types) · io.microsphere.util.jar (1) · io.microsphere.constants (8) · io.microsphere.text (1) · io.microsphere.misc (1) — 45 main-source types |
| JDK level | Compiled at Java 8; CI-tested on JDK 8 / 11 / 17 / 21 / 25 (Temurin) |
| Compile dependency | microsphere-java-annotations (@Nonnull / @Nullable / @Immutable / @ConfigurationProperty) — no other core package is required |
| Marker convention | Utility classes are abstract + private constructor + implements io.microsphere.util.Utils; see Utils |
This slice is the foundation layer of microsphere-java-core. It holds the low-level helpers that every other
package builds on: class-loader and resource resolution (ClassLoaderUtils), classpath introspection
(ClassPathUtils), the service-loading facade over java.util.ServiceLoader (ServiceLoaderUtils), version modelling
(Version + VersionUtils), array/string/assert primitives (ArrayUtils, StringUtils, Assert, SystemUtils),
the text placeholder formatter (FormatUtils), JAR access (util.jar.JarUtils), sun.misc.Unsafe access
(misc.UnsafeUtils) and the shared constant interfaces (io.microsphere.constants).
Reach for these types when you need a null-safe, JDK-version-portable way to: resolve a class or resource across a
class-loader hierarchy, enumerate or load SPI providers in priority order, compare a library or JDK version, split/blank
strings, build empty arrays, read JAR manifests, or reach Unsafe — without writing the fragile reflective code
yourself. If a helper here already does the job, prefer it over Apache Commons or hand-rolled reflection: the behaviour
is pinned by the library's own test suite and documented against the real sources.
Note
These utilities deliberately degrade rather than throw: most lookups return null / an empty collection / a
neutral boolean when the input is absent. This is by design (see Pitfalls), but a
handful of methods do throw — every such case is flagged below and is backed by a line reference.
| I want to… | Use | Why this rather than the JDK directly |
|---|---|---|
| Load a class by name, walking the full loader chain | ClassLoaderUtils.loadClass / resolveClass |
Handles primitives, null loaders, and caches results (util/ClassLoaderUtils.java:547, :1426) |
Find a .class / package / arbitrary resource URL |
ClassLoaderUtils.getResource(...) + ResourceType |
Normalises the name per resource kind before lookup (util/ClassLoaderUtils.java:783, :843, :1931) |
| List a loader's classpath entries | ClassLoaderUtils.findAllClassPathURLs |
Delegates to a JDK-aware URLClassPathHandle (util/ClassLoaderUtils.java:1550-1564) |
| Load SPI providers in priority order | ServiceLoaderUtils.loadServicesList / loadFirstService |
Sorts by Prioritized, optional caching (util/ServiceLoaderUtils.java:890, :846) |
| Know the running JDK version | VersionUtils.CURRENT_JAVA_VERSION |
Parses java.version for legacy and modern schemes (util/VersionUtils.java:266, :284) |
| Compare library versions | Version.ofVersion(String) |
Immutable, Comparable, lenient segment parsing (util/Version.java:595) |
| Read system properties safely | SystemUtils.getSystemProperty |
Tolerates SecurityException, caches a copy (util/SystemUtils.java:605) |
| Split / blank-check strings | StringUtils.split / isBlank |
null-safe, keeps empty tokens (util/StringUtils.java:163, :81) |
| Shared literal values | io.microsphere.constants.* |
One source of truth for separators, protocols, symbols (constants/Constants.java:13-14) |
Format {} placeholders |
text.FormatUtils.format |
SLF4J-style substitution (text/FormatUtils.java:38) |
| Open a JAR from a resource URL | util.jar.JarUtils.toJarFile |
Resolves the archive path, returns null on miss (util/jar/JarUtils.java:86) |
Reach sun.misc.Unsafe |
misc.UnsafeUtils |
Reflects theUnsafe, with offsets cached (misc/UnsafeUtils.java:34, :564) |
Maven coordinates (from the reactor POM):
<dependency>
<groupId>io.github.microsphere-projects</groupId>
<artifactId>microsphere-java-core</artifactId>
<version>0.3.20</version> <!-- newest published tag; 0.3.20-SNAPSHOT is this same line's working version -->
</dependency>| Field | Value | Evidence |
|---|---|---|
| Group ID | io.github.microsphere-projects |
pom.xml:12 |
| Artifact ID | microsphere-java-core |
microsphere-java-core/pom.xml (module in pom.xml:61) |
| Version | ${revision} = 0.3.20-SNAPSHOT on release |
pom.xml:54 |
| Parent (build) | microsphere-build:0.3.16 |
pom.xml:9 |
| Compile dependency | microsphere-java-annotations |
microsphere-java-core/pom.xml (module pom.xml:60) |
| Bytecode target | Java 8 | no maven.compiler.* / <release> property exists anywhere in the reactor POMs, so the level is inherited from the external parent microsphere-build:0.3.16; cross-checked by the CI matrix below |
| CI JDK matrix | 8, 11, 17, 21, 25 (Temurin) | .github/workflows/maven-build.yml:25, :33 |
| Consumable BOM | microsphere-java-dependencies (in-repo; manages exactly the 7 code-bearing modules) |
microsphere-java-dependencies/pom.xml |
| Imported BOM | microsphere-all-bom:0.3.13 + spring-framework-bom |
microsphere-java-parent/pom.xml:22, :33-36, :40-46 |
| Spring Framework compatible range | 5.3.39 on JDK [1.8,17) via the java8-16 profile; 7.0.9 otherwise |
microsphere-java-parent/pom.xml:23, :55-62 |
Tip
The CI runs mvn ... -Drevision=0.0.1-SNAPSHOT ... -Dsurefire.useSystemClassLoader=false test
(.github/workflows/maven-build.yml:38-45). The -Drevision there is a build-time override, not the published
version; the released release-branch version stays 0.3.20-SNAPSHOT. The useSystemClassLoader=false flag is
relevant to these utilities because several tests assert behaviour that depends on which class loader sees the test
resources.
| Item | Value | Evidence |
|---|---|---|
| Documented line | release, ${revision} = 0.3.20-SNAPSHOT |
root pom.xml:54 |
| Newest published release | 0.3.20, tagged 2026-10-16 |
the 0.3.20 tag resolves to chore: merge main into release [skip ci], committed 2026-10-16T03:43:20Z |
| Previous release | 0.3.19, tagged 2026-08-30 |
the 0.3.19 tag, committed 2026-08-30T12:51:37Z |
| Changelog source | release-notes.md at the repository root |
The project publishes no CHANGELOG.md; the v0.3.16–v0.3.20 blocks of release-notes.md read Release notes generation failed and carry raw commit lists, so every entry below is quoted as a commit subject and checked against the release sources. |
0.3.20 adds no new API surface for this area — the 0.3.19...0.3.20 range is a hardening release. What it does change, and how far that reaches the sources this page documents:
- Nullability contracts. 21 of 45 main sources in the scope of this page declare
@Nonnull/@Nullable. Subjects in the range that name this area:74743377 Add nullability annotations to core utils/34e27823 Refine nullability contracts across core APIs. - Null-input and behaviour fixes.
001f2a6c Update VersionUtils.java/40919b19 Update Version.java/0f2c953d Update ValueHolder.java/dd98f715 Update StringUtils.java/7098a537 Update ObjectUtils.java/7162bd14 Update PriorityComparator.java/afdd348c Update ServiceLoaderUtils.java/abe91a32 Update PropertyResourceBundleUtils.java/1fa3f655 Update PropertyResourceBundleControl.java/9f123115 Add Apache license header to ClassUtils/6e039faa Use computeIfAbsent for package class sets/81806fd9 Refine utility APIs and test assertions.
Note
Reading the commit list: Two subjects in the same range do not hold on this line: 0ddf8dd9 Remove deprecated BaseUtils class (io.microsphere.util.BaseUtils is still present on release, 44 lines) and 2c166301 Add ObjectUtils.requireNonNull helpers (ObjectUtils declares no requireNonNull). The 0.3.19...0.3.20 comparison range includes the chore: merge main into release [skip ci] commits, so it carries main-side work that the published release tree does not have yet.
Full paths are given once; subsequent references shorten microsphere-java-core/src/main/java/io/microsphere/ to the
last two segments (e.g. util/ClassLoaderUtils.java). Signatures are copied from the release sources.
// util/Utils.java — an empty marker interface, @since 1.0.0
public interface Utils {}Utils carries no methods. Its contract is conventional, not enforced: a "utility" class is declared abstract, given
a private constructor, and implements Utils. Nearly every class in this slice follows it — ClassLoaderUtils,
ServiceLoaderUtils, ClassPathUtils, ArrayUtils, StringUtils, SystemUtils, NumberUtils, VersionUtils,
text.FormatUtils, util.jar.JarUtils, misc.UnsafeUtils.
Warning
util/Assert.java is abstract with a private constructor but does not implement Utils — a deliberate
exception, because it throws rather than degrades. util/Version.java is also not a utility: it is an immutable
value object (@Immutable, Comparable<Version>, Serializable).
public abstract class ClassLoaderUtils implements Utils { // @since 1.0.0, private ctor
protected static final ClassLoadingMXBean classLoadingMXBean; // :90
private static final ConcurrentMap<String,Class<?>> loadedClassesCache = new ConcurrentHashMap<>(256); // :92
private static final URLClassPathHandle urlClassPathHandle; // :94 (ServiceLoadingURLClassPathHandle)
@Nonnull public static ClassLoader getDefaultClassLoader(); // :244
@Nonnull public static ClassLoader getClassLoader(@Nullable Class<?> loadedClass); // :298
@Nullable public static Class<?> loadClass(@Nonnull ClassLoader classLoader, @Nonnull String className); // :547
@Nullable public static Class<?> loadClass(@Nonnull ClassLoader classLoader, @Nonnull String className, boolean cached); // :589
@Nullable public static Class<?> doLoadClass(@Nullable ClassLoader classLoader, @Nullable String className); // :620
@Nonnull public static Set<URL> getResources(@Nullable ClassLoader classLoader, @Nonnull String name) throws IOException; // :707
@Nonnull public static Set<URL> getResources(@Nullable ClassLoader classLoader, @Nonnull ResourceType type, @Nonnull String name) throws IOException; // :669
@Nullable public static URL getResource(@Nonnull ClassLoader classLoader, @Nonnull String resourceName); // :783
@Nullable public static URL getResource(@Nullable ClassLoader classLoader, @Nonnull ResourceType resourceType, @Nonnull String resourceName); // :843
@Nullable public static URL getClassResource(@Nullable ClassLoader classLoader, @Nonnull Class<?> classType); // :1035
@Nullable public static String getResourceAsString(@Nullable ClassLoader classLoader, @Nullable String name); // :875
@Nonnull public static Set<ClassLoader> getInheritableClassLoaders(@Nullable ClassLoader classLoader); // :1069
@Nonnull public static Set<URL> findAllClassPathURLs(@Nullable ClassLoader classLoader); // :1550
public static boolean removeClassPathURL(@Nullable ClassLoader classLoader, @Nullable URL url); // :1598
@Nullable public static ClassLoader findURLClassLoader(@Nonnull ClassLoader classLoader); // :1635
@Nonnull public static URLClassLoader newURLClassLoader(@Nonnull URL[] urls, @Nonnull ClassLoader parent); // :1667 (overloads :1699..:1781)
@Nullable public static Class<?> resolveClass(@Nullable String className); // :1426 (overloads :1466, :1511)
public static boolean isPresent(@Nonnull String className); // :1369 / :1399
}| Behaviour | Detail | Evidence |
|---|---|---|
null class loader |
nullSafeClassLoader substitutes the system class loader (:218); getDefaultClassLoader tries thread-context → this class's loader → default (:244) |
util/ClassLoaderUtils.java:218, :244 |
| Blank / unresolvable name | doLoadClass returns null for a blank name and swallows any Throwable during loading (logs at trace), then tries to resolve a primitive (:620) |
util/ClassLoaderUtils.java:620 |
| Caching | loadClass(...,true) memoises into loadedClassesCache keyed by className + classLoader.hashCode() (buildCacheKey :1902) |
util/ClassLoaderUtils.java:589, :1902 |
| Resource order | getResource(cl,name) iterates ResourceType.values() and returns the first non-null; getResources returns the first non-empty set (:783, :707) |
util/ClassLoaderUtils.java:707, :783 |
| Throwing methods | getResources(...) declares IOException and NullPointerException (:669, :707); newURLClassLoader(...) throws IllegalArgumentException on null URL elements (assertNoNullElements, :1667) |
util/ClassLoaderUtils.java:669, :1667 |
| Thread safety | loadedClassesCache is a ConcurrentHashMap; classLoadingMXBean and the handle are static final — safe to publish (:90, :92, :94) |
util/ClassLoaderUtils.java:90-94 |
ResourceType (util/ClassLoaderUtils.java:1931) is a nested enum that both validates and normalises a resource name
before the lookup:
| Constant | Accepts | Normalisation | Evidence |
|---|---|---|---|
DEFAULT |
anything (supports() = true) |
as-is | :1945 |
CLASS |
names ending in .class, or a dotted class name |
dots → slashes | :1983 |
PACKAGE |
names with no / or \, not a .class |
dot-package form | :2030 |
resolve(name) |
— | returns null when blank or unsupported, else normalises and strips a leading / |
:2086 |
public abstract class ServiceLoaderUtils implements Utils { // @since 1.0.0
public static final String SERVICES_PROVIDER_LOCATION = "META-INF/services/"; // :119
public static final String SERVICE_LOADER_CACHED_PROPERTY_NAME = /* "microsphere." */ "...service-loader.cached"; // :112
private static final boolean SERVICE_LOADER_CACHED; // :138, parsed from -D, default "false" (:107)
private static final ConcurrentMap<Class<?>, List<?>> servicesCache; // :140
@Nonnull public static <T> List<T> loadServicesList(@Nonnull Class<T> serviceType, @Nullable ClassLoader cl, boolean cached); // :382..:468
@Nonnull public static <T> T[] loadServices(@Nonnull Class<T> serviceType, @Nullable ClassLoader cl, boolean cached); // :493..:576
@Nullable public static <T> T loadFirstService(@Nonnull Class<T> serviceType, @Nullable ClassLoader cl, boolean cached); // :602..:693
@Nullable public static <T> T loadLastService(@Nonnull Class<T> serviceType, @Nullable ClassLoader cl, boolean cached); // :719..:805
@Nonnull public static Set<Class<T>> getServiceClasses(@Nonnull Class<T> serviceType, boolean failFast); // :262
@Nonnull public static Set<URL> getServiceResoources(@Nonnull Class<?> serviceType, @Nonnull ClassLoader classLoader); // :355 (note: source name has the typo)
}| Behaviour | Detail | Evidence |
|---|---|---|
| Ordering | Providers are loaded then sorted with Prioritized.COMPARATOR; first/last are ListUtils.first/last of that sorted list (:890, :828) |
util/ServiceLoaderUtils.java:890 |
| Caching key | When cached (or the global flag is on), servicesCache.computeIfAbsent(serviceType, ...) — keyed by service type only, not class loader (:846) |
util/ServiceLoaderUtils.java:843-846 |
| Zero providers | loadServicesAsList(type, cl) throws IllegalArgumentException when the ServiceLoader iterator is empty (:882-886) — this is a real throw, not an empty list |
util/ServiceLoaderUtils.java:882-886 |
null class loader |
Falls back to getClassLoader(...) (:873) |
util/ServiceLoaderUtils.java:873 |
| Non-assignable / unloadable provider | getServiceClasses(type, true) throws IllegalStateException (:270, :276); with failFast=false it returns an empty set |
util/ServiceLoaderUtils.java:262 |
Warning
The method is named getServiceResoources (three os) in the source — copy it exactly (util/ServiceLoaderUtils.java:355).
The cache is keyed by service type only, so do not reuse loadServicesList(type, loaderA, true) and then
loadServicesList(type, loaderB, true) expecting different results — the second call returns loaderA's cached list
(:846).
public abstract class ClassPathUtils implements Utils { // @since 1.0.0
protected static final RuntimeMXBean runtimeMXBean; // :58
public static final Set<String> bootstrapClassPaths; // :61 (immutable, computed once)
public static final Set<String> classPaths; // :64
@Nonnull public static Set<String> getBootstrapClassPaths(); // :127
@Nonnull public static Set<String> getClassPaths(); // :149
@Nullable public static String getRuntimeClassLocation(@Nonnull Class<?> classType); // :207
}| Behaviour | Detail | Evidence |
|---|---|---|
| Bootstrap path on JDK 9+ | initBootstrapClassPaths calls resolveClassPaths(runtimeMXBean.isBootClassPathSupported(), ...); when unsupported (JDK 9+) the set is empty |
util/ClassPathUtils.java:68, :94 |
| Application classpath | initClassPaths always splits getClassPath() on File.pathSeparator |
util/ClassPathUtils.java:73, :103 |
| One-time immutability | Both fields are static final sets computed at class-init — snapshot values, never refreshed |
util/ClassPathUtils.java:61, :64 |
@Immutable
public class Version implements Comparable<Version>, Serializable { // @since 1.0.0
private final int major;
private final int minor;
private final int patch;
@Nullable private final String preRelease; // :95-102
public Version(int major, int minor, int patch, @Nullable String preRelease); // :165
public static Version of(int... versionParts); // :441..
@Nonnull public static Version ofVersion(@Nonnull String version);// :595
@Nonnull public static Version ofVersion(@Nonnull Class<?> classType); // :626-640
public boolean gt(@Nullable Version v) /* ge/lt/le/eq + isGreaterThan/… */; // :220-317
@Override public int compareTo(@Nonnull Version other); // :372
public static int getValue(@Nonnull String version); // :661 (package-private helper)
public enum Operator implements BiPredicate<Version,Version> { EQ,LT,LE,GT,GE; public static Operator of(String symbol); } // :671, :766
}| Behaviour | Detail | Evidence |
|---|---|---|
ofVersion(String) parsing |
splits on - for pre-release, then on . for major[.minor[.patch]]; missing minor/patch default to 0; extra dot segments are ignored |
util/Version.java:595 |
| Null comparison | every gt/lt/le/ge/eq returns false for a null argument (Operator handles null) |
util/Version.java:220-317, :671 |
| Pre-release rank | a release version ranks after its pre-release (comparePreRelease: null > non-null) |
util/Version.java:403 |
| Invalid input | ofVersion(String) throws IllegalArgumentException for null/blank; getValue throws IllegalArgumentException for non-numeric; constructor asserts non-negative and not all-zero |
util/Version.java:165, :595, :661 |
| From a class | ofVersion(Class) uses the caller's loader, else returns VersionUtils.CURRENT_JAVA_VERSION for bootstrap-loaded classes; throws IllegalArgumentException when the artifact has no version |
util/Version.java:626-640 |
public abstract class VersionUtils implements Utils { // @since 1.0.0
public static final SourceVersion LATEST_JAVA_VERSION; // :39 (javax.lang.model.SourceVersion)
public static final Version CURRENT_JAVA_VERSION; // :44
public static final Version JAVA_VERSION_8; /* …through JAVA_VERSION_24 */ // :49-129
public static boolean testCurrentJavaVersion(@Nonnull String operator, @Nonnull Version version); // :156
public static boolean testVersion(@Nullable Version base, @Nullable Operator op, @Nullable Version compared); // :248
public static String currentJavaMajorVersion(); // :266
public static String detectJavaMajorVersion(@Nonnull String javaVersion); // :284
}| Behaviour | Detail | Evidence |
|---|---|---|
Legacy vs modern java.version |
detectJavaMajorVersion treats a leading "1" segment as the legacy scheme and returns the next segment ("1.8.0" → "8"); otherwise returns the segment before the first dot ("17.0.0" → "17") |
util/VersionUtils.java:284 |
| Null-safe comparison | testVersion(...) returns false if any argument is null |
util/VersionUtils.java:248 |
| Compile dependency | references javax.lang.model.SourceVersion (java.compiler) |
util/VersionUtils.java:39 |
StringUtils (util/StringUtils.java), ArrayUtils (util/ArrayUtils.java), Assert (util/Assert.java), SystemUtils (util/SystemUtils.java)
public abstract class StringUtils implements Utils { // :81 isBlank, :113 isNotBlank, :137/:163 split, :221 contains
public static final String EMPTY = ""; // :50
public static final String[] EMPTY_STRING_ARRAY = {}; // :61
public static final int INDEX_NOT_FOUND = -1; // :294
}
public abstract class ArrayUtils implements Utils { // :211 of, :375 ofArray, :396 nullSafeArray, :414.. length
public static final boolean[] EMPTY_BOOLEAN_ARRAY; /* ~24 EMPTY_* constants */ // :55..
public static boolean contains(int[] array, int value); // :1911 (binarySearch — array MUST be sorted)
public static boolean contains(Object[] array, Object obj); // :2096 (linear)
}
public abstract class Assert { // :60 assertTrue … :149 assertNotNull … :348 assertNoNullElements (throws IAE)
public static <T> T assertNotNull(@Nullable T object, String message, Object... args); // :131
}
public abstract class SystemUtils implements Utils { // :347 IS_OS_WINDOWS … :357 IS_JAVA_8 … :522 IS_LTS_JAVA_VERSION
@Nullable public static String getSystemProperty(@Nonnull String key, @Nullable String defaultValue); // :605
}| Type | Null handling | Throws? | Thread-safety |
|---|---|---|---|
StringUtils.isBlank |
null/empty/whitespace → true (:81) |
no | stateless |
StringUtils.split(v, delim) |
null/empty value → EMPTY_STRING_ARRAY; null delim → single-element array of the whole string; empty tokens are kept (:163) |
no | stateless |
ArrayUtils.contains(int[],int) |
returns false for null array |
no | stateless — but uses Arrays.binarySearch, so results are wrong unless the array is sorted (:1911) |
Assert.* |
null object → throws IllegalArgumentException (:131) |
yes — this class exists to throw | stateless; note assertNoNullElements skips null/empty arrays rather than failing (:348) |
SystemUtils.getSystemProperty |
returns the default when the property is missing or a SecurityException is caught (:605) |
no (logs a warning) | systemPropertiesCopy is a plain non-volatile Map; copySystemProperties() is synchronized (SystemUtils.class) (:528, :548) |
SystemUtils flags |
IS_JAVA_* are computed from java.specification.version (:357, :638), not java.version |
no | immutable static final |
Note
NumberUtils (util/NumberUtils.java) is abstract … implements Utils with a private constructor and @since 1.0.0,
but currently exposes no public methods — it is a reserved placeholder. Do not look for parsing helpers there.
public abstract class FormatUtils implements Utils { // text/FormatUtils.java
public static final String DEFAULT_PLACEHOLDER = "{}"; // :17
@Nullable public static String format(@Nullable String pattern, Object... args); // :38 → formatWithPlaceholder (:62)
}
public abstract class JarUtils implements Utils { // util/jar/JarUtils.java
public static final String MANIFEST_RESOURCE_PATH = "META-INF/MANIFEST.MF"; // :63
@Nullable public static JarFile toJarFile(@Nonnull URL url) throws NullPointerException, IllegalArgumentException; // :86
@Nonnull @Immutable public static List<JarEntry> filter(@Nullable JarFile jarFile, @Nullable JarEntryFilter filter); // :193
}
public abstract class UnsafeUtils implements Utils { // misc/UnsafeUtils.java
public static final String UNSAFE_CLASS_NAME = "sun.misc.Unsafe"; // :34
private static final ConcurrentMap<String,Long> offsetCache = new ConcurrentHashMap<>(); // :559
}| Type | Behaviour | Evidence |
|---|---|---|
FormatUtils.format |
blank pattern → returned as-is; zero args → pattern unchanged; each arg replaces the first remaining {}; surplus args ignored, surplus placeholders left literal (:62, loop :73-80) |
text/FormatUtils.java:38, :62-80 |
JarUtils.toJarFile |
null URL → NullPointerException; non-jar:/file: protocol → IllegalArgumentException (assertJarURLProtocol :111); unresolvable archive → null (logged, IOException swallowed) (:86) |
util/jar/JarUtils.java:86, :111, :168 |
JarUtils.filter |
null JarFile → shared emptyList() (:193) |
util/jar/JarUtils.java:193 |
UnsafeUtils class-init |
getUnsafe() reflects theUnsafe; on failure throws UnsupportedOperationException, which surfaces as ExceptionInInitializerError on JDKs without sun.misc.Unsafe (:564, :570-572) |
misc/UnsafeUtils.java:564, :570-572 |
Eight interfaces, all @since 1.0.0, aggregated by Constants so a single import static io.microsphere.constants.Constants.* pulls every literal (constants/Constants.java:13-14).
| Interface | Representative constants | Evidence |
|---|---|---|
SymbolConstants |
COMMA = "," :184, SPACE = " " :189, DOT = "." :233, HYPHEN = "-" :308, EQUAL = "=" :243, LESS_THAN_OR_EQUAL_TO = "<=" :253, GREATER_THAN_OR_EQUAL_TO = ">=" :263; char twins (COMMA_CHAR :30, …) |
constants/SymbolConstants.java |
SeparatorConstants |
ARCHIVE_ENTRY_SEPARATOR = "!/" :26, FILE_SEPARATOR :31, PATH_SEPARATOR :36, LINE_SEPARATOR :41 (from File/System) |
constants/SeparatorConstants.java |
PathConstants |
SLASH = "/" :28, DOUBLE_SLASH :33, BACK_SLASH :38, SLASH_CHAR :18 |
constants/PathConstants.java |
ProtocolConstants |
FILE_PROTOCOL = "file" :24, JAR_PROTOCOL :49, CLASSPATH_PROTOCOL = "classpath" :64, CONSOLE_PROTOCOL = "console" :69 |
constants/ProtocolConstants.java |
FileConstants |
JAR = "jar" :26, FILE_EXTENSION = "." :51, CLASS_EXTENSION = ".class" :76 |
constants/FileConstants.java |
PropertyConstants |
MICROSPHERE_PROPERTY_NAME_PREFIX = "microsphere." :35, ENABLED_PROPERTY_NAME = "enabled" :30 |
constants/PropertyConstants.java |
ResourceConstants |
METADATA_RESOURCE = "META-INF/" :33, MICROSPHERE_METADATA_RESOURCE :38 |
constants/ResourceConstants.java |
Constants |
aggregator only (extends the six others) |
constants/Constants.java:13-14 |
All snippets are condensed from test bodies under microsphere-java-core/src/test/java/io/microsphere/. The citation
above each block names the file and lines.
Parse, compare and rank versions — util/VersionTest.java:117-122, :190-221, :240-242:
Version v = Version.of("1.2.3"); // of/ofVersion both parse
assertTrue(v.gt(Version.of(1, 2, 2))); // strictly greater
assertFalse(v.gt(v)); // not greater than itself
assertFalse(v.gt(null)); // null-safe → false, never throws
Version alpha = Version.of("1.2.3-alpha");
assertTrue(v.gt(alpha)); // release ranks above its pre-releaseResolve a library version from one of its classes — util/VersionTest.java:124-137, :316-324:
Version jsr305 = Version.ofVersion(javax.annotation.Nullable.class); // "3.0.2"
assertEquals("3.0.2", jsr305.toString());
assertSame(VersionUtils.CURRENT_JAVA_VERSION, Version.ofVersion(String.class)); // bootstrap loader → running JDK
assertThrows(IllegalArgumentException.class, () -> Version.ofVersion(Version.class)); // no version metadata → throwsLegacy vs modern java.version detection — util/VersionUtilsTest.java:142-150:
assertEquals("17", VersionUtils.detectJavaMajorVersion("17.0.0")); // modern
assertEquals("8", VersionUtils.detectJavaMajorVersion("1.8.0")); // legacy 1.x scheme
assertEquals("1", VersionUtils.detectJavaMajorVersion("1")); // no dot → as-isLoad SPI providers in priority order — util/ServiceLoaderUtilsTest.java:81-105, :211-232:
List<EventListener> listeners = ServiceLoaderUtils.loadServicesList(EventListener.class, classLoader, true);
assertEquals(2, listeners.size());
// sorted by Prioritized: EchoEventListener2 wins the "first", EchoEventListener the "last"
assertSame(EchoEventListener2.class, ServiceLoaderUtils.loadFirstService(EventListener.class, classLoader).getClass());
assertSame(EchoEventListener.class, ServiceLoaderUtils.loadLastService(EventListener.class, classLoader).getClass());A service with no provider throws (do not rely on an empty result) — util/ServiceLoaderUtilsTest.java:107-112, :59-63:
assertThrows(IllegalArgumentException.class, () -> ServiceLoaderUtils.loadServicesList(Set.class)); // zero providers
assertThrows(IllegalStateException.class, () -> ServiceLoaderUtils.getServiceClasses(Utils.class)); // non-assignable, failFast
assertEquals(java.util.Collections.emptySet(), ServiceLoaderUtils.getServiceClasses(Utils.class, false)); // failFast=false degradesClass-loading degrades to null — util/ClassLoaderUtilsTest.java:345-356, :376, :431-438:
assertNull(ClassLoaderUtils.loadClass(classLoader, "io.microsphere.not.found.class")); // unknown → null
assertNull(ClassLoaderUtils.loadClass(classLoader, "")); // blank → null
assertNull(ClassLoaderUtils.loadClass(classLoader, null, true)); // null name → null (even cached)
assertNull(ClassLoaderUtils.getResourceAsString("not-found")); // missing resource → nullResource lookup by ResourceType — util/ClassLoaderUtilsTest.java:410-411:
Set<URL> urls = ClassLoaderUtils.getResources(classLoader, "io.microsphere.util"); // iterates ResourceType.values()
assertNotNull(urls);StringUtils.split keeps empty tokens and is null-safe — util/StringUtilsTest.java:89-112, :72-86:
assertTrue(StringUtils.isBlank(" ")); // whitespace-only
assertSame(StringUtils.EMPTY_STRING_ARRAY, StringUtils.split(null, " "));
assertArrayEquals(new String[]{"a", "b", "c"}, StringUtils.split("a,b,c", ","));
assertArrayEquals(new String[]{"a", "", "b", "c"}, StringUtils.split("a,,b,c", ",")); // empty tokens preservedFormatUtils placeholder substitution — text/FormatUtilsTest.java:34-53:
assertNull(FormatUtils.format(null));
assertEquals("A,B,C,D,E", FormatUtils.format("A,{},C,{},E", "B", "D"));
assertEquals("A,B,C,{},E", FormatUtils.format("A,{},C,{},E", "B")); // surplus placeholder stays literal
assertEquals("A,1,C,2,E", FormatUtils.format("A,{},C,{},E", 1, 2, 3)); // 3rd arg ignoredOpen a JAR from a resource URL — util/jar/JarUtilsTest.java:104-125, :99-102:
URL url = ClassLoaderUtils.getClassResource(classLoader, javax.annotation.Nonnull.class);
assertNotNull(JarUtils.toJarFile(url)); // resolves jsr305.jar
assertNull(JarUtils.toJarFile(new URL("jar:file:/path/to/missing.jar!/e")));// unresolvable → null
assertThrows(IllegalArgumentException.class, () -> JarUtils.toJarFile(new URL("http://github.com"))); // bad protocol
assertSame(java.util.Collections.emptyList(), JarUtils.filter(null, null)); // null jar → empty| Concern | JDK 8 | JDK 9 / 11 | JDK 17 | JDK 21 | JDK 25 | Evidence |
|---|---|---|---|---|---|---|
ClassPathUtils.getBootstrapClassPaths() |
populated from sun.misc boot CP |
empty (isBootClassPathSupported() = false) |
empty | empty | empty | util/ClassPathUtils.java:68, :94 |
SystemUtils.IS_JAVA_* |
IS_JAVA_8 true |
IS_JAVA_9/11 … |
IS_JAVA_17 |
IS_JAVA_21 |
no dedicated flag past IS_JAVA_24 |
util/SystemUtils.java:357-517 |
VersionUtils.detectJavaMajorVersion("java.version") |
"1.8.0_x" → "8" |
"9.0.0"/"11.0.x" → major |
"17.x" |
"21.x" |
"25.x" (scheme unchanged) |
util/VersionUtils.java:284 |
misc.UnsafeUtils static init |
works (theUnsafe present) |
works | works (JDK-internal, deprecated) | works | may fail → ExceptionInInitializerError if sun.misc.Unsafe is gone |
misc/UnsafeUtils.java:564, :570-572 |
ClassLoaderUtils.removeClassPathURL |
URLClassLoader (a SecureClassLoader) → mutable |
system loader is not URLClassLoader; needs SecureClassLoader or returns false |
same | same | same | util/ClassLoaderUtils.java:1598-1603 |
VersionUtils.LATEST_JAVA_VERSION |
RELEASE_8 |
RELEASE_11 |
RELEASE_17 |
RELEASE_21 |
RELEASE_25 (delegated to SourceVersion.latest()) |
util/VersionUtils.java:39 |
Note
IS_JAVA_* and CURRENT_JAVA_VERSION come from different sources: the flags read java.specification.version
(matchesJavaVersion, util/SystemUtils.java:638) while CURRENT_JAVA_VERSION reads java.version through
currentJavaMajorVersion() (util/VersionUtils.java:266). Under the legacy scheme these agree, but they are
computed independently.
loadServicesList/loadServicesthrow when there is no provider. An emptyServiceLoaderraisesIllegalArgumentException, not an empty result (util/ServiceLoaderUtils.java:882-886). Guard withgetServiceClassNames(...)first, or catch, if the service may be absent.- The services cache ignores the class loader.
servicesCacheis keyed by service type only (util/ServiceLoaderUtils.java:846); cached loads across different loaders share one list. Passcached = falsewhen loader isolation matters. ArrayUtils.contains(int[], int)and the other numeric overloads need a sorted array. They delegate toArrays.binarySearch(util/ArrayUtils.java:1911-2069); on an unsorted array the answer can be wrong. Theboolean[]andObject[]overloads use a linear scan and are order-independent (:1887,:2096).ClassPathUtilssnapshots are computed once.bootstrapClassPathsandclassPathsarestatic final(util/ClassPathUtils.java:61,:64) and never refresh; on JDK 9+ the boot set is empty by design, so filtering JDK libraries via the boot path only works on JDK 8.Assert.assertNoNullElementsskipsnull/empty input. It only inspects elements when the array/iterable is present and non-empty (util/Assert.java:348), so anullarray passes. UseAssert.assertNotNullwhennullitself must fail.UnsafeUtilscan blow up at class-load time. Ifsun.misc.Unsafeis unavailable,getUnsafe()throwsUnsupportedOperationExceptionfrom a static initializer (misc/UnsafeUtils.java:570-572), which the JVM reports asExceptionInInitializerError— not a value you cancatchat the call site. TreatUnsafeUtilsas JDK-version gated.ofVersion(Class)throws for unversioned artifacts. A class whose archive carries no version metadata producesIllegalArgumentException(util/Version.java:626-640); explodedtarget/classesdirectories are the common case.JarUtils.toJarFilemixes returns and throws. Missing archives returnnull(:86) butnullURLs throwNullPointerExceptionand bad protocols throwIllegalArgumentException(assertJarURLProtocol,:111) — the three outcomes are easy to conflate.- Blank input degrades silently in class loading.
doLoadClassreturnsnullfor blank names and swallows anyThrowablewhile loading (util/ClassLoaderUtils.java:620); anullresult does not by itself prove the class does not exist (it could indicate a linkage error) — check logs at trace level if needed. getAllLoadedClassesMapuses the top loader for every key. When mapping each inheritable loader to its loaded classes, the loop passes the caller-supplied loader rather than the iteration variable (util/ClassLoaderUtils.java:1142-1144), so the map values reflect one loader. Do not use it to attribute classes to their individual defining loaders.
- Class Loading and Artifacts — the
URLClassPathHandleSPI thatClassLoaderUtilsdelegates to - Collections and Filters —
Lists/Sets/Prioritizedused byServiceLoaderUtilssorting - Networking and URL Protocols —
URLUtilsbehindJarUtilsarchive-path resolution - Language Abstractions —
io.microsphere.lang.Prioritized, the ordering contract these utilities honour - Developer-facing deep dive: Utilities, Constants and Text — Developer Guide