Requires, Exports, and Opens Directives in Java

Comprehensive guide to Java Module System directives for controlling module dependencies and accessibility.

1. Module System Overview

// Basic module structure module com.example.myapp { requires java.base; // Dependency on other modules requires java.sql; exports com.example.api; // Public API exports com.example.util to com.example.test; opens com.example.model; // Reflection access opens com.example.internal to spring.core; uses com.example.spi.ServiceProvider; provides com.example.spi.ServiceProvider with com.example.impl.DefaultServiceProvider; }

2. Requires Directive

Basic Requires

// Simple dependencies module com.example.persistence { requires java.sql; // Compile-time and runtime dependency requires java.logging; // JDK logging requires java.transaction.xa; // XA transactions } module com.example.web { requires com.example.persistence; // Our own module requires java.net.http; // HTTP client requires jdk.httpserver; // HTTP server }

Requires Static

// Optional dependencies - compile-time only module com.example.optional.features { requires static java.xml; // Optional XML support requires static jdk.compiler; // Optional compiler API requires static com.thirdparty.lib; // Optional 3rd party } // Usage with conditional code public class OptionalFeature { public void processXml(String xml) { // Check if XML module is available at runtime Module xmlModule = ModuleLayer.boot().findModule("java.xml").orElse(null); if (xmlModule != null) { // Use XML features processWithXml(xml); } else { // Fallback implementation processWithoutXml(xml); } } private void processWithXml(String xml) { try { // XML processing code that will only be called if module is present } catch (NoClassDefFoundError e) { // Handle missing optional dependency } } }

Requires Transitive

// Module that exposes dependencies to its consumers module com.example.framework { requires transitive java.sql; // Consumers get java.sql automatically requires transitive java.logging; // Consumers get logging automatically requires com.internal.lib; // Internal dependency, not exposed exports com.example.framework.api; } // Consumer module - automatically gets transitive dependencies module com.example.myapp { requires com.example.framework; // Implicitly gets java.sql and java.logging // No need to explicitly require java.sql or java.logging exports com.example.myapp.ui; }

Complete Requires Example

// Comprehensive module with different require types module com.example.enterprise.app { // Standard dependencies requires java.base; // Implicit, but explicit for clarity // Runtime dependencies requires java.sql; requires java.naming; // JNDI requires java.management; // JMX // Optional features requires static java.xml.bind; // JAX-B (optional) requires static java.compiler; // Annotation processing (optional) // Framework dependencies with transitive exposure requires transitive com.example.security; requires transitive com.example.logging; // Internal implementation dependencies (not exposed) requires com.example.internal.utils; requires com.example.internal.cache; exports com.example.enterprise.app.api; exports com.example.enterprise.app.spi; }

3. Exports Directive

Basic Exports

// Module exposing public API module com.example.library { requires java.base; // Public API packages exports com.example.library.core; exports com.example.library.api; exports com.example.library.util; // Internal packages - not exported // com.example.library.internal.* is hidden } // Public API classes package com.example.library.core; /** * Public class available to all modules */ public class LibraryCore { public void publicMethod() { /* ... */ } public static final String VERSION = "1.0"; } package com.example.library.api; public interface Service { void execute(); } // Internal implementation - not exported package com.example.library.internal; class InternalImplementation { // Package-private - not accessible outside void internalMethod() { /* ... */ } }

Qualified Exports

// Selective exporting to specific modules module com.example.security { requires java.base; requires transitive java.security; // Public API for all consumers exports com.example.security.api; // Internal API only for specific framework modules exports com.example.security.internal to com.example.framework, com.example.integration, org.springframework.core; // SPI for service providers exports com.example.security.spi to com.example.security.providers; } // Framework module that needs internal access module com.example.framework { requires com.example.security; // Gets both api and internal (via qualified export) exports com.example.framework.core; } // Regular application - only gets public API module com.example.myapp { requires com.example.security; // Only gets com.example.security.api // CANNOT access com.example.security.internal }

Export Examples with Different Scenarios

// Multi-layer architecture module module com.example.layered.app { requires java.base; requires java.sql; // Public facade layer exports com.example.layered.app.facade; // Service layer - only for specific business modules exports com.example.layered.app.service to com.example.business.module1, com.example.business.module2; // DAO layer - only for service implementation exports com.example.layered.app.dao to com.example.layered.app.service.impl; // Internal utilities - not exported at all // com.example.layered.app.internal.* } // API and implementation separation module com.example.service.api { exports com.example.service.api; exports com.example.service.spi; } module com.example.service.impl { requires com.example.service.api; requires transitive com.example.persistence; // Implementation details not exported // Only visible to the API module through services }

4. Opens Directive

Basic Opens for Reflection

// Module allowing reflection on specific packages module com.example.persistence.entity { requires java.persistence; // Allow full reflection access to entity packages opens com.example.persistence.entity.model; opens com.example.persistence.entity.audit; // Public API for compile-time access exports com.example.persistence.entity.api; } // Entity classes that need reflection access package com.example.persistence.entity.model; @Entity @Table(name = "users") public class User { @Id private Long id; @Column(name = "username") private String username; // Private fields accessed via reflection by JPA providers private String password; // Getters and setters will be called via reflection public Long getId() { return id; } public void setId(Long id) { this.id = id; } }

Qualified Opens

// Selective reflection access to specific frameworks module com.example.spring.app { requires java.base; requires static spring.core; requires static spring.context; requires static hibernate.core; // Public API exports com.example.spring.app.service; exports com.example.spring.app.repository; // Reflection access for specific frameworks only opens com.example.spring.app.entity to spring.core, spring.beans, spring.context, hibernate.core, org.hibernate.orm; opens com.example.spring.app.config to spring.core, spring.context; // Internal packages - no reflection access // com.example.spring.app.internal.* } // Configuration class needing reflection package com.example.spring.app.config; @Configuration @ComponentScan public class AppConfig { @Bean public DataSource dataSource() { // Spring will call this via reflection return new HikariDataSource(); } @Bean @Profile("dev") public Service devService() { // Reflection access needed for profile annotation return new DevService(); } }

Opens vs Exports Difference

module com.example.demonstration { exports com.example.demonstration.api; // Compile-time access opens com.example.demonstration.model; // Runtime reflection access // Package with both export and open exports com.example.demonstration.shared; opens com.example.demonstration.shared; // Both compile-time and reflection } // Test demonstrating the difference public class AccessTest { public void testExports() { // This works - exported package PublicClass obj = new PublicClass(); obj.publicMethod(); // This works - shared package (exported) SharedClass shared = new SharedClass(); shared.publicMethod(); } public void testReflection() throws Exception { // This works - opened package Class<?> modelClass = Class.forName("com.example.demonstration.model.ModelEntity"); Field privateField = modelClass.getDeclaredField("privateData"); privateField.setAccessible(true); // Allowed because package is opened // This works - shared package (opened) Class<?> sharedClass = Class.forName("com.example.demonstration.shared.SharedEntity"); Field sharedField = sharedClass.getDeclaredField("internalState"); sharedField.setAccessible(true); // Allowed because package is opened // This would fail - api package is only exported, not opened // Class<?> apiClass = Class.forName("com.example.demonstration.api.ApiClass"); // Field apiField = apiClass.getDeclaredField("internalField"); // apiField.setAccessible(true); // Would throw IllegalAccessException } }

5. Real-world Module Examples

Enterprise Application Structure

// Domain module - core business entities module com.enterprise.domain { requires java.base; // Public API exports com.enterprise.domain.model; exports com.enterprise.domain.service; exports com.enterprise.domain.event; // Reflection for serialization and ORM opens com.enterprise.domain.model to com.fasterxml.jackson.databind, org.hibernate.orm, spring.core; // SPI for domain extensions exports com.enterprise.domain.spi; } // Persistence module - data access module com.enterprise.persistence { requires transitive com.enterprise.domain; requires java.sql; requires java.persistence; requires transitive org.hibernate.orm; exports com.enterprise.persistence.repository; exports com.enterprise.persistence.dao; opens com.enterprise.persistence.entity to org.hibernate.orm, spring.data.jpa; uses com.enterprise.domain.spi.RepositoryProvider; } // Service module - business logic module com.enterprise.service { requires transitive com.enterprise.domain; requires transitive com.enterprise.persistence; requires java.transaction; requires static spring.context; exports com.enterprise.service.facade; exports com.enterprise.service.manager; opens com.enterprise.service.impl to spring.core, spring.context; } // Web module - REST API module com.enterprise.web { requires transitive com.enterprise.service; requires java.net.http; requires jaxrs.api; requires static spring.web; requires static jackson.databind; exports com.enterprise.web.controller; exports com.enterprise.web.dto; opens com.enterprise.web.controller to spring.web, jaxrs.api; opens com.enterprise.web.dto to jackson.databind, spring.web; }

Framework Integration Example

// Spring Boot application module module com.example.bootapp { requires java.base; requires static spring.boot; requires static spring.boot.autoconfigure; requires static spring.context; requires static spring.web; requires static spring.data.jpa; requires transitive com.example.domain; requires transitive com.example.service; // Export main application class exports com.example.bootapp; // Open everything for Spring Boot reflection opens com.example.bootapp to spring.core, spring.context, spring.beans, spring.boot; opens com.example.bootapp.config to spring.core, spring.context; opens com.example.bootapp.controller to spring.web, spring.core; // Open domain entities for JPA opens com.example.domain.model to spring.data.jpa, org.hibernate.orm, jackson.databind; // Open service layer for dependency injection opens com.example.service.impl to spring.core, spring.context; uses org.springframework.boot.SpringApplication; }

6. Advanced Patterns and Techniques

Optional Dependencies Pattern

// Module with multiple optional features module com.example.pluggable.features { requires java.base; // Core functionality exports com.example.pluggable.core; // Optional features requires static com.example.feature.email; requires static com.example.feature.sms; requires static com.example.feature.push; // Service loader for feature detection uses com.example.pluggable.spi.FeatureProvider; } // Service interface for optional features package com.example.pluggable.spi; public interface FeatureProvider { String getName(); boolean isAvailable(); void execute(); } // Feature detection and usage package com.example.pluggable.core; public class FeatureManager { public void executeAvailableFeatures() { ServiceLoader<FeatureProvider> loader = ServiceLoader.load(FeatureProvider.class); for (FeatureProvider feature : loader) { if (feature.isAvailable()) { try { feature.execute(); } catch (Exception e) { // Log but continue with other features System.err.println("Feature failed: " + feature.getName()); } } } } }

Module Configuration Pattern

// Configurable module with different profiles module com.example.configurable.app { requires java.base; requires static com.example.config.profile.dev; requires static com.example.config.profile.prod; requires static com.example.config.profile.test; exports com.example.configurable.app.api; opens com.example.configurable.app.config to spring.core, spring.context; } // Configuration resolver public class ProfileResolver { private static final String ACTIVE_PROFILE = System.getProperty("app.profile", "dev"); public static boolean isProfileActive(String profile) { Module module = ProfileResolver.class.getModule(); switch (profile) { case "dev": return module.canRead( ModuleLayer.boot().findModule("com.example.config.profile.dev").orElse(null)); case "prod": return module.canRead( ModuleLayer.boot().findModule("com.example.config.profile.prod").orElse(null)); case "test": return module.canRead( ModuleLayer.boot().findModule("com.example.config.profile.test").orElse(null)); default: return false; } } }

7. Migration and Compatibility

Automatic Module Migration

// Before: Classpath-based JARs // After: Automatic modules // Non-modular JARs become automatic modules // JAR name: my-library-1.0.0.jar → module name: my.library module com.example.migrating.app { // Automatic modules from legacy JARs requires commons.lang; requires guava; requires slf4j.api; // Regular modules requires java.sql; requires com.example.framework; exports com.example.migrating.app; // Open for reflection (common in migration) opens com.example.migrating.app.model; opens com.example.migrating.app.dto; }

Mixed Mode Configuration

// module-info.java for incremental migration module com.example.incremental.migration { // Start with minimal requires requires java.base; // Add dependencies as they become modularized requires java.sql; requires java.logging; // Automatic modules for non-modularized dependencies requires legacy.database.driver; requires old.validation.framework; // Export only what's necessary exports com.example.incremental.migration.api; // Open packages for reflection during migration opens com.example.incremental.migration.entity; opens com.example.incremental.migration.dto; opens com.example.incremental.migration.util; // Gradually reduce opens as code is refactored }

8. Troubleshooting and Common Issues

Common Problems and Solutions

// Problem: IllegalAccessError at runtime module com.example.problem.app { requires java.base; exports com.example.problem.app.api; // Missing: opens com.example.problem.app.model for reflection } // Solution: Add opens directive module com.example.solution.app { requires java.base; exports com.example.solution.app.api; opens com.example.solution.app.model to spring.core, hibernate.core; } // Problem: Module not found module com.example.dependency.issue { requires java.base; requires non.existent.module; // Compilation error } // Solution: Check module names and dependencies module com.example.dependency.fixed { requires java.base; requires correct.module.name; requires com.valid.module.name; }

Diagnostic Tools and Techniques

// Module inspection utility public class ModuleInspector { public static void printModuleInfo(Class<?> clazz) { Module module = clazz.getModule(); System.out.println("Module: " + module.getName()); System.out.println("Named: " + module.isNamed()); System.out.println("Exports: " + module.getPackages()); // Check readability module.getLayer().configuration().modules().forEach(m -> { if (module.canRead(m)) { System.out.println("Reads: " + m.name()); } }); } public static void checkReflectionAccess(Class<?> targetClass) { try { targetClass.getDeclaredFields(); System.out.println("Reflection access allowed for: " + targetClass); } catch (InaccessibleObjectException e) { System.out.println("Reflection access denied for: " + targetClass); System.out.println("Package opened: " + targetClass.getModule().isOpen(targetClass.getPackageName())); } } }

Key Directives Summary

DirectivePurposeUsage Example
requiresModule dependencyrequires java.sql;
requires transitiveTransitive dependencyrequires transitive com.example.core;
requires staticOptional dependencyrequires static java.xml;
exportsPublic APIexports com.example.api;
exports toQualified exportexports com.example.internal to framework;
opensReflection accessopens com.example.model;
opens toQualified opensopens com.example.entity to hibernate;

These directives provide fine-grained control over module dependencies, API exposure, and reflection access, enabling strong encapsulation and better modular architecture in Java applications.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper