Spring Boot Starter — как и зачем?

Spring Boot Starter — как и зачем?

Spring — уже не магия (спасибо «Spring-потрошителю» и Евгению Борисову), а вот Spring Boot довольно часто клеймят магической поделкой. Но многим нравится, особенно новичкам!

В докладе мы осветим следующее:

зачем вообще в рамках типовой компании, использующей Spring Boot, могут понадобиться собственные стартеры;
как скоро инквизиция приходит за новичками, если они бездумно используют готовые стартеры;
насколько Spring Boot самостоятелен и что это значит для разработчиков.
Доклад рассчитан на практикующих Spring (а лучше Spring Boot) инженеров, которые уже сталкивались с различными трудностями поддержки увесистой инфраструктуры, разрабатываемой с использованием Spring.

40951719c6ca509831d5c38b764661c9?s=128

Kirill Tolkachev

March 04, 2018
Tweet

Transcript

  1. Spring Boot Starter City Как??? Зачем?! магический лагерь для похудания

  2. @tolkv @lavcraft @gorelikoff @gorelikov

  3. Цели • Развеять миф о магии в Spring Boot

  4. None
  5. Цели • Развеять миф о магии в Spring Boot •

    Понять принципы работы экосистемы стартеров
  6. Цели • Развеять миф о магии в Spring Boot •

    Понять принципы работы экосистемы стартеров • Понять прикладной смысл Spring Boot Starter
  7. Spring уже не магия

  8. Spring Boot все еще магия

  9. Что такое Spring Boot? Эта jar-ник c Tomcat внутри Java

    interview 2014
  10. Java interview 2017 Что такое Spring Boot? Эта хрень помогает

    проще собрать микросервис
  11. @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
  12. Но новичкам-то нравится

  13. Пока не приходит Spring Boot инквизиция

  14. Spring Boot слишком самостоятелен

  15. Откуда 436 spring beans?

  16. Откуда 436 spring beans?

  17. Давайте разберёмся в “магии”

  18. Смотрим код Spring Boot’a

  19. @SpringBootApplication

  20. @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 { …
  21. @SpringBootApplication @EnableAutoConfiguration

  22. @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 {}; }
  23. @SpringBootApplication @EnableAutoConfiguration ImportSelector

  24. AutoConfigurationImportSelector protected List<String> getCandidateConfigurations(...) { List<String> configurations = SpringFactoriesLoader.loadFactoryNames( getSpringFactoriesLoaderFactoryClass(),

    getBeanClassLoader()); ... return configurations; }
  25. @SpringBootApplication @EnableAutoConfiguration ImportSelector SpringFactoriesLoader

  26. spring-boot-autoconfigure.jar/spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\ org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\ org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\ org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration.\ … 129

    lines
  27. @SpringBootApplication @EnableAutoConfiguration ImportSelector SpringFactoriesLoader Starter#1 Starter#2 Starter#3

  28. Ваше приложение

  29. @ConditionalOnClass @ConditionalOnBean @ConditionalOnExpression @ConditionalOnProperty @ConditionalOnResource Загрузка при условии

  30. Как устроен стартер

  31. Autoconfigure

  32. Условия @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); } ... }
  33. Условия @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); } ... }
  34. Starter

  35. spring.factories org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ ru.alfalab.discovery.version.DiscoveryAPIVersionFilterAutoConfiguration

  36. Вся магия 1. @EnableAutoConfiguration 2. @ConditionalOn… 3. spring.factories

  37. Выводы часть #1 • все не так сложно • лишних

    бинов нет, грузится только то, что надо
  38. Мы готовы писать свой стартер! Новичок #1 Новичок #2 Новичок

    #3 Новичок #4 Странный новичок
  39. Стандартного набора хватит всем! Зачем писать свое? Опытный тех. лид

  40. Из чего состоит сервис?

  41. Из чего состоит сервис? • API

  42. Из чего состоит сервис? • API • Clients ◦ JSON

    ◦ SOAP
  43. Из чего состоит сервис? • API • Clients ◦ JSON

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

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

    ◦ SOAP • Cloud ◦ Discovery ◦ Config • Metrics ◦ Healthcheck ◦ Logs • Data • Stream
  46. Смотрим код сервиса

  47. Так это выглядит изнутри...

  48. Так это выглядит изнутри...

  49. Так это выглядит изнутри...

  50. Так это выглядит изнутри...

  51. Так это выглядит изнутри...

  52. Так это выглядит изнутри...

  53. Так это выглядит изнутри...

  54. Этим сервисам стоило бы похудеть

  55. Я не толстый! у меня кость широкая

  56. Так это выглядит изнутри...

  57. Что в этих классах

  58. Service discovery

  59. Межсервисное общение

  60. Межсервисное общение

  61. Простое общение с @FeignClient("cards-api") public interface P2PCardService { @RequestMapping(value =

    "/", method = RequestMethod.POST) CardDTO loadDetachedCard(String id); }
  62. Межсервисное общение 2.0 1.0 1.0

  63. Межсервисное общение

  64. Межсервисное общение

  65. Отфильтруем друзей 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))
  66. Отфильтруем друзей 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))
  67. Отфильтруем друзей 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))
  68. 68 Нахуа?!

  69. В нашем сервисе есть что-то инородное

  70. Надо переносить в библиотеки

  71. Конфигурация фильтра 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
  72. Конфигурация фильтра 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
  73. Хочется видеть что-то такое spring: discovery: filter: versions: someOtherService: 2.0

    api: versions: 1.0,2.0
  74. EnvironmentPostProcessor public class PropertyTranslatorPostProcessor implements EnvironmentPostProcessor { @Override public void

    postProcessEnvironment(ConfigurableEnvironment env, SpringApplication application) { } translateClientVersionProperty(env); translateZuulRoutes(env); } … }
  75. EnvironmentPostProcessor`s Application ContextInitializer`s Application ReadyEvent Тут начинается Spring Ripper Application

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

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

    StartingEvent Application EnvironmentPreparedEvent Application PreparedEvent Context RefreshedEvent EmbeddedServlet Container InitializedEvent
  78. запуск SpringApplicationInitializer Локальные бины приложения созданы ApplicationEnvironment PreparedEvent запуск EnvironmentPostPorcessors

    Пользовательские бины создаются слишком поздно
  79. Межсервисное общение 2.0 1.0 1.0

  80. Выводы часть#2 Живешь со Spring - живи по правилам Spring.

    Доработка и расширение механизмов Spring должны быть в стартерах
  81. Все еще много лишнего?

  82. Все равно толстый!

  83. Первый шаг к похуданию

  84. 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">
  85. 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
  86. SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool

  87. SOAP Services and Apache CXF ServiceDefinition.wsdl Java Stub wsdl2java tool

    WS Stub in Spring Context Configure bean
  88. 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>
  89. 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
  90. 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
  91. 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
  92. CXF Stub в библиотеки

  93. CXF Stub в библиотеки dependencies { compile 'ru.alfabank.ws:currency-stub:1.0.0' }

  94. CXF Stub в библиотеки ws-stub transfers-api cards-api ...-api dependencies {

    compile 'ru.alfabank.ws:currency-stub:1.0.0' }
  95. <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 – ты кто такой
  96. <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 – ты кто такой
  97. WS – ты кто такой transactions-api – ws.xml

  98. WS – ты кто такой transactions-api – ws.xml targeting-api –

    ws.xml
  99. WS – ты кто такой transactions-api – ws.xml targeting-api –

    ws.xml transfers-api – ws.xml ...
  100. 1. Добавить зависимость (WSDL/WS-STUB) 2. Настроить бины в ws.xml 3.

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

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

    Настроить бины в ws.xml 3. Прописать адреса и настройки для добавленных сервисов ${lb.address.base} И как это делается? Captain Copy-Paste
  103. А что нужно разработчикам?

  104. Сервис!

  105. 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
  106. 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
  107. 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
  108. Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...

  109. Автоматизируем? Classpath @WebService WSAccountClickPayment13PortType @WebService WSCardsTransactions12PortType @Component MySuperService @Other ...

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

    WSCardsTransactions12PortType Inject Spring Context @Component MyService BeanInstance WSCardsTransactions12PortType Configure Properties and Endpoints
  112. Автоматизируем? 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
  113. EPP нет, можно в обычную библиотеку?

  114. @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"; } }
  115. Конфигурация библиотек @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(); }
  116. Конфигурация библиотек Или, не дай бог : @ComponentScan( { "ru.alfa.cxf",

    "ru.alfa.discovery", "ru...", "ru..." ... } )
  117. Проблемы 1. Необходимость знания кишков библиотек Капитан сложность Долой инверсию

    контроля!
  118. Проблемы Толстый сервис 1. Необходимость знания кишков библиотек 2. Лишние

    куски кода в самом сервисе
  119. Конфигурация стартеров внутри приложения

  120. Вывод часть #3 Сложность конфигурации библиотеки иногда сопоставима со сложностью

    логики библиотеки
  121. Вывод часть #3 Сложность конфигурации библиотеки иногда сопоставима со сложностью

    логики библиотеки Ее логичнее выносить в стартер
  122. Вывод часть #3 Даешь инверсию контроля для компонентов Spring!

  123. Умным сервисам - умные библиотеки

  124. Похудел и накачался, но тормоз

  125. Бонусная секция

  126. Что нового в Spring 5? Context Indexer

  127. Просто добавь зависимость dependencies { compileOnly 'org.springframework:spring-context-indexer:5.0.1.RELEASE' }

  128. Просто добавь зависимость 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 ...
  129. Просто добавь зависимость 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`а
  130. Просто добавь зависимость 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 ... Чем проаннотировано
  131. Выводы 1. Не магия, а скрытые возможности 2. Создавать стартеры

    просто и не грешно 3. Ничего лишнего там не грузится и множество стартеров - не обязательно медленный старт 4. Сложность перетекает постепенно и ее надо выносить из сервиса с бизнес-логикой по максимуму
  132. Выводы 1. Не магия, а скрытые возможности 2. Создавать стартеры

    просто и не грешно 3. Ничего лишнего там не грузится и множество стартеров - не обязательно медленный старт 4. Сложность перетекает постепенно и ее надо выносить из сервиса с бизнес-логикой по максимуму
  133. Выводы 1. Не магия, а скрытые возможности 2. Создавать стартеры

    просто и не грешно 3. Ничего лишнего там не грузится и множество стартеров - не обязательно медленный старт 4. Сложность перетекает постепенно и ее надо выносить из сервиса с бизнес-логикой по максимуму Все стало проще? Да ладно?!
  134. Это еще не конец Но он близко!

  135. Стартеры тоже бывают разные Starter#1 Starter#2 Starter#3 spring-boot-autoconfigure

  136. В чем разница? Starter#1 Starter#2 Starter#3

  137. В чем разница? Starter#1 Starter#2 Starter#3 spring-boot-autoconfigure

  138. 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
  139. 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
  140. Толстеем

  141. Толстеем, Худеем

  142. Толстеем, Худеем, Толстеем

  143. Gradle transactions-api: dependencies { compile … compile … } offers-api:

    dependencies { compile … compile … } ...
  144. 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 … } ...
  145. 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
  146. Сложно обновлять зависимости

  147. Сложно обновлять зависимости Все немного разные

  148. Управляем зависимостями

  149. 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 Декларативный подход
  150. • class MyPlugin implements Plugin<Project> { void apply(Project project) {

    project.configure { dependencies { compile … compile … } } } } Сила в композиции 150
  151. • class MyPlugin implements Plugin<Project> { void apply(Project project) {

    project.configure { dependencies { compile … compile … } } project.plugins.apply(AddGitTagPlugin) project.plugins.apply(UserInfoPlugin) } } Сила в композиции 151
  152. • 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 Сила в композиции
  153. Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 153

  154. Сила в композиции apply plugin: "your.plugin.all" //2.1.+ 154 Автоапдейт минорных

    версий Путь героев – но это уже другая история
  155. Вывод ...последний На больших проектах даже умными зависимостями надо управлять

  156. Вопросы?