Home  >   Blog  >   Cross-Framework Dependency Injection with spring-guice

2021-10-30

Cross-Framework Dependency Injection with spring-guice

  Support by donation   Gift a cup of coffee


Dependency Injection (DI) is a well-known design pattern that creates and binds dependent objects outside of a class. The technique nicely decouples dependencies from a main application class and enables developers to achieve high testability, maintainability, and extensibility. As I understand, Google Guice and Spring Framework are major DI frameworks in Java.

Unsurprisingly, working with a specific framework among others often causes a compatibility issue. Imagine you are developing an application using Guice for DI. Meanwhile, there is a third-party package that may accelerate your development effort, which actually relies on Spring unlike yours. Here, how can we apply Spring-based injection logic to Guice-based applications? An intermediate tool spring-guice could be a solution in this situation.

Dummy scenario: Machine learning application using Guice

Assume there is a Java-based machine learning framework that provides BaseModel and BaseMetric interface, and you have implemented LogisticRegression model and Recall metric on top of the framework. An ultimate goal for you is to implement the following BinaryClassification application using Guice:

public class BinaryClassification {

   @Inject
   private BaseModel model;

   @Inject
   private BaseMetric metric;

   ...

}

Notice that such abstraction can be commonly seen in the community, and scikit-learn's BaseEstimator is a good example. The actual implementation doesn't follow the DI design pattern in the formal sense though.

In Guice, the injectors can be triggered as:

Injector injector = Guice.createInjector(new ModelModule(), new MetricModule());
BinaryClassification app = injector.getInstance(BinaryClassification.class);

where the modules defining which model/metric to bind are:

public class ModelModule extends AbstractModule {

   @Override
   protected void configure() {
       bind(BaseModel.class).to(LogisticRegression.class);
   }

}
public class MetricModule extends AbstractModule {

   @Override
   protected void configure() {
       bind(BaseMetric.class).to(Recall.class);
   }

}

Leveraging a third-party application using Spring

As a UML diagram, the original implementation can be depicted:

uml

Meanwhile, you found a novel RandomForest implementation for the framework on GitHub, and it seems to be promising as an alternative to your LogisticRegression model. However, unlike your own app, the third-party code depends on Spring to achieve DI and serves the custom application in the form of @Configuration-annotated SpringAppConfig module:

@Configuration
@ComponentScan("org.example.ml.app.spring") // RandomForest is in this path
public class SpringAppConfig {
}

In this situation, spring-guice enables you to easily apply the Spring-based injection logic to your Guice-based client application as follows:

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.guice.module.SpringModule;

AnnotationConfigApplicationContext context =
       new AnnotationConfigApplicationContext(SpringAppConfig.class);

Injector injector = Guice.createInjector(new SpringModule(context), new MetricModule());
BinaryClassification app = injector.getInstance(BinaryClassification.class);

Eventually, the Spring module seamlessly replaces your LogisticRegression with RandomForest in the binary classification app.

Bottom line

"Dependency for Dependency Injection" is sometimes troublesome, especially when your application needs to interact with a number of third-party software that are out of your control. Of course, rewriting original Spring-based code with Guice could be a solution if the third-party implementation is simple enough, but it's not always the case. Hence, knowing one or more bridge tools helps us to consider the trade-off and make a better decision.

Last but not least, spring-guice has additional use cases that aren't covered by my example above, and their README highlights the multiple ways to fulfill the gap.

You can find a complete version of the sample code on GitHub: takuti-sandbox/spring-guice-test

  Share


  Categories

Programming

  See also

2022-06-05
Becoming Permanent Resident of Canada
2020-08-29
What I Think About When I Talk About ML Product
2020-02-07
Why a Data Science Engineer Becomes a Product Manager

  More

Last updated: 2022-09-02

  Author: Takuya Kitazawa

Takuya Kitazawa is a freelance software developer, previously working at a Big Tech and Silicon Valley-based start-up company where he wore multiple hats as a full-stack software developer, machine learning engineer, data scientist, and product manager. At the intersection of technological and social aspects of data-driven applications, he is passionate about promoting the ethical use of information technologies through his mentoring, business consultation, and public engagement activities. See CV for more information, or contact at [email protected].

  Support by donation   Gift a cup of coffee

  Disclaimer

  • Opinions are my own and do not represent the views of organizations I am/was belonging to.
  • I am doing my best to ensure the accuracy and fair use of the information. However, there might be some errors, outdated information, or biased subjective statements because the main purpose of this blog is to jot down my personal thoughts as soon as possible before conducting an extensive investigation. Visitors understand the limitations and rely on any information at their own risk.
  • That said, if there is any issue with the content, please contact me so I can take the necessary action.