Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Spring Boot И Не Только

Spring Boot И Не Только

В программе
* spring boot
* способы его расширения и заточки под нужды компании
* сложности возникающие при масштабировании подходов работы с микросервисами
* подходы к их решению с помощью spring boot и gradle
* немного о том как работать в команде и к чему нужно стремиться

Учитесь, развивайтесь, ходите на конференции :)

Промокоды на конференции в 2020 году:
https://jpoint.ru/ – JavaMentorJpointPC
http://heisenbug.ru/ – JavaMentorHeisenbugPC

Kirill Tolkachev

March 01, 2020
Tweet

More Decks by Kirill Tolkachev

Other Decks in Technology

Transcript

  1. Начнём? Господин Best Practice Господин «Аастанавись, Задумайся» Господин тимлид –

    он за контроль Господин «№;$?% Магия…» Господин, который просто классно шутит 5
  2. Цели Мы пошли в микросервисы, какие проблемы ожидать и как

    их решать? Чем разработчику могут помочь Spring Boot и другие инструменты, и какие могут быть проблемы? 9
  3. Disclaimer Слушайте нас, но используйте свои мозги Писать код не

    будем – есть доки Только опыт, логика и выводы 12
  4. План 1. Spring boot 2. Spring Boot 3. Что-то еще

    4. Перерыв 5. Spring Boot 6. Выводы 14
  5. И как выбирают? Корпоративный стандарт 1. Только Java 2. Только

    Java EE 3. Только Oracle 4. Только <ENTERPRISE_ПРОДУКТ> От компании 17
  6. И как выбирают? Корпоративный стандарт 1. Только Java 2. Только

    Java EE 3. Только Oracle 4. Только <ENTERPRISE_ПРОДУКТ> Какой стандарт? 1. Go 2. Rust 3. Unicorn 4. Смузи От компании От разработчика 18
  7. Ну, а если серьезно? • Обладает нужной функциональностью • Вписывается

    в нефункциональные требования • Поддержка и количество тем на stackoverflow • Знания и интересы команды 19
  8. Серьезно? • Обладает нужной функциональностью • Вписывается в нефункциональные требования

    • Поддержка и количество тем на stackoverflow • Знания и интересы команды 20
  9. @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 28
  10. @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 { … 39
  11. Из чего состоит сервис? • API • Clients ◦ JSON

    ◦ SOAP • Cloud ◦ Discovery ◦ Config 57
  12. Из чего состоит сервис? • API • Clients ◦ JSON

    ◦ SOAP • Cloud ◦ Discovery ◦ Config • Metrics ◦ Healthcheck ◦ Logs 58
  13. Из чего состоит сервис? • API • Clients ◦ JSON

    ◦ SOAP • Cloud ◦ Discovery ◦ Config • Metrics ◦ Healthcheck ◦ Logs • Data • Stream 59
  14. Простое общение с @FeignClient("cards-api") public interface P2PCardService { @RequestMapping(value =

    "/", method = RequestMethod.POST) CardDTO loadDetachedCard(String id); } 79
  15. Отфильтруем друзей 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)) 80
  16. Отфильтруем друзей 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)) 81
  17. Отфильтруем друзей 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)) 82
  18. Много странного кода 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)) 84
  19. Конфигурация фильтра 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", "2.0"] spring: discovery: filter: versions: someServiceId: 1.0 89
  20. spring: discovery: filter: versions: someServiceId: 1.0 api: versions: ["1.0", "2.0"]

    Хочется видеть что-то такое И проверять!!! 96
  21. ERROR [-,,,] 27393 --- [main] o.s.boot.SpringApplication: Application startup failed Error

    java.lang.IllegalStateException: Version of supported API should be specified, e.g.: spring: discovery: api: versions: ["1.0", "2.0"] Run Application 97
  22. EnvironmentPostProcessor public class PropertyTranslatorPostProcessor implements EnvironmentPostProcessor { @Override public void

    postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) { } translateClientVersionProperty(env); translateZuulRoutes(env); } … } 98
  23. @Configuration public class DiscoveryVersionFilterConfiguration { @Bean public void propertyTranslator() {

    } return new PropertyTranslatorPostProcessor(); } … } Создаём EPP 99
  24. EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application

    StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent 102
  25. EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application

    StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent 103
  26. EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application

    StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent 104
  27. Условия @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); } ... } 110
  28. Конфигурация @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); } ... } 111
  29. Дело не в spring-cloud → Умное логирование → Web-фильтры с

    доп. логикой → Любые другие расширения базовых механизмов 118
  30. Доработка и расширение механизмов Spring должны быть в стартерах используй

    инструменты в соответствии с уже заложенными принципами
  31. 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"> 125
  32. 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 126
  33. 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> 129
  34. 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 130
  35. 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 131
  36. 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 132
  37. <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 – ты кто такой 136
  38. <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 – ты кто такой 137
  39. 1. Добавить зависимость (WSDL/WS-STUB) 2. Настроить бины в ws.xml 3.

    Прописать адреса и настройки для добавленных сервисов ${lb.address.base} Как сделать новый RPC вызов 141
  40. Как сделать новый RPC вызов 1. Добавить зависимость (WSDL/WS-STUB) 2.

    Настроить бины в ws.xml 3. Прописать адреса и настройки для добавленных сервисов ${lb.address.base} И как это делается? 142
  41. Как сделать новый RPC вызов 1. Добавить зависимость (WSDL/WS-STUB) 2.

    Настроить бины в ws.xml 3. Прописать адреса и настройки для добавленных сервисов ${lb.address.base} И как это делается? Captain Copy-Paste 143
  42. 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 146
  43. 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 147
  44. 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 148
  45. Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...

    Find WS Classes @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType 150
  46. Автоматизируем? 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 151
  47. Автоматизируем? Spring Context BeanDefinition + BeanFactory WSAccountClickPayment13PortType BeanDefinition + BeanFactory

    WSCardsTransactions12PortType Inject Spring Context @Component MyService BeanInstance WSCardsTransactions12PortType Configure Properties and Endpoints 152
  48. Автоматизируем? 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 153
  49. @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"; } } 156
  50. @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"; } } 157
  51. Конфигурация библиотек @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(); } 158
  52. Проблемы Толстый сервис 1. Необходимость знания кишков библиотек 2. Лишние

    куски кода в самом сервисе 3. Случайное подхваченные бины 166
  53. Не в SOAP дело • Thrift/gRPC • Netty • Любая

    сложная или устаревшая внешняя зависимость 167
  54. А какие ещё стартеры бывают? • Tracing • Logging •

    Health indicators • Monitoring • Etc • Какие-то ваши стартеры 171
  55. Э не не не. Нам нужны свои 1. Не для

    всего есть стартеры 2. Мы можем их тестировать 179
  56. Структура +-- starter +--- autoconfigure +--- starter \--- examples +--

    demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests 184
  57. Структура +-- starter +--- autoconfigure +--- starter \--- examples +--

    demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests 185
  58. Структура +-- starter +--- autoconfigure +--- starter \--- examples +--

    demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests 186
  59. Структура +-- starter +--- autoconfigure +--- starter \--- examples +--

    demo-spec-0 +-- demo-spec-1 \-- demo-spec-N + spec code + tests 187
  60. Failed to load ApplicationContext Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Invalid bean definition

    with name 'ru.some.pack.client.SomeClient' defined in null: Could not resolve placeholder some.url' in value "http://${some.url}"; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder some.url' in value "http://${some.url}" Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder some.url' in value "http://${some.url}" Первое использование 190
  61. Starter component tests Сформировать часть контекста нам помогут: 1. @SpringBootTest

    2. @TestConfiguration 3. @ContextConfiguration 4. ApplicationContextRunner 193
  62. @SpringBootTest @SpringBootTest(classes = [ContextPartConfiguration]) class CxfConnectionTimeoutSpec extends Specification { @Requires({

    !os.isMacOs() }) def 'should apply timeout to ASYNC requests'() { when: client.sayHelloAsync('foo', { res -> }).get() then: ExecutionException ex = thrown() ex.cause.class == SocketTimeoutException } ... 195
  63. @SpringBootTest @SpringBootTest(classes = [ContextPartConfiguration]) class CxfConnectionTimeoutSpec extends Specification { ...

    @Requires({ os.isMacOs() }) def 'should apply timeout to ASYNC requests for OSX'() { when: client.sayHelloAsync('foo', { res -> }).get() then: ExecutionException ex = thrown() ex.cause.class == ConnectException } 196
  64. @ContextConfiguration @ContextConfiguration(classes = ValidConfiguration) class InterceptorConfigurerTest extends Specification { @Autowired

    CxfInterceptorConfigurer interceptorConfigurer def "should add interceptors to JaxWsProxyFactoryBean"() { when: interceptorConfigurer.configure(...) then: ... } } 197
  65. Kubernetes Sidecar POD Application container Sidecar container Могут быть: •

    общая файловая система • общая сеть 213
  66. Не везде работает • Tracing покрывается не до конца -

    коннекты к БД и MQ не обслуживаются 215
  67. Не везде работает • Tracing покрывается не до конца -

    коннекты к БД и MQ не обслуживаются • Логи покрываются не до конца - приложение в любом случае должно выдавать структурированный лог 216
  68. Просто контейнеры Любой оркестратор: • Kubernetes • Mesos • Swarm

    • Bash + puppet • Что угодно запускающее контейнер на хосте 220
  69. Добавляем стартеры transactions-api/build.gradle: dependencies { compile 'hazelcast-starter:1.0.0' compile 'redis-starter:0.2.0' compile

    'mongodb-starter:0.2.0' … } // → 'hazelcast:3.6.9' // → 'redis:1.1.1' // → 'mongodb:3.6.0' Транзитивные зависимости 244
  70. 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' … } 247
  71. 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' } 248
  72. 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' } 249
  73. 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 … } ... 254
  74. 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 255
  75. 'hazelcast:3.6.9' 'redis:1.1.1' Указываем версии 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0'

    'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' Ну такое.. Буду складывать jar`ы в VCS 261
  76. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 262
  77. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 263
  78. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 264
  79. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 265
  80. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 266
  81. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 267
  82. Все немного разные 'hazelcast:3.6.9' 'redis:1.1.1' 'mongodb:3.6.0' 'hazelcast-starter:1.0.0' 'redis-starter:0.2.0' 'mongodb-starter:0.2.0' 'elasticsearch-starter:1.2.0

    'mesos-starter:0.1.0' 'arangodb-starter:0.2.0' 'eureka-starter:1.8.3' 'sleuth-starter:1.0.8' 'zipkin-starter:3.1.1' 'jcache-starter:2.4.1' 'actuator-starter:0.1.0' 'aop-starter:0.2.0' 'logging-starter:1.8.3' 'freemarker-starter:1.0.8' 'validation-starter:3.1.1' 'tomcat-starter:3.1.1' 'etc-starter:0.1.0' 268
  83. Выход №1 – latest version dependencies { implementation 'some-starter:0.1.0' //

    → 'some-starter:+' implementation 'marathon-starter:0.1.0' // → 'marathon-starter:+' ... } 270
  84. Выход №1 – latest version + locking dependencyLocking { lockAllConfigurations()

    } dependencies { implementation 'some-starter:0.1.0' // → 'some-starter:+' implementation 'marathon-starter:0.1.0' // → 'marathon-starter:+' ... } 272
  85. Выход №1 – latest version + locking $ ./gradlew build

    --write-locks $ ./gradlew build --update-locks ru.company:starter $ git status modified: gradle/dependency-locks/annotationProcessor.lockfile modified: gradle/dependency-locks/buildscript-classpath.lockfile modified: gradle/dependency-locks/compileClasspath.lockfile modified: gradle/dependency-locks/runtimeClasspath.lockfile modified: gradle/dependency-locks/testAnnotationProcessor.lockfile modified: gradle/dependency-locks/testCompileClasspath.lockfile modified: gradle/dependency-locks/testRuntimeClasspath.lockfile 275
  86. Выход №1 – latest version + locking diff ./gradle/dependency-locks/buildscript-classpath.lockfile: -com.netflix.nebula:nebula-publishing-plugin:11.0.0

    -com.netflix.nebula:nebula-release-plugin:10.1.1 +com.netflix.nebula:nebula-publishing-plugin:12.0.0 +com.netflix.nebula:nebula-release-plugin:10.1.2 276
  87. Хей, у нас новые новая система метрик! Пример Инф раструктура

    Но я не умею экспортировать метрики в таком формате 282
  88. Хей, у нас новые новая система метрик! Пример Но я

    не умею экспортировать метрики в таком формате Инф раструктура Ща мы тебя пересоберём 283
  89. Ща мы тебя пересобирём Пример Инф раструктура Теперь я умею

    экспортироавть Метрики в нужном формате 286
  90. Ща мы тебя пересобирём Теперь я умею экспортироавть Метрики в

    нужном формате Пример Инф раструктура Ща мы тебя задеплоим! 287
  91. Ща мы тебя пересобирём Теперь я умею экспортироавть Метрики в

    нужном формате Пример Инф раструктура Ща мы тебя задеплоим! 288
  92. 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" 294 Декларативный подход
  93. • class MyPlugin implements Plugin<Project> { void apply(Project project) {

    project.configure { dependencies { compile … compile … } } } } Сила в композиции 295
  94. Почему не хватает BOM? dependencies { gems 'asciidoctor-diagram:1.5.4.1' } asciidoctorj

    { version = '1.5.6' } task cleanTempDirs(type: Delete) { delete fileTree(dir: docsDir) } artifactoryPublish { properties = [artifactType: 'DOC'] } asciidoctor { finalizedBy tasks.withType(Zip) sources { include fileTree(dir: 'src/docs', includ include fileTree(dir: snippetsDir, inclu } dependsOn jrubyPrepare, project(':app').test gemPath = jrubyPrepare.outputDir requires = ['asciidoctor-diagram'] attributes 'source-highlighter': 'prettify', 'imagesdir': 'images', 'toc': 'left', 'icons': 'font', ...~200 lines 302
  95. Почему не хватает BOM? dependencies { gems 'asciidoctor-diagram:1.5.4.1' } asciidoctorj

    { version = '1.5.6' } task cleanTempDirs(type: Delete) { delete fileTree(dir: docsDir) } artifactoryPublish { properties = [artifactType: 'DOC'] } asciidoctor { finalizedBy tasks.withType(Zip) sources { include fileTree(dir: 'src/docs', includ include fileTree(dir: snippetsDir, inclu } dependsOn jrubyPrepare, project(':app').test gemPath = jrubyPrepare.outputDir requires = ['asciidoctor-diagram'] attributes 'source-highlighter': 'prettify', 'imagesdir': 'images', 'toc': 'left', 'icons': 'font', ...~200 lines ugin 303
  96. • class MyPlugin implements Plugin<Project> { void apply(Project project) {

    project.configure { dependencies { compile … compile … } } project.plugins.apply(AddGitTagPlugin) project.plugins.apply(UserInfoPlugin) } } Сила в композиции 304
  97. • 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 } } } 305 Сила в композиции
  98. Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 307 Автоапдейт минорных

    версий Путь героев – но это уже другая история
  99. Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 308 Автоапдейт минорных

    версий Путь героев – но это уже другая история И dependency locking тоже работает!
  100. Как это работает starter Gradle plugin Любое изменение – повод

    пересобрать всё и передеплоить dependency 312
  101. Как это работает build rebuild rebuild rebuild rebuild rebuild rebuild

    Обновление приводит к сборке новой версии сервисов dependency 313
  102. rebuild rebuild rebuild rebuild rebuild rebuild Как это работает Deploy

    ALL production И последующему деплою 314
  103. rebuild rebuild rebuild rebuild rebuild rebuild Как это работает Deploy

    ALL production И последующему деплою 315
  104. rebuild rebuild rebuild rebuild rebuild rebuild Как это работает Deploy

    ALL test,dev,prod И последующему деплою и тестированию 316
  105. Актуализация версий Ручное + понятно + стабильно - геморойно -

    сложно масштабировать - нет контроля запуска - ... CI/CD + единый процесс для всех + стабильно - много работы - цементирует процессы - СI/CD — 24/7 - ... Runtime + Всё из CI/CD + без пересборки + быстрый запуск + полный контроль - очень много работы - опасно - сложно тестировать - ... 318
  106. Update strategy • Скачать и поднять новый инстанс • Дождаться

    пока инстанс запустится • Проверить что он жив • Переключить балансировку • Потушить старый инстанс 326
  107. Update strategy • Скачать и поднять новый инстанс • Дождаться

    пока инстанс запустится • Проверить что он жив • Переключить балансировку • Потушить старый инстанс 327
  108. Просто добавь зависимость 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 ... 334
  109. Просто добавь зависимость 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`а 335
  110. Просто добавь зависимость 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 ... Чем проаннотировано 336
  111. Радикальный путь К черту ваш Spring, у меня новый друг

    TEAM LEAD MICRONAUT Я сделан по новым принципам 339
  112. Новый подход MICRONAUT Micronaut делает IoC/DI в во время компиляции

    Я быстрее, потому что делаю все заранее 341
  113. Новый подход на Spring Spring Fu val app = application(WebApplicationType.SERVLET)

    { logging { level = LogLevel.DEBUG } beans { bean<SampleService>() } webMvc { router { val service = ref<SampleService>() GET("/") { ok().body(service.generateMessage()) } } converters { string() jackson { indentOutput = true 343
  114. название →__ условие →__ -Xmx/mb время старта/ms -Xmx=enough micronaut 6

    950 +- 0.04 spring boot 11 1500 +- 0.1 spark 3 350 +- 0.01 spring Fu 5 1041 +- 0.08 Сравним время старта 344
  115. Где оптимизировать • Не забывать обновляться • Следить за лишними

    зависимостями • Следить за тем, что и как инициализируются в приложениях 366
  116. Где оптимизировать • Не забывать обновляться • Следить за лишними

    зависимостями • Следить за тем, что и как инициализируются в приложениях • Если много бинов, использовать Context Indexer 367
  117. Где оптимизировать • Не забывать обновляться • Следить за лишними

    зависимостями • Следить за тем, что и как инициализируются в приложениях • Если много бинов, использовать Context Indexer • Использовать spring.main.lazy-initialization на свой страх [spring boot 2.2+] 368
  118. Где оптимизировать • Не забывать обновляться • Следить за лишними

    зависимостями • Следить за тем, что и как инициализируются в приложениях • Если много бинов, использовать Context Indexer • Использовать spring.main.lazy-initialization на свой страх [spring boot 2.2+] • Отказаться от actuator 369