Monday, January 19, 2015

Learning Netflix Governator - Part 1

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

1 comment:

  1. @AutoBindSingleton was deprecated:

    @AutoBindSingleton is deprecated in favor of using bindings in Guice modules. @AutoBindSingleton has been the source of numerous problems since it frequently results in unwanted classes being instantiated. Furthermore, @AutoBindSingleton is misleading since it is not a proper Guice scope. For example, any @AutoBindSingleton annotated class that does not also have the @Singleton annotation does not exhibit singleton behavior when that class was not pulled in via classpath scanning.

    ReplyDelete