Я работал с Netflix Governator в течение последних нескольких дней, и мне пришлось опробовать небольшой пример, используя Governator, чтобы сравнить его с набором функций внедрения зависимостей в Spring Framework. Нижеследующее ни в коем случае не является исчерпывающим, я остановлюсь на этом в следующей серии постов.
Таким образом, губернатор для непосвященных — это расширение для Google Guice, которое дополняет его некоторыми функциями, подобными Spring, цитируя сайт губернатора:
сканирование пути к классам и автоматическое связывание, управление жизненным циклом, конфигурация для сопоставления полей, проверка полей и параллельный прогрев объектов
Здесь я продемонстрирую две функции: сканирование пути к классам и автоматическое связывание.
Внедрение базовой зависимости
Рассмотрим BlogService, в зависимости от BlogDao:
|
01
02
03
04
05
06
07
08
09
10
11
12
|
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); }} |
Если бы я использовал Spring для определения зависимости между этими двумя компонентами, конфигурация была бы следующей:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
package sample.spring;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import sample.dao.BlogDao;import sample.service.BlogService;@Configurationpublic class SampleConfig { @Bean public BlogDao blogDao() { return new DefaultBlogDao(); } @Bean public BlogService blogService() { return new DefaultBlogService(blogDao()); }} |
В Spring конфигурация зависимостей указывается в классе с аннотацией @Configuration. Методы, аннотированные @Bean, возвращают компоненты, обратите внимание на то, как blogDao внедряется посредством инжектора конструктора в методе blogService.
Модульный тест для этой конфигурации заключается в следующем:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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(); }} |
Обратите внимание, что Spring предоставляет хорошую поддержку для модульного тестирования, лучшим тестом будет следующее:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
|
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)@ContextConfigurationpublic class SampleSpringAutowiredTest { @Autowired private BlogService blogService; @Test public void testSpringInjection() { assertThat(blogService.get(1l), is(notNullValue())); } @Configuration @ComponentScan("sample.spring") public static class SpringConig { }} |
Это базовое внедрение зависимостей, поэтому указывать такой регулятор зависимостей само по себе не требуется, достаточно Guice, вот как будет выглядеть конфигурация с использованием модулей Guice:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
|
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); }} |
и модульный тест для этой конфигурации следующий:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
|
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 и Autobinding
Сканирование пути к классам — это способ обнаружения компонентов путем поиска маркеров в пути к классам. Образец с Spring должен уточнить это:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
@Repositorypublic class DefaultBlogDao implements BlogDao { ....}@Servicepublic class DefaultBlogService implements BlogService { private final BlogDao blogDao; @Autowired public DefaultBlogService(BlogDao blogDao) { this.blogDao = blogDao; } ...} |
Здесь аннотации @Service, @Repository используются в качестве маркеров для указания того, что это компоненты, а зависимости определяются аннотацией @Autowired в конструкторе DefaultBlogService.
Учитывая это, конфигурация теперь упрощена, нам просто нужно предоставить имя пакета, который должен быть проверен на наличие таких аннотированных компонентов, и вот как будет выглядеть полный тест:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
|
package sample.spring;...@RunWith(SpringJUnit4ClassRunner.class)@ContextConfigurationpublic class SampleSpringAutowiredTest { @Autowired private BlogService blogService; @Test public void testSpringInjection() { assertThat(blogService.get(1l), is(notNullValue())); } @Configuration @ComponentScan("sample.spring") public static class SpringConig {}} |
Губернатор оказывает подобную поддержку:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
@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; } ....} |
Здесь аннотация @AutoBindSingleton используется в качестве аннотации маркера для определения привязки guice, учитывая, что тест со сканированием пути к классам следующий:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
|
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())); }} |
Посмотрите, как пакет для сканирования указан с помощью компонента LifecycleInjector регулятора, это автоматически обнаруживает компоненты и связывает их вместе.
Просто чтобы обернуть функции сканирования пути к классам и автосвязки, регулятор, как Spring, предоставляет поддержку для тестирования junit, и лучшим тестом будет следующее:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
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())); }} |
Вывод
Если вы заинтересованы в дальнейшем изучении этого вопроса, у меня есть пример в этом проекте github , я буду расширять этот проект по мере того, как узнаю больше о регуляторе.
| Ссылка: | Изучение регулятора Netflix — часть 1 от нашего партнера по JCG Биджу Кунджуммена в блоге all and sundry. |