I have been working with
Netflix Governator for the last few days and got to try out a small sample using Governator as a way to compare it with the dependency injection feature set of Spring Framework. The following is by no means comprehensive, I will expand on this in the next series of posts.
So Governator for the uninitiated is an extension to
Google Guice enhancing it with some Spring like features, to quote the Governator site:
classpath scanning and automatic binding, lifecycle management, configuration to field mapping, field validation and parallelized object warmup.
Here I will demonstrate two features, classpath scanning and automatic binding.
Basic Dependency Injection
Consider a BlogService, depending on a BlogDao:
public class DefaultBlogService implements BlogService {
private final BlogDao blogDao;
public DefaultBlogService(BlogDao blogDao) {
this.blogDao = blogDao;
}
@Override
public BlogEntry get(long id) {
return this.blogDao.findById(id);
}
}
If I were using Spring to define the dependency between these two components, the following would be the configuration:
package sample.spring;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import sample.dao.BlogDao;
import sample.service.BlogService;
@Configuration
public class SampleConfig {
@Bean
public BlogDao blogDao() {
return new DefaultBlogDao();
}
@Bean
public BlogService blogService() {
return new DefaultBlogService(blogDao());
}
}
In Spring, the dependency configuration is specified in a class annotated with @Configuration annotation. The methods annotated with @Bean return the components, note how the blogDao is being injected through constructor injection in blogService method.
A unit test for this configuration is the following:
package sample.spring;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import sample.service.BlogService;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
public class SampleSpringExplicitTest {
@Test
public void testSpringInjection() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(SampleConfig.class);
context.refresh();
BlogService blogService = context.getBean(BlogService.class);
assertThat(blogService.get(1l), is(notNullValue()));
context.close();
}
}
Note that Spring provides good support for unit testing, a better test would be the following:
package sample.spring;
package sample.spring;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import sample.service.BlogService;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SampleSpringAutowiredTest {
@Autowired
private BlogService blogService;
@Test
public void testSpringInjection() {
assertThat(blogService.get(1l), is(notNullValue()));
}
@Configuration
@ComponentScan("sample.spring")
public static class SpringConig {
}
}
This is basic dependency injection, so to specify such a dependency Governator itself is not required, Guice is sufficient, this is how the configuration would look using Guice Modules:
package sample.guice;
import com.google.inject.AbstractModule;
import sample.dao.BlogDao;
import sample.service.BlogService;
public class SampleModule extends AbstractModule{
@Override
protected void configure() {
bind(BlogDao.class).to(DefaultBlogDao.class);
bind(BlogService.class).to(DefaultBlogService.class);
}
}
and a Unit test for this configuration is the following:
package sample.guice;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.junit.Test;
import sample.service.BlogService;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.MatcherAssert.*;
public class SampleModuleTest {
@Test
public void testExampleBeanInjection() {
Injector injector = Guice.createInjector(new SampleModule());
BlogService blogService = injector.getInstance(BlogService.class);
assertThat(blogService.get(1l), is(notNullValue()));
}
}
Classpath Scanning and Autobinding
Classpath scanning is a way to detect the components by looking for markers in the classpath. A sample with Spring should clarify this:
@Repository
public class DefaultBlogDao implements BlogDao {
....
}
@Service
public class DefaultBlogService implements BlogService {
private final BlogDao blogDao;
@Autowired
public DefaultBlogService(BlogDao blogDao) {
this.blogDao = blogDao;
}
...
}
Here the annotations @Service, @Repository are used as markers to indicate that these are components and the dependencies are specified by the @Autowired annotation on the constructor of the DefaultBlogService.
Given this the configuration is now simplified, we just need to provide the package name that should be scanned for such annotated components and this is how a full test would look:
package sample.spring;
...
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class SampleSpringAutowiredTest {
@Autowired
private BlogService blogService;
@Test
public void testSpringInjection() {
assertThat(blogService.get(1l), is(notNullValue()));
}
@Configuration
@ComponentScan("sample.spring")
public static class SpringConig {}
}
Governator provides a similar kind of a support:
@AutoBindSingleton(baseClass = BlogDao.class)
public class DefaultBlogDao implements BlogDao {
....
}
@AutoBindSingleton(baseClass = BlogService.class)
public class DefaultBlogService implements BlogService {
private final BlogDao blogDao;
@Inject
public DefaultBlogService(BlogDao blogDao) {
this.blogDao = blogDao;
}
....
}
Here, @AutoBindSingleton annotation is being used as a marker annotation to define the guice binding, given this a test with classpath scanning is the following:
package sample.gov;
import com.google.inject.Injector;
import com.netflix.governator.guice.LifecycleInjector;
import com.netflix.governator.lifecycle.LifecycleManager;
import org.junit.Test;
import sample.service.BlogService;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.notNullValue;
public class SampleWithGovernatorTest {
@Test
public void testExampleBeanInjection() throws Exception {
Injector injector = LifecycleInjector
.builder()
.withModuleClass(SampleModule.class)
.usingBasePackages("sample.gov")
.build()
.createInjector();
LifecycleManager manager = injector.getInstance(LifecycleManager.class);
manager.start();
BlogService blogService = injector.getInstance(BlogService.class);
assertThat(blogService.get(1l), is(notNullValue()));
}
}
See how the package to be scanned is specified using a LifecycleInjector component of Governator, this autodetects the components and wires them together.
Just to wrap the classpath scanning and Autobinding features, Governator like Spring provides a support for junit testing and a better test would be the following:
package sample.gov;
import com.google.inject.Injector;
import com.netflix.governator.guice.LifecycleTester;
import org.junit.Rule;
import org.junit.Test;
import sample.service.BlogService;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
public class SampleWithGovernatorJunitSupportTest {
@Rule
public LifecycleTester tester = new LifecycleTester();
@Test
public void testExampleBeanInjection() throws Exception {
tester.start();
Injector injector = tester
.builder()
.usingBasePackages("sample.gov")
.build()
.createInjector();
BlogService blogService = injector.getInstance(BlogService.class);
assertThat(blogService.get(1l), is(notNullValue()));
}
}
Conclusion
If you are interested in exploring this further I have a sample in
this github project, I would be expanding this project as I learn more about Governator