Java Development Frameworks

There is a lot of development frameworks for Java. This post talks about Guice (for Dependency Injection design pattern implementation), Spring (Java integration environment), JUnit and Mockito (for unit test).

Dependency Injection (DI)

DI is design pattern. When an object A depends on another object B, DI means to pass a built object B to object A from outside of A instead of letting object A create the object B.

Using DI pattern to organize code could decouple the classes and make it eaiser for testing. There are some libraries and framework could help to implement the DI pattern. e.g. Guice

Guice

Guice is Googal’s framework to let your application use DI pattern. To use Guice, you needs to following elements.

  • Java class constructors that are annoated with @Inject
  • Guice modules where binding the dependency class to the instance or other subclass without dependency. Module class needs to extend AbstractModule
  • Guice injector created based on the Guice modules in the main function. Then the main function could use the injector to get the instance of an object. The injector will resolve the dependencies according to the configuration.

Concepts

  • Key: to identify a dependency that can be resolved
    • e.g. @Message String is a kind of Key<String>
  • Provider: represent factories that are capable of creating objects to satisfy dependency requirements
    • e.g. interface Provider<T> { T get(); }

The Guice module is a collections of Map<Key, Provider>, which means it bind a lot of dependencies with real objects.

@Inject to mark required injection

The @Inject annotation could describe a class’s constructor, function, or field. Once the content is annotated by @Inject, Guice could inject the dependency to the class automatically.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Test {
@Inject
private DataType data1;

private DataType data2;
@Inject
public void setData2(DataType data2) {
this.data2 = data2;
}

private DataType data3;
@Inject
Test(DataType data3) {
this.data3 = data3;
}
}

The data1, data2, and data3 could be injected by Guice when the class Test is institated, and there is no need to use code to set those field to class Test

Guice Module class

Guice Module is a class whose name ends with Module. In this class, those classes used as dependency of other classes are binded to a specific object. Thus when those classes have dependencies of others (and have been annoated by Inject), Guice can use this Guice Module class to provide a built object to the classes.

Use bind function to bind a class to other class

  • Usually bind a base class / interface to a sub class without dependency or an instance
  • Could use annotation to classify different class. So that the same class could be bonded to different target
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// Note that the BaseClass is the dependency of a class. SubClass is the type 
// of the object passed to that class. SubClass has no dependency or its
// dependency has been bind in the Guice Module

bind(BaseClass.class)
.to(SubClass.class)

bind(BaseClass.class)
.annotatedWith(Names.named("MyAnnotation"))
.to(SubClass.class)

@BindingAnnotation // or @Qualifier
@Retention(RUNTIME)
public @interface MyAnnotation {}

bind(BaseClass.class)
.annotatedWith(MyAnnotation.class)
.to(SubClass.class)

Use @Provides

1
2
3
4
5
// This bind the int class to the xxx
@Provides
int getSomething() {
return xxx;
}

Use Provider

1
2
3
4
5
6
7
8
9
10
11
bind(int.class).toProvider(MyCustomizedProvider)

public MyCustomizedProvider implements Provider<T> {
MyCustomizedProvide() {
//...
}

T get() {
// ....
}
}

Use Guice to get instance of class

1
2
3
4
Injector inject = createInjector(
new XXXModule()
);
DataType A = injector.getInstance(DataType.class);

If DataType has dependencies of other classes, injector will check whether the class of dependencies have been bind to a specific object. If it is, then pass that object to DataType

Unit Test

Unit test is the test for method of classes. It guides the development process and this kind of process is known as Test-driven Development (TDD).

JUnit is an open-source framework for unit test

JUnit

JUnit uses the following annotations to comment functions to achieve different functions

  • @Test: indiciate the function is a unit test
  • @ParameterizedTest: give parameters to the test case
    • @MethodSource: indicate using the static function to get parameters
    • @ValueSource(): indicate the parameters
    • @CsvSource()
    • @CscFileSource()
  • Some conditions used with @Test
    • @Disabled: let JUnit skip this test case
    • @EnableOnOs()
    • @EnableOnJre()
  • @Before: do actions before performing the tests
    • @BeforeEach
    • @BeforeAll
  • @After: do tear down work after performing the tests
    • @AfterEach
    • @AfterAll

In unit test function, Assertion is often used

  • assertEquals()
  • assertTrue()
  • assertFalse()
  • assertThrows()

Mockito

  • Mock objects or functions for unit test
    • Mock dummy objects
    • Mock the return value of a function
  • Can work with JUnit

Mock Static Method

  1. Add @PrepareForTest at class level
  2. Call PowerMockito.mockStatic() for mocking static class; Call PowerMockito.spy() for mocking a specific method in the class
  3. Use Mockito.when().thenReturn() to mock specific method

Maven

A tool for Java project management and construction. It provides

  • Standard project structure
  • Standard construction pipeline (compile, test, package, release)
  • Dependency Management

Maven Project Structure

The pom.xml file includes the configuration of the project. The most important thing is the groupId, artifactId, and version.

Construction Piepline

Maven has a lifecycle to mark the status of the project. The lifecycle includes several phase, from validate to compile and to test, etc.

Using mvn command could let the project go into specific phase.

Dependency Management

Maven could resolve the dependency of packages and download the jar packages automatically. The dependencies includes the following scopes

  • compile: require the package during compile
  • test: require the packakge during test
  • runtime: require the package during runtime but not in compile
  • provided: require the package during compile but not in runtime

Spring

Spring is a light development framework. It has the following components

  • Core: DI, AOP (Aspect-Oriented Programming), events, resources
  • Test: TestContext
  • Data Access: JDBC, ORM, OXM
  • Web: Spring MVC (MVC: Model, View, Controller), Spring WebFlux Web, Web Socket
  • Integration:
  • Language: Kotlin, Groovy