Lesson 3 - Java Server - Google Guice
In the previous lesson, Java Server - Server Parameters, we looked at server parameters. Today
we're going to implement dependency management using the Google guice library.
The result of our efforts will be to get an instance implementing
IParameterProvider
using Google guice. In the future, we'll acquire
all instances like this.
Google guice
If you have no idea what dependency injection is, I suggest you read the local DI course carefully. To understand Google guice itself, I recommend the following video series.
In short, it's a library that will create class instances for us and take care of passing them across the application to places where we need them.
Adding a Dependency
We'll start by adding a dependency to our project, specifically to the
server
module. Open the build.gradle
file and add the
following dependency to the dependencies
block:
implementation group: 'com.google.inject', name: 'guice', version: '4.2'
Parameter Factory
From the previous lesson we got the CmdParser
class and
IParameterProvider
interface. Now, let's show how to create an
instance implementing IParameterProvider
by using Google guice. We
need to use a factory
to create the instance, because the CmdParser
class has a parameter
in its constructor that we must pass from the "outside". In the cmd
package we'll create a new interface named IParameterFactory
. This
interface will have one method that will create a concrete instance with the
IParameterProvider
interface:
package cz.stechy.chat.cmd; public interface IParameterFactory { IParameterProvider getParameters(String[] args); }
Next we'll implement this factory as a ParameterFactory
class:
package cz.stechy.chat.cmd; import com.google.inject.Singleton; @Singleton public class ParameterFactory implements IParameterFactory { @Override public IParameterProvider getParameters(String[] args) { return new CmdParser(args); } }
The implementation of the getParameters()
method is very simple.
It just creates and returns a new CmdParser
class instance.
Configuring guice
Now let's configure Google guice to map instances correctly. We'll create a
new ServerModule
class that will inherit from the
com.google.inject.AbstractModule
class and implement a single
method in it: configure()
. The class is not in the cmd
package, but one package up.
package cz.stechy.chat; import com.google.inject.AbstractModule; import cz.stechy.chat.cmd.IParameterFactory; import cz.stechy.chat.cmd.ParameterFactory; public class ServerModule extends AbstractModule { @Override public void configure() { bind(IParameterFactory.class).to(ParameterFactory.class); } }
This is the class doing all the magic, mapping implementations to the
corresponding interfaces. In our case we have only one mapping here, which is
the implementation of the IParameterFactory
interface by the
ParameterFactory
class.
Main class
Finally, we'll create the application's startup class. So we'll create a new
Server
class. Place it in the same folder as
ServerModule
.
Next we'll create one instance constant of the IParameterFactory
type in the class:
private final IParameterFactory parameterFactory;
The class constructor will have one parameter, the
IParameterFactory
interface:
@Inject public Server(IParameterFactory parameterFactory) { this.parameterFactory = parameterFactory; }
Notice the @Inject
annotation. This annotation comes from the
Google guice library and says that the constructor parameters are the
dependencies guice will pass automatically.
Then we'll create a private run()
instance method, which will be
the entry point of the application:
private void run(String[] args) { final IParameterProvider parameters = parameterFactory.getParameters(args); System.out.println("Maximum number of clients: " + parameters.getInteger(CmdParser.CLIENTS)); }
For now, we'll just write the information about the maximum number of clients to the console.
Finally, we'll create a static main()
method to create an
injector, get a server class instance, and run the run()
method
above:
public static void main(String[] args) throws Exception { final Injector injector = Guice.createInjector(new ServerModule()); Server server = injector.getInstance(Server.class); server.run(args); }
Launching the Application
To start the application, we'll use the gradient command:
gradlew server:run -Parg=-port=6298,-clients=5,-max_waiting_queue=10
The result should be number 5
, which corresponds to the maximum
number of clients.
If everything works for you, congratulations, you've just implemented dependency injection using the Google guice library. In case of problems, you can look in the attached archive, which is available for download below the article, and find your mistake. Next time, in the lesson Java Server - Server Thread, we'll create a code template for the server thread.