Share
Embed
Spring_Boot_Starter_-_Как_и_зачем_.pdf
Kirill Tolkachev
March 06, 2018
Transcript
156 slides
-
Цели • Развеять миф о магии в Spring Boot •
Понять принципы работы экосистемы стартеров -
Цели • Развеять миф о магии в Spring Boot •
Понять принципы работы экосистемы стартеров • Понять прикладной смысл Spring Boot Starter -
Что такое Spring Boot? Эта jar-ник c Tomcat внутри Java
interview 2014 -
Java interview 2017 Что такое Spring Boot? Эта хрень помогает
проще собрать микросервис -
@Getter @Setter @Aspect @ToString @EnableWs @Endpoint @EnableWebMvc @EnableCaching @Configuration @RestController
@XmlRootElement @EnableWebSocket @RedisHash("cat") @EnableScheduling @EnableWebSecurity @NoArgsConstructor @ContextConfiguration @SpringBootApplication @Accessors(chain = true) @EnableAspectJAutoProxy @EnableAutoConfiguration @EnableRedisRepositories @EnableWebSocketMessageBroker // generate getters // generate setters // we are an aspect // generate toString() // SOAP is so enterprisy, we definitely need it // Seriously, just read above // we want MVC // and we want to cache stuff // this class can configure itself // we want some REST // this component is marshallable // we want web socket, it's so new-generation // this class is an entity saved in redis // we want scheduled tasks // and some built-in security // generate no args constructor // we want context configuration for unit testing // this is a Sprint Boot application // getters/setters are chained (ala jQuery) // we want AspectJ auto proxy // and auto configuration // since it is an entity we want to enable spring data repositories for redis // we want a broker for web socket messages -
@SpringBootApplication @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { … -
@EnableAutoConfiguration @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @AutoConfigurationPackage @Import({EnableAutoConfigurationImportSelector.class}) public @interface EnableAutoConfiguration
{ String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; Class<?>[] exclude() default {}; String[] excludeName() default {}; } -
AutoConfigurationImportSelector protected List<String> getCandidateConfigurations(...) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader()); ... return configurations; } -
Условия @Configuration @ConditionalOnBean({Client.class, DiscoveryClient.class}) @EnableConfigurationProperties(value = APIVersionFilterProperties.class) public class DiscoveryAPIVersionFilterAutoConfiguration
{ @Bean public ApiVersionServerListFilter versionedFilter(DiscoveryClient discoveryClient, APIVersionFilterProperties properties) { return new APIVersionServerListFilter(properties.getServiceVersions(), discoveryClient); } ... } -
Условия @Configuration @ConditionalOnBean({Client.class, DiscoveryClient.class}) @EnableConfigurationProperties(value = APIVersionFilterProperties.class) public class DiscoveryAPIVersionFilterAutoConfiguration
{ @Bean public ApiVersionServerListFilter versionedFilter(DiscoveryClient discoveryClient, APIVersionFilterProperties properties) { return new APIVersionServerListFilter(properties.getServiceVersions(), discoveryClient); } ... } -
Выводы часть #1 • все не так сложно • лишних
бинов нет, грузится только то, что надо -
Мы готовы писать свой стартер! Новичок #1 Новичок #2 Новичок
#3 Новичок #4 Странный новичок -
Из чего состоит сервис? • API • Clients ◦ JSON
◦ SOAP • Cloud ◦ Discovery ◦ Config -
Из чего состоит сервис? • API • Clients ◦ JSON
◦ SOAP • Cloud ◦ Discovery ◦ Config • Metrics ◦ Healthcheck ◦ Logs -
Из чего состоит сервис? • API • Clients ◦ JSON
◦ SOAP • Cloud ◦ Discovery ◦ Config • Metrics ◦ Healthcheck ◦ Logs • Data • Stream -
Простое общение с @FeignClient("cards-api") public interface P2PCardService { @RequestMapping(value =
"/", method = RequestMethod.POST) CardDTO loadDetachedCard(String id); } -
Отфильтруем друзей public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ... @Override
public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance)) -
Отфильтруем друзей public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ... @Override
public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance)) -
Отфильтруем друзей public class ServiceAPIVersionServerListFilter extends ZoneAffinityServerListFilter<Server> { ... @Override
public List<Server> getFilteredListOfServers(List<Server> listOfServers) { if (listOfServers == null || listOfServers.isEmpty()) return listOfServers; List<ServiceInstance> infos = this.discoveryClient.getInstances(listOfServers.first() .getMetaInfo() .getServiceIdForDiscovery()); final List<ServiceInstance> versionedInstance = infos.stream() .filter(instanceInfo -> !versions.containsKey(instanceInfo.getServiceId().toLowerCase()) || checkVersion(versions.get(instanceInfo.getServiceId().toLowerCase()), instanceInfo.getMetadata().get(VERSION_FIELD))) .collect(Collectors.toList()); return listOfServers.stream() .filter(server -> checkServiceInstance(server, versionedInstance)) -
Конфигурация фильтра eureka: client: registryFetchIntervalSeconds: 5 serviceUrl: defaultZone: ${EUREKA_SERV:http://127.0.0.1:8763/eureka/} instance:
virtualHostName : ${spring.application.name} metadataMap: instanceId: ${spring.application.name}:${random.value} versions: 1.0 spring: discovery: filter: versions: someServiceId: 2.0 -
Конфигурация фильтра eureka: client: registryFetchIntervalSeconds: 5 serviceUrl: defaultZone: ${EUREKA_SERV:http://127.0.0.1:8763/eureka/} instance:
virtualHostName : ${spring.application.name} metadataMap: instanceId: ${spring.application.name}:${random.value} versions: 1.0 spring: discovery: filter: versions: someServiceId: 2.0 -
Хочется видеть что-то такое spring: discovery: filter: versions: someOtherService: 2.0
api: versions: 1.0,2.0 -
EnvironmentPostProcessor public class PropertyTranslatorPostProcessor implements EnvironmentPostProcessor { @Override public void
postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) { } translateClientVersionProperty(env); translateZuulRoutes(env); } … } -
EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application
StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent -
EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application
StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent -
EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application
StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent -
запуск SpringApplicationInitializer Локальные бины приложения созданы ApplicationEnvironment PreparedEvent запуск EnvironmentPostPorcessors
Пользовательские бины создаются слишком поздно -
Выводы часть#2 Живешь со Spring - живи по правилам Spring.
Доработка и расширение механизмов Spring должны быть в стартерах -
SOAP Services and Apache CXF ServiceDefinition.wsdl <definitions name = "HelloService"
... xmlns:tns = "http://www.examples.com/wsdl/HelloService.w <message name = "SayHelloRequest"> <part name = "firstName" type = "xsd:string"/> </message> <message name = "SayHelloResponse"> <part name = "greeting" type = "xsd:string"/> </message> <portType name = "Hello_PortType"> <operation name = "sayHello"> <input message = "tns:SayHelloRequest"/> <output message = "tns:SayHelloResponse"/> </operation> </portType> ... <service name = "Hello_Service"> -
SOAP Services and Apache CXF ServiceDefinition.wsdl <definitions name = "HelloService"
... xmlns:tns = "http://www.examples.com/wsdl/HelloService.w <message name = "SayHelloRequest"> <part name = "firstName" type = "xsd:string"/> </message <message name = "SayHelloResponse"> <part name = "greeting" type = "xsd:string"/> </message> <portType name = "Hello_PortType"> <operation name = "sayHello"> <input message = "tns:SayHelloRequest"/> <output message = "tns:SayHelloResponse"/> </operation> </portType> ... <service name = "Hello_Service"> Java Stub wsdl2java tool -
SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool
WS Stub in Spring Context Configure bean -
SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool
WS Stub in Spring Context Configure bean <beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynami serviceClass="ru.alfalab...wscodedynamicfields address="${lb.address.base}/WSCodeClickDynamic <jaxws:client id="clickPaymentPasswordWS" name="clickPayment serviceClass="ru.alfalab...wsclickpaymentpassw address="${lb.address.base}/WSClickPaymentPass ... <jaxws:client id="customerAddressCompleteWS" name="customerA serviceClass="ru.alfalab...wscustomeraddress22 address="${ws.address.base}/WSCustomerAddress/ <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSC address="${lb.address.base}/WSCustomerInfo/WSC </beans> -
SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool
WS Stub in Spring Context Configure bean <beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynami serviceClass="ru.alfalab...wscodedynamicfields address="${lb.address.base}/WSCodeClickDynamic <jaxws:client id="clickPaymentPasswordWS" name="clickPayment serviceClass="ru.alfalab...wsclickpaymentpassw address="${lb.address.base}/WSClickPaymentPass ... <jaxws:client id="customerAddressCompleteWS" name="customerA serviceClass="ru.alfalab...wscustomeraddress22 address="${ws.address.base}/WSCustomerAddress/ <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSC address="${lb.address.base}/WSCustomerInfo/WSC </beans> Call Bean Inject ws bean -
SOAP Services and Apache CXF Apache CXF ServiceDefinition.wsdl Java Stub
wsdl2java tool WS Stub in Spring Context Configure bean Call Bean Inject ws bean -
SOAP Services and Apache CXF Apache CXF ServiceDefinition.wsdl Java Stub
wsdl2java tool WS Stub in Spring Context Configure bean Call Bean Inject ws bean -
CXF Stub в библиотеки ws-stub transfers-api cards-api ...-api dependencies {
compile 'ru.alfabank.ws:currency-stub:1.0.0' } -
<beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynamicFields" serviceClass="ru.alfalab...wscodedynamicfields32.WSCodeDynamicFields31PortType" address="${lb.address.base}/WSCodeClickDynamicFields/WSCodeDynamicFields11"/> <jaxws:client id="clickPaymentPasswordWS" name="clickPaymentPassword" serviceClass="ru.alfalab...wsclickpaymentpassword10.WSPaymentPassword13PortType"
address="${lb.address.base}/WSClickPaymentPassword/WSPaymentPassword13"/> ... <jaxws:client id="customerAddressCompleteWS" name="customerAddressComplete" serviceClass="ru.alfalab...wscustomeraddress22.WSCustomerAddressCompletePortType" address="${ws.address.base}/WSCustomerAddress/WSCustomerAddressComplete22"/> <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSCustomerInfo9PortType" address="${lb.address.base}/WSCustomerInfo/WSCustomerInfo9"/> </beans> WS – ты кто такой -
<beans> <jaxws:client id="codeClickDynamicFieldsWS" name="codeDynamicFields" serviceClass="ru.alfalab...wscodedynamicfields32.WSCodeDynamicFields31PortType" address="${lb.address.base}/WSCodeClickDynamicFields/WSCodeDynamicFields11"/> <jaxws:client id="clickPaymentPasswordWS" name="clickPaymentPassword" serviceClass="ru.alfalab...wsclickpaymentpassword10.WSPaymentPassword13PortType"
address="${lb.address.base}/WSClickPaymentPassword/WSPaymentPassword13"/> ... <jaxws:client id="customerAddressCompleteWS" name="customerAddressComplete" serviceClass="ru.alfalab...wscustomeraddress22.WSCustomerAddressCompletePortType" address="${ws.address.base}/WSCustomerAddress/WSCustomerAddressComplete22"/> <jaxws:client id="customerBaseInfoWS" name="customerInfo" serviceClass="ru.alfalab...wscustomerinfo9.WSCustomerInfo9PortType" address="${lb.address.base}/WSCustomerInfo/WSCustomerInfo9"/> </beans> WS – ты кто такой -
WS – ты кто такой transactions-api – ws.xml targeting-api –
ws.xml transfers-api – ws.xml ... -
1. Добавить зависимость (WSDL/WS-STUB) 2. Настроить бины в ws.xml 3.
Прописать адреса и настройки для добавленных сервисов ${lb.address.base} Как сделать новый RPC вызов -
Как сделать новый RPC вызов 1. Добавить зависимость (WSDL/WS-STUB) 2.
Настроить бины в ws.xml 3. Прописать адреса и настройки для добавленных сервисов ${lb.address.base} И как это делается? -
Как сделать новый RPC вызов 1. Добавить зависимость (WSDL/WS-STUB) 2.
Настроить бины в ws.xml 3. Прописать адреса и настройки для добавленных сервисов ${lb.address.base} И как это делается? Captain Copy-Paste -
Run Application ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup
failed Error creating bean with name 'ru.alfabank...WSAccountClickPayment13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank.ws.cs.eq.wsaccountclickpayment13.WSAccountClickPayment13PortType -
ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup failed Error
creating bean with name 'ru.alfabank...WSAccountClickPayment13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank.ws.cs.eq.wsaccountclickpayment13.WSAccountClickPayment13PortType Run Application -
ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup failed Error
creating bean with name 'ru.alfabank...WSAccountClickPayment13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank.ws.cs.eq.wsaccountclickpayment13.WSAccountClickPayment13PortType Process finished with exit code 1 Run Application -
Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...
Find WS Classes @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType -
Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...
Find WS Classes @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory WSCardsTransactions12PortType Configure Context -
Автоматизируем? Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory
WSCardsTransactions12PortType Inject Spring Context @Component MyService BeanInstance WSCardsTransactions12PortType Configure Properties and Endpoints -
Автоматизируем? Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory
WSCardsTransactions12PortType Inject Spring Context @Component MyService BeanInstance WSCardsTransactions12PortType Configure Properties and Endpoints Или падает с ошибкой если не настроен endpoint FAIL-FAST Error creating bean with name 'ru.alfabank...WSSomeServicet13PortType' defined in Apache CXF starter autoscan package: Add next properties to your application.yml file: spring.cxf: clients: - endpoint: http://SOME_HOST/SOME_PATH_TO_WS className: ru.alfabank....WSSomeServicet13PortType -
@Slf4j @Configuration @EnableConfigurationProperties @ConditionalOnProperty(name = "spring.cxf.client.enabled", matchIfMissing = true) public
class CxfClientConfiguration { @Bean public CxfBeanDefinitionPostProcessor cxfBeanDefinitionPP(Environment environment) { return new CxfBeanDefinitionPostProcessor(environment); } @Bean public static BusWiringBeanFactoryPostProcessor jsr250BeanPostProcessor() { return new BusWiringBeanFactoryPostProcessor(); } @Bean public static BusExtensionPostProcessor busExtensionPostProcessor() { return new BusExtensionPostProcessor(); } @Slf4j @Configuration @ConditionalOnClass({SpringBus.class, JaxWsClientFactoryBean.class, ConfigurationPropertiesBindingPostProcessor.class}) @EnableConfigurationProperties({CxfClientsProperties.class, WSConfiguration.class}) public static class CxfClientFactoryAutoConfiguration { @Bean(name = CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME) @ConditionalOnMissingBean(name = {CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME}) CxfWsStubBeanFactory proxyWsBeanFactory( CxfClientsProperties cxfClientsProperties, Bus bus, CxfInterceptorConfigurer interceptorConfigurer ) { return new CxfWsStubBeanFactory( cxfClientsProperties, bus, interceptorConfigurer ); } Конфигурация библиотек @Bean @ConditionalOnMissingBean(CxfBusConfigurer.class) public CxfBusConfigurer cxfBusConfigurer(CxfClientsProperties cxfClientsProperties) { return new DefaultCxfBusConfigurer(cxfClientsProperties); } @Bean(destroyMethod = "shutdown") public Bus cxf(CxfBusConfigurer cxfBusConfigurer) { SpringBus bus = new SpringBus(); cxfBusConfigurer.configure(bus); return bus; } @Bean @ConditionalOnMissingBean(CxfInterceptorConfigurer.class) public CxfInterceptorConfigurer cxfInterceptorConfigurer( CxfInterceptorAnnotationProcessor cxfInterceptorAnnotationProcessor, BeanFactory beanFactory ) { return new CxfInterceptorConfigurer( beanFactory, cxfInterceptorAnnotationProcessor.getGlobalInterceptors(), cxfInterceptorAnnotationProcessor.getSpecificInterceptors() ); } @Bean @ConditionalOnMissingBean(CxfInterceptorAnnotationProcessor.class) public static CxfInterceptorAnnotationProcessor cxfInterceptorBFPP() { return new CxfInterceptorAnnotationProcessor(); } static final String CXF_WS_CLIENT_PROXY_FACTORY_DEFAULT_NAME = "CxfWsClientProxyFactory"; } } -
Конфигурация библиотек @Bean public CxfBeanDefinitionPostProcessor cxfBeanDefinitionPP(Environment env) { return new
CxfBeanDefinitionPostProcessor(env); } @Bean public static BusWiringBeanFactoryPostProcessor jsr250BeanPostProcessor() { return new BusWiringBeanFactoryPostProcessor(); } @Bean public static BusExtensionPostProcessor busExtensionPostProcessor() { return new BusExtensionPostProcessor(); } -
Конфигурация библиотек Или, не дай бог : @ComponentScan( { "ru.alfa.cxf",
"ru.alfa.discovery", "ru...", "ru..." ... } ) -
Проблемы Толстый сервис 1. Необходимость знания кишков библиотек 2. Лишние
куски кода в самом сервисе -
Вывод часть #3 Сложность конфигурации библиотеки иногда сопоставима со сложностью
логики библиотеки Ее логичнее выносить в стартер -
Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' } # META-INF/spring.components
o.s.t.f.APIVersionServerFilter=org.springframework.stereotype.Component o.s.t.f.APIVersionFilterHelper=org.springframework.stereotype.Component ... -
Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' } # META-INF/spring.components
o.s.t.f.APIVersionServerFilter=org.springframework.stereotype.Component o.s.t.f.APIVersionFilterHelper=org.springframework.stereotype.Component ... Классы связанный проаннотированный аннотацией Spring`а -
Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' } # META-INF/spring.components
o.s.t.f.APIVersionServerFilter=org.springframework.stereotype.Component o.s.t.f.APIVersionFilterHelper=org.springframework.stereotype.Component ... Чем проаннотировано -
Выводы 1. Не магия, а скрытые возможности 2. Создавать стартеры
просто и не грешно 3. Ничего лишнего там не грузится и множество стартеров - не обязательно медленный старт 4. Сложность перетекает постепенно и ее надо выносить из сервиса с бизнес-логикой по максимуму -
Выводы 1. Не магия, а скрытые возможности 2. Создавать стартеры
просто и не грешно 3. Ничего лишнего там не грузится и множество стартеров - не обязательно медленный старт 4. Сложность перетекает постепенно и ее надо выносить из сервиса с бизнес-логикой по максимуму -
Выводы 1. Не магия, а скрытые возможности 2. Создавать стартеры
просто и не грешно 3. Ничего лишнего там не грузится и множество стартеров - не обязательно медленный старт 4. Сложность перетекает постепенно и ее надо выносить из сервиса с бизнес-логикой по максимуму Все стало проще? Да ладно?! -
Gradle transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0'
compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile 'mongodb-starter:0.2.0' … } task testReport(type: TestReport) { … } jacocoTestCoverageVerification { … } … ~200 lines -
Gradle transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0'
compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile 'mongodb-starter:0.2.0' … } transactions-api: dependencies { compile 'hazelcast:3.6.9' compile 'redis:1.1.1' compile 'mongodb:3.6.0' compile 'fat-starter:100.50.0' } task testReport(type: TestReport) { … } jacocoTestCoverageVerification { … } … ~200 lines -
Gradle transactions-api: dependencies { compile … compile … } offers-api:
dependencies { compile … compile … } ... -
Gradle transactions-api: dependencies { compile … compile … } offers-api:
dependencies { compile … compile … } ... accounts-api: dependencies { compile … compile … } settings-api: dependencies { compile … compile … } ... transfer-api: dependencies { compile … compile … } payment-api: dependencies { compile … compile … } ... -
Gradle transactions-api: dependencies { compile … compile … } offers-api:
dependencies { compile … compile … } ... accounts-api: dependencies { compile … compile … } settings-api: dependencies { compile … compile … } ... transfer-api: dependencies { compile … compile … } payment-api: dependencies { compile … compile … } ... Captain Copy-Paste -
build.gradle: apply plugin: "version.plugin" apply plugin: "maven.plugin" apply plugin: "check.plugin"
apply plugin: "findbugs.plugin" apply plugin: "verify.plugin" apply plugin: "publish.plugin" apply plugin: "awesome.plugin" 149 Декларативный подход -
• class MyPlugin implements Plugin<Project> { void apply(Project project) {
project.configure { dependencies { compile … compile … } } } } Сила в композиции 150 -
• class MyPlugin implements Plugin<Project> { void apply(Project project) {
project.configure { dependencies { compile … compile … } } project.plugins.apply(AddGitTagPlugin) project.plugins.apply(UserInfoPlugin) } } Сила в композиции 151 -
• class MyPlugin implements Plugin<Project> { void apply(Project project) {
project.configure { dependencies { compile … compile … } } project.plugins.apply(AddGitTagPlugin) project.plugins.apply(UserInfoPlugin) project.tasks.withType(SomeTaskType) { //configure } } } 152 Сила в композиции -
Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 154 Автоапдейт минорных
версий Путь героев – но это уже другая история