JavaData & JavaDealing with Module Dependency in Java 9

Dealing with Module Dependency in Java 9

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

With the introduction of Java 9, more and more Java projects are going to be designed modular from its core. Modules never work in isolation, although they may yet they work in relationship with other modules. In other words, these relationships can be called module dependency, where one module requires another to deliver its full functionality. This relationship between modules id established in a manner that the module that exposes its API does it explicitly so that another module can use it. Because a module now becomes the top-level paradigm of encapsulation, developers need to encompass this new consideration in program design. This article glimpses at some of the key aspects of module dependency and how it is realized in Java programming.

Module Dependency

Creating a dependency is like creating a relationship among modules. The public types of the module must be explicitly exposed to enable other modules to access it. This exposure can be open to all, or it can be exclusive to some modules only. The relationship, therefore, is that Module one exports the public content by explicitly expressing it in the module-info.java file and Module two reads them by virtue of permission given by Module one. Note that, because a module (as a programming element) supports top-level construct, a level above package, developers typically organize their code encapsulated into modules and declare dependencies among them with their respective module definition files. This file is named modules-info.java.

Module Definition

The module definition files, aka modules-info.java, typically contain the following information:

  • Name
  • Information about publicly available packages
  • Other modules that it depends on
  • Services consumed by it
  • Service implementation that it provides

Implementing Module Dependency

Suppose that a simple application is implemented as having three modules: a model module that represents the entity, a service module that represents the services provided for data interaction with the back-end database, and the client module that represents the application interface. As should be obvious, they have a dependency upon one another; here you’ll see how the relationship design is drawn. Also, because the focus will be on the idea of modular design, the code given is barebone and minimal.

What you need is JDK 9 and the IntelliJ IDEA IDE. (Modular programming can be done without using any IDE; an IDE is used simply for convenience. As of writing this article, the author did not find any other IDE that has out-of-the-box support for Java 9 modularity, although many are catching up. Some have third-party plug-ins only as an alternative for now.) Here’s how to get going:

  1. Open the Idea IDE and click File, New, Project…
  2. Select Empty Project and then click Next.
  3. Give a name of the project and click Finish.

This creates a project space in the IDE where we’ll add one or more modules. In the Project Structure item, under the File menu, make sure that the settings are as shown in Figure 1:

Creating a project space in the IDE
Figure 1: Creating a project space in the IDE

One can add modules directly into the Project Settings window, as you can see in Figure 2, or add them later from the File menu through the New –> Module… option.

Adding modules
Figure 2: Adding modules

Once this is done, the project structure in IntelliJ IDEA IDE might look like what’s shown in Figure 3:

The completed project structure
Figure 3: The completed project structure

Now, create a package with the same name as the module name under the respective src directory and, inside that package, create one or more classes, interfaces, sub packages, any Java type, and so forth. This is pretty usual and similar to any Java programming. But, because here we are using modules, the packages encompassed within the modules must be exposed explicitly so that other modules can read them. For example, the service module obviously uses the model module. Therefore, the model module must explicitly expose its public content type in a file names module-info.java. In fact, every module has its own module-info.java file (see Figure 4).

Every module has its own info file
Figure 4: Every module has its own info file

In this regard, it is worth mentioning that, according to The State of the Module System by Mark Reinhold: Module names, like package names, must not conflict. The recommended way to name a module is to use the reverse-domain-name pattern that has long been recommended for naming packages. The name of a module will, therefore, often be a prefix of the names of its exported packages, but this relationship is not mandatory.

The model-info.java file can be added by right-clicking the src directory icon of the project window in the IDEA IDE. The module-info.java file of the model is as follows:

module org.mano.model {
   exports org.mano.model;
}

This exports all the public types under the package org.mano.model to all other modules. The syntax is:

module <module-name> {
   export <package>;
}

The export of APIs can be made exclusive to a specific package with a qualified export statement like this:

module <module-name> {
   export <package1> to <package2>;
}

If one module has dependency on another module and wants to read the exported API of that module, it is stated in this manner:

module <module-name> {
   requires <package>;
}

Suppose we want a module to declare the services it provides but not define them, or we want the actual implementation or definition in some other module. We can state them as follows, such that the implementer actually enhances the declaration.

module <module-name> {
   provides <package1.ServiceName> with
      <package2.ServiceNameImpl>;
}

Note that the package name should be followed by the full-qualified type name.

The consumer module, however, needs to register as a consumer of services using a full-qualified name of the interface as follows:

module <module-name> {
   requires <package1> uses <package1.ServiceName>;
}

The Example Continues…

Our barebones project has three modules that separate each others’ concern yet are dependent upon one another. The classes and packages implementations are simple and usual. The module definition file elaborates the real deal of module dependency. Each module has its own file name: module-info.java.

The first module, represented as a model, exports all its public types to any other module interested in reading it.

module org.mano.model {
   exports org.mano.model;<
}
package org.mano.model;
public class User {
   private long userId;
   private String password;
   private String email;
   private String phone;
   public User() {
   }
   public User(long uid, String pass,
         String em, String ph) {
      userId = uid;
      password = pass;
      email = em;
      phone = ph;
   }

   // Getters and setters

   public String toString() {
      return userId + " " + password + " " +
         email + " " + phone;
   }
}

The second module provides the services associated with the model. Hence, it needs the exported APIs of the model module. This means that, if a module reads another module, the module that reads must have a requires statement in its module definition file. There are two optional modifiers that may be used with requires statement: static and transitive. The static modifier states that the module is dependent at compile-time and optional at run-time. The transitive modifier states implied readability which means:

“…extend module declarations so that one module can grant readability to additional modules, upon which it depends, to any module that depends upon it. Such implied readability is expressed by including the transitive/public modifier in a requires clause.” [The State of the Module System] (Initially, it was named as a public modifier; later, it was changed to transitive modifier.)

This also implies that we can use these two modifiers in any way we like. Therefore, the following is valid syntax:

module <module-name> {
   requires static transitive <package>;
}

Here, we have made the service module dependent on the model module to form a transitive relationship: aRb, bRc, aRc.

module org.mano.service {
   requires transitive org.mano.model;
   exports org.mano.service;
   exports org.mano.service.org.mano.service.impl;
}

package org.mano.service;
import org.mano.model.User;
public interface UserService {
   public void addNew(User user);
   public void update(User user);
   public void delete(long id);
}
package org.mano.service.org.mano.service.impl;
import org.mano.model.User;
import org.mano.service.UserService;
public class UserServiceImpl implements UserService {
   @Override
   public void> addNew(User user) {
      System.out.println("Add new "+user.toString());
   }
   @Override
   public void update(User user) {
      System.out.println("Update "+user.toString());
   }
   @Override
   public void delete(long id) {
      System.out.println("Id "+id);
   }
}

The client module implies the interface for user interaction. Also, it uses the exposed APIs of the other two modules.

module org.mano.client {
   requires org.mano.service;
   requires org.mano.model;
}
package org.mano.client;
import org.mano.service.UserService;
import org.mano.service.org.mano.service.impl.UserServiceImpl;
import org.mano.model.User;
public class Main {
   public static void main(String[] args){
      UserService us=new UserServiceImpl();
      us.addNew(new User(1234,"AAA","aa@gmail.com",
         "1029384756"));
      us.update(new User(1234,"BBB","bb@gmail.com",
         "9876543210"));
      us.delete(1234);
   }
}

Conclusion

This is the basic idea about module dependency in a very simple layout. This idea of the sample project may be used to build a full-fledged Java modular application. Dependency in a production-ready application is, however, quite complex, in the sense that there are many dependencies with a heavily coded module-info.java file. There are some other concepts with regard to the use of Reflection in module dependency. These has been left out in this article for simplicity; we’ll take them up later.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories