Я работал с 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; @Configuration public 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 ) @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 { } } |
Это базовое внедрение зависимостей, поэтому указывать такой регулятор зависимостей само по себе не требуется, достаточно 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
|
@Repository public class DefaultBlogDao implements BlogDao { .... } @Service public 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 ) @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 {} } |
Губернатор оказывает подобную поддержку:
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. |