🏠 Home Modules & Migration Module Path vs Classpath
MODULES & MIGRATION

Module Path vs Classpath

Rule: Modules use module path while legacy code uses classpath, with different visibility and loading rules.

  • Module path: Strong encapsulation, explicit dependencies
  • Classpath: Flat namespace, all classes visible to each other
  • Mixed mode: Named modules can depend on automatic modules
// Compilation commands
// Classpath (legacy)
javac -cp lib/commons-lang.jar:lib/jackson.jar src/**/*.java

// Module path (Java 9+)
javac --module-path lib --module-source-path src -d out --module com.myapp

// Runtime commands
// Classpath
java -cp out:lib/* com.myapp.Main

// Module path
java --module-path out:lib --module com.myapp/com.myapp.Main

Module visibility rules:

// Named module on module path
module com.named {
    requires java.base;      // Explicit dependency
    requires java.logging;   // Must be declared
    exports com.named.api;   // Controlled exports
    
    // Can only access:
    // - Exported packages from required modules
    // - Own packages
}

// Automatic module (JAR on module path without module-info.java) 
// - Gets automatic name from JAR filename
// - Exports all packages
// - Requires all other modules
// - Can access all other automatic modules and named modules

// Unnamed module (classpath)
// - Can access all automatic modules and their exported packages
// - Cannot access named modules (unless via automatic modules)
// - All classes in flat namespace

Migration scenarios:

// Scenario 1: Pure modular
// All JARs on module path with module-info.java
java --module-path libs --module com.myapp/com.myapp.Main

// Scenario 2: Mixed (recommended for migration)
// Application is modular, libraries are automatic modules
java --module-path libs --module com.myapp/com.myapp.Main

// Scenario 3: Legacy
// Everything on classpath
java -cp "libs/*:myapp.jar" com.myapp.Main

// Scenario 4: Gradual migration
// Some modules modular, some on classpath
java --module-path modulelibs --class-path legacylibs/* --module com.myapp/com.myapp.Main

Automatic module naming:

// JAR filename -> Automatic module name
"commons-lang3-3.12.jar"        -> "commons.lang3"
"jackson-databind-2.14.jar"     -> "jackson.databind"
"spring-boot-starter-2.7.jar"   -> "spring.boot.starter"
"my-custom-lib-1.0-SNAPSHOT.jar" -> "my.custom.lib"

// Rules:
// 1. Remove version suffix
// 2. Replace non-alphanumeric with dots
// 3. Remove consecutive dots
// 4. Remove leading/trailing dots

Common migration patterns:

// Pattern 1: Top-down migration
module com.myapp {
    // Start with automatic modules for dependencies
    requires commons.lang3;      // commons-lang3.jar (automatic)
    requires jackson.databind;   // jackson-databind.jar (automatic)
    requires spring.boot;        // spring-boot.jar (automatic)
    
    exports com.myapp.api;
}

// Pattern 2: Create module for legacy JAR
// Add module-info.java to existing JAR
module legacy.library {
    exports legacy.library.api;
    requires java.base;
    // Don't export internal implementation packages
}

// Pattern 3: Split large applications
module com.myapp.core {
    exports com.myapp.core.api;
}

module com.myapp.web {
    requires com.myapp.core;
    requires spring.web;
}

module com.myapp.data {
    requires com.myapp.core;
    requires java.sql;
}

💡 Learning Tip: Think “HIGHWAY vs CITY STREETS” - module path is like controlled highway with explicit on/off ramps (requires/exports), classpath is like city streets where everything connects to everything.

Q: Can a named module access classes from the classpath?
A: Not directly - named modules can only access other modules on the module path. However, automatic modules can bridge between named modules and classpath content.