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

Spring/Spring boot workshop at rfb 2019

Spring/Spring boot workshop at rfb 2019

Part 1: Spring
* Spring infrastructure
* Spring lifecycle
* Spring patterns
Part 2: Spring Boot
* Spring Boot anatomy
* Starters
* Practice

Kirill Tolkachev

April 04, 2019
Tweet

More Decks by Kirill Tolkachev

Other Decks in Technology

Transcript

  1. Spring {Boot,Cloud}
    Workshop

    View Slide

  2. Тренинг с конференции JPoint 2019
    securi

    View Slide

  3. Часть 1
    Основы микросервисописания

    View Slide

  4. Как будет проходить тренинг?
    Теория – мы рассказываем вы слушаете

    View Slide

  5. Как будет проходить тренинг?
    Практика

    View Slide

  6. The project structure

    View Slide

  7. web.xml

    org.springframework.web.context.ContextLoaderListener


    springSecurityFilterChain
    org.springframework.web.filter.DelegatingFilterProxy


    springSecurityFilterChain
    /*


    mvc-dispatcher
    org.springframework.web.servlet.DispatcherServlet
    1


    mvc-dispatcher
    /

    View Slide

  8. View Slide

  9. View Slide



  10. contextClass param-name>
    …AnnotationConfigWebApplicationContext param-value>


    contextConfigLocation param-name>
    com.inwhite.conf.AppConfig param-value>


    View Slide

  11. Deployment
    →Use maven/gradle to build a war from that project
    →Copy it to webapps directory of tomcat

    View Slide

  12. create Spring
    ContextLoaderListener
    start applicationContext
    Create filter
    mapping
    Create
    security filter
    login
    Create spring
    dispatcher
    servlet
    Delegate to
    Controller

    View Slide

  13. Теперь давайте замочим XML

    View Slide

  14. Жизнь без web.xml
    false
    Java Servlet Specification 3.+
    Tomcat 7+

    View Slide

  15. Что будем имплементировать
    вместо web.xml?

    View Slide

  16. WebApplicationInitializer

    View Slide

  17. ServletContainerInitializer

    View Slide

  18. WebApplicationInitializer

    View Slide

  19. ServletContainerInitializer

    View Slide

  20. View Slide

  21. § 8.2.4 Shared libraries / runtimes pluggability
    The ServletContainerInitializer class is looked up via
    the jar services API. For each application, an instance
    of the ServletContainerInitializer is created by the
    container at application startup time. The framework
    providing an implementation of the
    ServletContainerInitializer MUST bundle in the
    META-INF/services directory of the jar file a file
    called javax.servlet.ServletContainerInitializer, as per
    the jar services API, that points to the implementation
    class of the ServletContainerInitializer.
    3.+

    View Slide

  22. Как? SPI
    tomcat
    |→ get javax.servlet.ServletContainerInitializer impl via SPI

    View Slide

  23. § 8.2.4 Shared libraries / runtimes pluggability
    The ServletContainerInitializer’s onStartup method get's
    a Set of Classes that either extend / implement the
    classes that the initializer expressed interest in or if
    it is annotated with any of the classes specified via
    the @HandlesTypes annotation.
    3.+

    View Slide

  24. Как? SPI
    tomcat
    |→ get javax.servlet.ServletContainerInitializer impl via SPI
    |→ get all MyInitializer classes (from @HandlesTypes)

    View Slide

  25. Как? SPI
    tomcat
    |→ get javax.servlet.ServletContainerInitializer impl via SPI
    |→ get all WebApplicationInitializer classes (from @HandlesTypes)
    org.springframework.web.WebApplicationInitializer

    View Slide

  26. Как? SPI
    tomcat
    |→ get javax.servlet.ServletContainerInitializer impl via SPI
    |→ get all WebApplicationInitializer classes (from @HandlesTypes)
    |→ call
    ServletContainerInitializer.onStartup(classes,servletCtx)
    according to @Order order

    View Slide

  27. SPI
    org.springframework.web.SpringServletContainerInitializer
    content

    View Slide

  28. § Class ServiceLoader
    loader =
    ServiceLoader.load(ServletContainerInitializer.class)
    6.+

    View Slide

  29. § Class ServiceLoader
    loader =ServiceLoader.load(ServletContainerInitializer.class)
    6.+
    Все имплементации ServletContainerInitializer

    View Slide

  30. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {
    ...

    View Slide

  31. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {

    View Slide

  32. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {
    @Override
    public void onStartup(Set>
    webAppInitializerClasses, ServletContext servletContext)

    View Slide

  33. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {
    @Override
    public void onStartup(Set>
    webAppInitializerClasses, ServletContext servletContext)
    ...
    AnnotationAwareOrderComparator.sort(initializers);
    ...

    View Slide

  34. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer implements
    ServletContainerInitializer {
    @Override
    public void onStartup(Set>
    webAppInitializerClasses, ServletContext servletContext)
    ...
    AnnotationAwareOrderComparator.sort(initializers);
    ...
    initializers.forEach(initializer -> initializer.onStartup(ctx));
    }

    View Slide

  35. Дружок, дай ка мне
    ServletContainerInitializer
    tomcat 7
    Spring
    Jar

    View Slide

  36. Spring
    ServletContainer
    Initializer
    tomcat 7
    Spring
    Jar

    View Slide

  37. @HandlesTypes(WebApplicationInitializer.class)
    public class SpringServletContainerInitializer
    implements ServletContainerInitializer

    View Slide

  38. WebApplicationInitializer
    W
    AI
    WAI
    tomcat 7
    JARS

    View Slide

  39. W
    AI
    WAI
    SpringServletContainerInitializer
    tomcat 7

    View Slide

  40. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws
    ServletException {
    AnnotationConfigWebApplicationContext webContext = new
    AnnotationConfigWebApplicationContext();
    webContext.register(WebConfig.class);
    ServletRegistration.Dynamic servlet =
    servletContext.addServlet("dispatcher", new
    DispatcherServlet(webContext));
    servlet.setLoadOnStartup(1);
    AnnotationConfigWebApplicationContext appContext = new
    AnnotationConfigWebApplicationContext();
    appContext.register(AppConfig.class);
    servletContext.addListener(new ContextLoaderListener(appContext));
    servlet.addMapping("/*");
    }

    View Slide

  41. А попроще нельзя?

    View Slide

  42. Можно, но со Spring Boot`ом – Демо

    View Slide

  43. Как выглядит наш pom.xml

    org.springframework.boot
    spring-boot-starter-parent
    2.1.4.RELEASE

    View Slide


  44. org.springframework.boot
    spring-boot-starter-parent
    2.1.4.RELEASE


    org.springframework.boot
    spring-boot-dependencies
    2.1.4.RELEASE

    View Slide

  45. View Slide

  46. В чём проблема получить
    dependency management таким путём?

    View Slide

  47. Это чужой parent!

    View Slide

  48. View Slide




  49. io.spring.platform
    platform-bom
    Brussels-SR2
    pom
    import



    View Slide

  50. Как выглядит наш build.gradle
    plugins {
    id "org.springframework.boot" version "2.1.4.RELEASE"
    }
    Пакует
    приложение

    View Slide

  51. Как выглядит наш build.gradle
    plugins {
    id "org.springframework.boot" version "2.1.4.RELEASE"
    }
    dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    }
    Добавляем
    зависимости

    View Slide

  52. Как выглядит наш build.gradle
    plugins {
    id "org.springframework.boot" version "2.1.4.RELEASE"
    }
    dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    }
    Automatic
    Dependency
    Management

    View Slide

  53. Как выглядит наш build.gradle
    plugins {
    id "org.springframework.boot" version "2.1.4.RELEASE"
    }
    dependencies {
    compile 'org.springframework.boot:spring-boot-starter-web'
    }
    dependencyManagement {
    imports {
    mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Greenwich.RELEASE'
    }
    }

    View Slide

  54. А я не понял

    View Slide

  55. Web Starter ТАЩИТ!

    View Slide

  56. Кто всё пакует



    org.springframework.boot
    spring-boot-maven-plugin



    View Slide

  57. Jar как Jar, но если зайти внутрь...

    View Slide

  58. View Slide

  59. Анатомия SpringBoot Jar
    jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot

    View Slide

  60. java -jar myapp.jar
    myapp.jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot
    MANIFEST.MF
    ...
    Main-Class: ???
    ...

    View Slide

  61. Перед запуском main – сформируй classpath

    View Slide

  62. JarLauncher

    View Slide

  63. Анатомия SpringBoot Jar
    myapp.jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot
    MANIFEST.MF
    ...
    Spring-Boot-Version: 1.5.3.RELEASE
    Implementation-Vendor: Pivotal Software, Inc.
    Main-Class:
    org.springframework.boot.loader.JarLauncher
    Start-Class:ru….ripper.OurMainClass
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    ...

    View Slide

  64. myapp.jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot
    Анатомия SpringBoot Jar
    MANIFEST.MF
    ...
    Spring-Boot-Version: 1.5.3.RELEASE
    Implementation-Vendor: Pivotal Software, Inc.
    Main-Class:
    org.springframework.boot.loader.JarLauncher
    Start-Class:ru….ripper.OurMainClass
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    ...

    View Slide

  65. MANIFEST.MF
    ...
    Spring-Boot-Version: 1.5.3.RELEASE
    Implementation-Vendor: Pivotal Software, Inc.
    Main-Class:
    org.springframework.boot.loader.JarLauncher
    Start-Class:ru….ripper.OurMainClass
    Spring-Boot-Classes: BOOT-INF/classes/
    Spring-Boot-Lib: BOOT-INF/lib/
    ...
    myapp.jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org
    |- springframework
    |- boot
    Анатомия SpringBoot Jar
    JarLauncher

    View Slide

  66. А кто прописывает манифест этому джару?

    View Slide




  67. org.springframework.boot
    spring-boot-maven-plugin
    conference...OurMainClass



    MANIFEST.MF
    ...
    Start-Class: conference.spring.boot.ripper.OurMainClass
    ...

    View Slide

  68. Как выбирается mainClass если его не указать
    Spring boot plugin сканирует проект – ищет мейны
    ● если есть только один – то это он :)
    ● если больше одного – смотрит где стоит
    @SpringBootApplication и выбирает его
    ● если @SpringBootApplication нет или >1 – ошибка о
    множественных main при сборке

    View Slide

  69. Жмя и Работает
    executable jar для бабушки
    java -jar слишком сложно

    View Slide

  70. Хочу executable jar: Maven



    org.springframework.boot
    spring-boot-maven-plugin

    true



    springBoot {
    executable = true
    }
    Gradle

    View Slide

  71. Demo. Executable jar

    View Slide

  72. Внутренний мир executable Jar
    ● Бабушка хочет чтобы жмя и работало
    ● windows “click click”
    ● $ ./app.jar
    ● ...

    View Slide

  73. Demo. jar structure

    View Slide

  74. Как выглядит Jar?
    Script
    0xf4ra -> а это ZIP!
    jar archive

    View Slide

  75. Как выглядит Jar?
    Script
    0xf4ra -> а это ZIP!
    jar archive
    Начало файла

    View Slide

  76. Как выглядит Jar?
    Script
    0xf4ra -> а это ZIP!
    jar archive
    Начало файла
    Начало zip архива

    View Slide

  77. Где контекс?
    @SpringBootApplilcation
    class App {
    public static void main(String[] args) {
    SpringApplication.run(App.class,args);
    }
    }

    View Slide

  78. Вот контекст!
    @SpringBootApplilcation
    class App {
    public static void main(String[] args) {
    ApplicationContext context =
    SpringApplication.run(App.class,args);
    }
    }

    View Slide

  79. Разрешите представиться

    View Slide

  80. Давайте поговорим о контексте
    Малыш знает про
    ClassPathXmlApplicationContext
    А какие контексты знаешь ТЫ?

    View Slide

  81. Типы ConfigurableApplicationContext

    View Slide

  82. Это я решаю какой контекст создать
    Я до создания
    контекста ещё много
    всего делаю, но об
    этом потом.
    SpringApplication

    View Slide

  83. Web Context Generic Context

    View Slide

  84. Web Context Generic Context
    Если в classpath есть
    Servlet.class...

    View Slide

  85. Web Context Generic Context

    View Slide

  86. Web Context Generic Context
    Если в classpath есть
    Servlet.class...

    View Slide

  87. Если есть
    javax.servlet.Servlet ConfigurableWebApplicationContext
    +
    AnnotationConfigEmbedded WebApplicationContext
    AnnotationConfig ApplicationContext
    Иначе

    View Slide

  88. И что там в контексте то?

    View Slide

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

    View Slide

  90. Откуда взялись все
    эти бины?

    View Slide

  91. Что за spring.factories ?
    § 44.1 Understanding auto-configured beans
    Under the hood, auto-configuration is implemented with standard @Configuration classes. Additional @Conditional annotations
    are used to constrain when the auto-configuration should apply. Usually auto-configuration classes use @ConditionalOnClass and
    @ConditionalOnMissingBean annotations. This ensures that auto-configuration only applies when relevant classes are found and
    when you have not declared your own @Configuration.
    You can browse the source code of spring-boot-autoconfigure to see the @Configuration classes that we provide (see
    theMETA-INF/spring.factories file).

    View Slide

  92. Даёшь инверсию контроля для стартеров

    View Slide

  93. @SpringBootApplication
    Всему голова

    View Slide

  94. @SpringBootApplication

    View Slide

  95. @SpringBootApplication
    → @ComponentScan
    → @Configuration
    → @EnableAutoConfiguration

    View Slide

  96. @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 {

    View Slide

  97. @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 {

    View Slide

  98. @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 {};
    }

    View Slide

  99. @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 {};
    }

    View Slide

  100. @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 {};
    }
    ImportSelector

    View Slide

  101. @EnableAutoConfiguration
    ImportSelector
    Web Starter Boot Starter

    Starter
    Mongo Starter

    View Slide

  102. Spring Factories Loader
    Jar/Starter
    spring.factories
    SpringFactoriesLoader

    View Slide

  103. SpringFactoriesLoader
    static List loadFactories(
    Class factoryClass,
    ClassLoader cl
    )
    static List loadFactoryNames(
    Class> factoryClass,
    ClassLoader cl
    )

    View Slide

  104. Даёшь инверсию контроля для стартеров

    View Slide

  105. 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
    ….

    View Slide

  106. spring-boot-autoconfigure.jar
    @Import(CacheConfigurationImportSelector.class)
    public class CacheAutoConfiguration {

    View Slide

  107. CacheConfigurations
    private static final Map> MAPPINGS;
    static {
    Map> mappings = new HashMap>();
    mappings.put(CacheType.GENERIC, GenericCacheConfiguration.class);
    mappings.put(CacheType.EHCACHE, EhCacheCacheConfiguration.class);
    mappings.put(CacheType.HAZELCAST, HazelcastCacheConfiguration.class);
    mappings.put(CacheType.INFINISPAN, InfinispanCacheConfiguration.class);
    mappings.put(CacheType.JCACHE, JCacheCacheConfiguration.class);
    mappings.put(CacheType.COUCHBASE, CouchbaseCacheConfiguration.class);
    mappings.put(CacheType.REDIS, RedisCacheConfiguration.class);
    mappings.put(CacheType.CAFFEINE, CaffeineCacheConfiguration.class);
    addGuavaMapping(mappings);
    mappings.put(CacheType.SIMPLE, SimpleCacheConfiguration.class);
    mappings.put(CacheType.NONE, NoOpCacheConfiguration.class);
    MAPPINGS = Collections.unmodifiableMap(mappings);
    }

    View Slide

  108. Где то внутри CacheAutoConfiguration
    for (int i = 0; i < types.length; i++) {
    Imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
    }
    return imports;

    View Slide

  109. Захардкодили

    View Slide

  110. И что, они всегда грузятся?

    View Slide

  111. Спокойствие, есть @Conditional
    Они фильтруются

    View Slide

  112. Почему бин то есть то нет?
    Какие условия?

    View Slide

  113. @ConditionalOn***

    View Slide

  114. Conditional и друзья
    @ConditionalOnBean
    @ConditionalOnClass
    @ConditionalOnCloudPlatform
    @ConditionalOnExpression
    @ConditionalOnJava
    @ConditionalOnJndi
    @ConditionalOnMissingBean
    @ConditionalOnMissingClass
    @ConditionalOnNotWebApplication
    @ConditionalOnProperty
    @ConditionalOnResource
    @ConditionalOnSingleCandidate
    @ConditionalOnWebApplication
    ...

    View Slide

  115. Паззлер
    @Configuration
    @ConditionalOnWinterIsHere
    public class UndeadArmyConfiguration {
    @Bean
    public WinterUndeadArmy cursedArmy() {
    return new WinterUndeadArmy();
    }
    @Bean
    @ConditionalOnWinterIsHere
    public DragonGlassFactory dragonGlassFactory() {
    return new DragonGlassFactory();
    }

    View Slide

  116. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    }

    View Slide

  117. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    @Bean
    @ConditionalOnClass ({Стул.class, Ток.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни cтулья() { return new ФабрикаЭлектрическихСтульев( "вж вж"); }
    }

    View Slide

  118. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    @Bean
    @ConditionalOnClass ({Стул.class, Ток.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни cтулья() { return new ФабрикаЭлектрическихСтульев( "вж вж"); }
    @Bean
    @ConditionalOnClass ({Гильотина.class, ХорошееНастроение. class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни гильотины() { return new ФабрикаГильотин( "хрусть хрусть"); }
    }

    View Slide

  119. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    @Bean
    @ConditionalOnClass ({Стул.class, Ток.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни cтулья() { return new ФабрикаЭлектрическихСтульев( "вж вж"); }
    @Bean
    @ConditionalOnClass ({Гильотина.class, ХорошееНастроение. class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни гильотины() { return new ФабрикаГильотин( "хрусть хрусть"); }
    }
    1. ClassDefNotFound ?
    2. Так вообще нельзя, не
    компилируется
    3. Будет работать
    4. Будет отлично работать

    View Slide

  120. ConditionalOnPuzzler
    @Configuration
    public class КонфигурацияКазни {
    @Bean
    @ConditionalOnClass ({Мыло.class, Веревка.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни виселицы() { return new ФабрикаВиселиц( "..."); }
    @Bean
    @ConditionalOnClass ({Стул.class, Ток.class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни cтулья() { return new ФабрикаЭлектрическихСтульев( "вж вж"); }
    @Bean
    @ConditionalOnClass ({Гильотина.class, ХорошееНастроение. class})
    @ConditionalOnMissingBean ({ФабрикаКазни. class})
    public ФабрикаКазни гильотины() { return new ФабрикаГильотин( "хрусть хрусть"); }
    }
    1. ClassDefNotFound ?
    2. Так вообще нельзя, не
    компилируется
    3. Будет работать
    4. Будет отлично работать

    View Slide

  121. Потому что ASM

    View Slide

  122. OnPropertyCondition
    @Conditional(OnPropertyCondition.class)
    public @interface ConditionalOnProperty {
    String[] value() default {};
    String prefix() default "";
    String[] name() default {};
    String havingValue() default "";
    boolean matchIfMissing() default false;
    boolean relaxedNames() default true;
    }
    Что если нужно изменить?

    View Slide

  123. Люблю Spring Boot
    Но хочу Tomcat

    View Slide

  124. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws
    ServletException {
    AnnotationConfigWebApplicationContext webContext = new
    AnnotationConfigWebApplicationContext();
    webContext.register(WebConfig.class);
    ServletRegistration.Dynamic servlet =
    servletContext.addServlet("dispatcher", new
    DispatcherServlet(webContext));
    servlet.setLoadOnStartup(1);
    AnnotationConfigWebApplicationContext appContext = new
    AnnotationConfigWebApplicationContext();
    appContext.register(AppConfig.class);
    servletContext.addListener(new ContextLoaderListener(appContext));
    servlet.addMapping("/*");
    }

    View Slide

  125. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) throws
    ServletException {
    AnnotationConfigWebApplicationContext webContext = new
    AnnotationConfigWebApplicationContext();
    webContext.register(WebConfig.class);
    ServletRegistration.Dynamic servlet =
    servletContext.addServlet("dispatcher", new
    DispatcherServlet(webContext));
    servlet.setLoadOnStartup(1);
    AnnotationConfigWebApplicationContext appContext = new
    AnnotationConfigWebApplicationContext();
    appContext.register(AppConfig.class);
    servletContext.addListener(new ContextLoaderListener(appContext));
    servlet.addMapping("/*");
    }

    View Slide

  126. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext){
    SpringApplication.run(RipperApplication.class);
    }
    }

    View Slide

  127. public class WebStarter implements WebApplicationInitializer {
    @Override
    public void onStartup(ServletContext servletContext) {
    SpringApplication.run(RipperApplication.class);
    }
    }
    @SpringBootApplication
    public class RipperApplication {
    public static void main(String[] args) {
    SpringApplication.run(RipperApplication.class, args);
    }
    }

    View Slide

  128. А в Tomcat не запустилось!
    В Tomcat`e
    не пашет

    View Slide

  129. public class WebStarter implements WebApplicationInitializer{
    @Override
    public void onStartup(ServletContext servletContext) throws
    ServletException {
    SpringApplication.run(RipperApplication.class);
    }
    }
    Tomcat в Tomcat`е

    View Slide

  130. Читаем документацию

    View Slide

  131. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
    return app.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }

    View Slide

  132. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
    return app.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }

    View Slide

  133. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
    return app.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Работает по
    разному

    View Slide

  134. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder app) {
    return app.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }

    View Slide

  135. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Никогда не запустится в tomcat
    Только java -jar

    View Slide

  136. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Без main не соберется
    см. maven/gradle плагины
    Обязательно

    View Slide

  137. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Опционально

    View Slide

  138. § 85.1 Create a deployable war file
    @SpringBootApplication
    public class Application extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder a) {
    return application.sources(Application.class);
    }
    public static void main(String[] args) throws Exception {
    SpringApplication.run(Application.class, args);
    }
    }
    Никогда не запустится в embedded режиме
    Только в Tomcat контейнере
    Опционально

    View Slide

  139. Анатомия War
    .war
    |- META-INF
    |- WEB-INF
    |- lib
    |- classes
    |- org/springframework/boot/loader
    |- WarLauncher and friends

    View Slide

  140. А как убрать tomcat из tomcat?
    If you are using a version of Gradle that supports compile only dependencies (2.12
    or later), you should continue to use providedRuntime. Among other limitations,
    compileOnly dependencies are not on the test classpath so any web-based
    integration tests will fail.

    View Slide

  141. Анатомия War
    .war
    |- META-INF
    |- WEB-INF
    |- lib
    |- lib-provided
    |- classes
    |- org/springframework/boot/loader
    |- WarLauncher and friends

    View Slide

  142. Анатомия SpringBoot Jar/War
    jar
    |- META-INF
    |- BOOT-INF
    |- libs
    |- classes
    |- org/springframework/boot/loader
    |- JarLauncher and friends
    war
    |- META-INF
    |- WEB-INF
    |- lib
    |- lib-provided
    |- classes
    |- org/springframework/boot/loader
    |- WarLauncher and friends

    View Slide

  143. Jar стал похож на War

    View Slide

  144. View Slide

  145. Замочили web.xml

    View Slide

  146. Замочили web.xml
    Вместе с Tomcat`ом
    Вопросы?

    View Slide

  147. Вспомним Spring MVC
    Перед самым важным

    View Slide

  148. @RestController("/examine")
    Ошибку видишь? А она есть

    View Slide

  149. @RestController("/examine")
    Это имя spring bean, не http path
    И это даже может работать:
    ● Если нет ни одного контроллера с путём (всегда будет вызываться)
    ● Если потом указали полный путь в @RequestMapping

    View Slide

  150. @RequestMapping и его братья
    Управляет маршрутизацией запросов в метод контроллера. Маршрутизирует
    по:
    ● name/value – сам http path
    ● method – http method
    ● params
    ● headers

    View Slide

  151. @RequestMapping и его братья
    @PostMapping("/examine")
    public CheckedExam ex`( @RequestBody SolvedExam title) { }
    @RequestMapping (path = "/examine", method = POST)
    public CheckedExam ex2( @RequestBody SolvedExam title) { }
    @RequestMapping (path = "/examine", method = POST, headers = {"my-app=appid"})
    public CheckedExam ex3( @RequestBody SolvedExam title) { }
    @GetMapping(path = "/examine", headers = {"content-type=application/json"})
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces =
    MimeTypeUtils. APPLICATION_JSON_VALUE)
    public CheckedExam ex3() { }

    View Slide

  152. @RequestMapping и его братья
    @PostMapping("/examine")
    public CheckedExam ex`( @RequestBody SolvedExam title) { }
    @RequestMapping (path = "/examine", method = POST)
    public CheckedExam ex2( @RequestBody SolvedExam title) { }
    @GetMapping(path = "/examine", headers = {"content-type=application/json"})
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces = APPLICATION_JSON_VALUE)
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces = APPLICATION_JSON_VALUE)
    public CheckedExam ex4() { }

    View Slide

  153. @RequestMapping и его братья
    @PostMapping("/examine")
    public CheckedExam ex`( @RequestBody SolvedExam title) { }
    @RequestMapping (path = "/examine", method = POST)
    public CheckedExam ex2( @RequestBody SolvedExam title) { }
    @GetMapping(path = "/examine", headers = {"content-type=application/json"})
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces = APPLICATION_JSON_VALUE)
    public CheckedExam ex3() { }
    @GetMapping(path = "/examine", produces = APPLICATION_JSON_VALUE)
    public CheckedExam ex4() { }

    View Slide

  154. @RequestMapping и его братья
    //curl -XPOST /examine -d '{ "title": {..} }'
    @PostMapping("/examine")
    public CheckedExam ex`( @RequestBody SolvedExam exam) { }
    //curl -XGET /examine/ mytitle
    @GetMapping("/examine/{title}")
    public CheckedExam ex3( @PathVariable String title) { }
    //curl -XGET /examine/mytitle ?debug=true
    @GetMapping("/examine/{title}")
    public CheckedExam ex3( @PathVariable String title, @RequestParam Boolean debug)
    { }

    View Slide

  155. @RequestMapping автовпрыскивание
    @PostMapping("/examine")
    public CheckedExam ex6(HttpServletRequest httpServletRequest,
    Authentication auth,
    UserDetails user,
    etc
    ) { }

    View Slide

  156. Controllers endpoints in logs since Spring 5
    logging.level:
    //print mapped controllers
    org.springframework.web.servlet.mvc.method.annotation: TRACE
    web: DEBUG //print requests to console

    View Slide

  157. Наконец то будем писать код!

    View Slide

  158. Задание 0
    Напишем стартер
    DoD:
    1. Все контроллеры помеченые @FrontendController аннотацией
    должны обворачивать возвращаемый результат в дополнительный Json
    {
    "result": {
    оригинальный_json
    }
    }
    2. https://github.com/lavcraft/spring-boot-and-cloud-ripper-2019
    3.

    View Slide

  159. Talk is cheap. Show me the code
    Linus Torvalds
    https://github.com/lavcraft/spring-boot-and-cloud-ripper-2019

    View Slide

  160. Задание 1. Напишем Экзаминатора
    public static void main(String[] args){
    SpringApplication.run(
    ExaminatorApplication.class,
    args);
    }

    View Slide

  161. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Теология
    Не грешно ли лезть в атом? Теорема Пифагора
    Неравенство Коши
    ваы
    Количество вопросов Количество вопросов
    Экзаминатор

    View Slide

  162. С чего начинают строить микросервис
    →Контроллеры
    →Сервисы
    →Dao
    →Model

    View Slide

  163. С чего начинают строить микросервис
    →Контроллеры
    →Сервисы
    →Dao
    →Model – поскольку модель используется на всех слоях,
    пожалуй лучше начинать с неё

    View Slide

  164. in: {
    "student": "Борисов Евгений",
    "title": "Экзамен на JokerConf",
    "sections": [{
    "title": "Java",
    "description": "простые вопросы по java",
    "exercises": [
    { "question": "разница между spring string и swing",
    "answer": "без spring`а не обойтись" },
    { "question": "разница между final finally finalize",
    "answer": "за finalize отрывают руки" }
    ]},
    {
    "title": "философия",
    "description": "сложные вопросы по философии",
    "exercises": [{
    "question": "В чем смысл жизни",
    "answer": "42"
    }, {
    "question": "Что раньше, яйцо или курица",
    "answer": "петух"
    }]
    }]
    }
    out: {
    "mark": 99,
    "title": "Экзамен на JokerConf",
    "sections": [{
    "title": "Java",
    "description": "простые вопросы по java",
    "exercises": [
    { "question": "разница между spring string и swing",
    "answer": "без spring`а не обойтись" },
    { "question": "разница между final finally finalize",
    "answer": "за finalize отрывают руки" }
    ]},
    {
    "title": "философия",
    "description": "сложные вопросы по философии",
    "exercises": [{
    "question": "В чем смысл жизни",
    "answer": "42"
    }, {
    "question": "Что раньше, яйцо или курица",
    "answer": "петух"
    }]
    }]
    }

    View Slide

  165. Data model - задание 1.1*
    Придумайте архитектуру модели, которая позволит написать Сервис:
    - принимающий на вход объект SolvedExam
    - отдающий CheckedExam с оценкой
    - Должны проходить тесты
    @Test
    public void checkExamineContract() throws Exception {
    ...
    mockMvc.perform(post("/examine"))
    ...
    }
    Use @RestController and @RequestMapping > @*Mapping Luke

    View Slide

  166. Data model - задание 1.1*
    Придумайте архитектуру модели, которая позволит написать Сервис:
    - принимающий на вход объект SolvedExam
    - отдающий CheckedExam с оценкой
    - Смотрите в src/test/resources/*.json
    - Должны проходить тесты
    @Test
    public void checkExamineContract() throws Exception {
    ...
    mockMvc.perform(post("/examine"))
    ...
    }
    Use @RestController and @RequestMapping > @*Mapping Luke

    View Slide

  167. Зло наследования

    View Slide

  168. Композиция намного лучше

    View Slide

  169. У композиции нет границ

    View Slide

  170. У композиции нет границ

    View Slide

  171. Lombok – composition / delegate and friends
    Annotation Processor
    Работает на этапе компиляции
    Генерит код, чтобы мы не писали
    Делает джаву более похожим на нормальный язык

    View Slide

  172. Про lombok, модели и не только
    @Data – POJO (@Getter, @Setter, @ToString, @EqualsAndHashcode)
    @Value – immutable POJO
    @AllArgumentConstruct(onConstructor = @_(@Autowired))

    View Slide

  173. Про lombok, модели и не только
    @Data – POJO (@Getter, @Setter, @ToString, @EqualsAndHashcode)
    @Value – immutable POJO
    @AllArgumentConstruct(onConstructor = @_(@Autowired))
    @RequireArgumentConstructor /@NoArgumentConstructor
    @Builder / @Singular
    @Delegate – примерно как в груви
    @SneakyThrows
    @Slf4j / @Log4j / …

    View Slide

  174. Теперь давайте разбираться Джэксоном

    View Slide

  175. Как Джексон пишет в джейсон
    1. Использует Java Getters
    a. Методы начинающиеся с Get
    2. @JsonIgnore – игнорируем Getter
    3. Нет ни одного Getter – падает

    View Slide

  176. Как Джексон пишет в объект
    1. Нет конструктора с @ConstructProperies
    → выставляет через Setters
    → если нет Setter и есть Getter – выставляет напрямую в филды
    → это все только при наличии пустого конструктора
    2. Есть конструктор с @ConstructProperies
    → выставляет значение через него
    3. После зачем то вызовет все геттеры
    * Но если есть то @JsonIgnore всё по другому
    * Это поведение по умолчанию

    View Slide

  177. Меняем дифолтное поведение
    @JsonIgnoreProperties
    @JsonIgnoreType
    @JsonIgnore
    @JsonProperty

    View Slide

  178. Что не так с JSR 310?

    View Slide

  179. → Если вы настраиваете руками:
    ObjectMapper mapper = new ObjectMapper()
    .registerModule( new ParameterNamesModule())
    .registerModule( new Jdk8Module())
    .registerModule( new JavaTimeModule());
    → Или так:
    mapper.findAndRegisterModules();
    Не забыть зависимости:
    compile 'com.fasterxml.jackson.module:jackson-module-parameter-names'
    compile 'com.fasterxml.jackson.datatype:jackson-datatype-jdk8'
    compile 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
    C последних версий есть поддержка Java8

    View Slide

  180. Достаточно одних зависимостей
    Если вы работаете с Spring Boot 1.x.x

    View Slide

  181. Вообще ничего не нужно
    Если вы работаете с Spring Boot 2.x.x

    View Slide

  182. Как Spring MVC работает с Джексоном
    Напишите метод который принимает json и класс, а возвращает объект
    данного класса
    Пробуем использовать objectMapper Jackson2

    View Slide

  183. @DateTimeFormat
    Ну вы в курсе…
    А ещё можно в application.properties

    View Slide

  184. Кстати о сериалайзерах
    Давайте как то без них, по возможности

    View Slide

  185. Кто использует Jackson в мире Spring MVC
    1. ClassLoader
    2. Dispatcher Servlet
    3. Tomcat
    4. BeanPostProcessor

    View Slide

  186. Кто использует Jackson в мире Spring MVC
    1. ClassLoader
    2. Dispatcher Servlet
    3. Tomcat
    4. BeanPostProcessor

    View Slide

  187. Задание 2
    Микросервис – Теологии
    1. Добавлять вопросы по теологии … (CRUD)
    2. Получать нужное количество рандомальных вопросов
    3. Тест должен проходить
    @Test
    public void should_return_random_exercise() throws Exception {
    mvc.perform(get("/exercise/random?count=3")
    .contentType( APPLICATION_JSON)
    )

    Use JpaRepository and @RequestMapping Luke

    View Slide

  188. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Теология
    Не грешно ли лезть в атом? Теорема Пифагора
    Неравенство Коши
    ваы
    Количество вопросов Количество вопросов
    Экзаминатор
    Person p = restTemplate.getForObject("/{name}/details", Person.class, name);
    Для связи сервисов используем RestTemplate

    View Slide

  189. Конфликт портов — relaxed properties
    1. server.port в application.properties
    2. export SERVER_PORT=8081
    3. java -jar --server.port=8081
    4. SPRING_APPLICATION_JSON = '{"server":{"port":8081}}'

    View Slide

  190. in: {
    "student": "Борисов Евгений",
    "title": "Экзамен на JokerConf",
    "sections": [{
    "title": "Java",
    "description": "простые вопросы по java",
    "exercises": [
    { "question": "разница между spring string и swing",
    "answer": "без spring`а не обойтись" },
    { "question": "разница между final finally finalize",
    "answer": "за finalize отрывают руки" }
    ]
    }, {
    "title": "философия",
    "description": "сложные вопросы по философии",
    "exercises": [{
    "question": "В чем смысл жизни",
    "answer": "42"
    }, {
    "question": "Что раньше, яйцо или курица",
    "answer": "петух"
    }]
    }]
    }

    View Slide

  191. compile 'org.springframework.data:spring-data-rest-webmvc'
    Подключаем Spring Data Rest

    View Slide

  192. Подключаем Spring Data Rest
    compile 'org.springframework.data:spring-data-rest-webmvc'
    или
    compile 'org.springframework.boot:spring-boot-starter-data-rest'

    View Slide

  193. Подключаем Spring Data Rest
    compile 'org.springframework.data:spring-data-rest-webmvc'
    или
    compile 'org.springframework.boot:spring-boot-starter-data-rest'

    View Slide

  194. Дальше пишем Entity + Repository
    Which repositories get exposed by defaults?
    Name Description
    DEFAULT Exposes all public repository interfaces but considers
    @Repository/@RestResource’s `exported flag
    ALL Exposes all repositories independently of type visibility and
    annotations
    ANNOTATION Only repositories annotated with @Repository/@RestResource
    are exposed, unless their exported flag is set to false
    VISIBILITY Only public repositories annotated are exposed

    View Slide

  195. Как поменять стратегию
    @Component
    public class MyWebConfiguration extends RepositoryRestConfigurerAdapter {
    @Override
    public void configureRepositoryRestConfiguration(
    RepositoryRestConfiguration config) {
    config.setRepositoryDetectionStrategy( ALL);
    }
    }

    View Slide

  196. Как задать URL
    @Configuration
    class CustomRestMvcConfiguration {
    @Bean
    public RepositoryRestConfigurer repositoryRestConfigurer() {
    return new RepositoryRestConfigurerAdapter() {
    @Override
    public void configureRepositoryRestConfiguration(
    RepositoryRestConfiguration config) {
    configuration.setBasePath( "/api")
    }
    };
    }
    }

    View Slide

  197. Как задать URL
    @Configuration
    class CustomRestMvcConfiguration {
    @Bean
    public RepositoryRestConfigurer repositoryRestConfigurer() {
    return new RepositoryRestConfigurerAdapter() {
    @Override
    public void configureRepositoryRestConfiguration(
    RepositoryRestConfiguration config) {
    configuration.setBasePath( "/api")
    }
    };
    }
    }
    Или в том же RepositoryRestConfigurerAdapter прописать
    в application.properties - spring.data.rest.base-path=/api

    View Slide

  198. Spring Data Rest official supports
    →Spring Data JPA
    →Spring Data MongoDB
    →Spring Data Neo4j
    →Spring Data GemFire
    →Spring Data Cassandra

    View Slide

  199. @RepositoryRestResource
    @RepositoryRestResource (collectionResourceRel = "people", path = "people")
    public interface PersonRepository extends MongoRepository {
    @RestResource(path = "byname")
    List findByLastName(@Param( "name") String name);
    }

    View Slide

  200. URL’s и методы
    localhost:8080/exercise – список всех (GET)
    localhost:8080/exercise/1 – дай человека с айдишником 1 (GET)
    localhost:8080/exercise/1 – стереть с айдишником 1 (DELETE)
    localhost:8080/exercise/1 – заменить с айдишником 1 (PUT)
    localhost:8080/exercise/1 – проапдэйтить с айдишником 1 (PATCH)
    localhost:8080/exercise/search/findByName?name=Lanister

    View Slide

  201. {
    "География":5,
    "Физика" :3
    }
    Физика Математика
    Теология
    Не грешно ли лезть в атом? Теорема Пифагора
    Неравенство Коши
    ваы
    Количество вопросов Количество вопросов
    Экзаминатор

    View Slide

  202. Задание 3
    Связать сервисы Экзаменатора и Предметы
    ● Запустить и проверить самим
    ● Должен отдаваться запрос с ответами другого сервиса
    {
    "География":5,
    "Физика" :3
    }

    View Slide

  203. --server.port
    Конфликт портов решается с помощью relaxed properties
    export SERVER_PORT=8081
    java -jar --server.port=8081
    SPRING_APPLICATION_JSON = '{"server":{"port":8081}}'

    View Slide

  204. RestTemplate
    ● you can create it even with new / or inject configured bean
    ● postForEntity / postForObject/ getForEntity / getForObject
    restTemplate.getForObject(
    "http://"+serviceName+"/exercice/random?count=" + number,
    Exercice[].class);
    ● exchange
    restTemplate.exchange(url.toString(),
    HttpMethod.POST,
    entity,
    String.class);

    View Slide

  205. Шаринг кода
    модель/бизнес
    логика/инфраструктурный код
    ● Шарить
    ● Или не шарить

    View Slide

  206. Подход №1 - общий код
    Зачем разбивать микросервис на модули?
    → Java API для сервисов
    → фиксация контракта на уровне зависимостей языка
    → утилитарная функция, избавления от бойлерплейта

    View Slide

  207. Подход №1 - общий код
    project
    |- project-sdk
    |- project-app
    |- project-...

    View Slide

  208. Подход №1 - общий код
    project
    |- project-sdk
    |- project-app
    |- project-api
    |- project-domain
    |- project-...
    Пфф

    View Slide

  209. Подход №1 - общий код
    project
    |- project-sdk
    |- project-app
    |- project-api
    |- project-domain
    |- project-...
    Пфф
    KISS

    View Slide

  210. Подход №2 - общий контракт
    → разделение кода и контракта
    → поставка контракта отдельно

    View Slide

  211. Обратимся к истории
    → REST подход

    → RPC подход

    View Slide

  212. Обратимся к истории
    → REST подход
    ○ анархия и рекомендации
    → RPC подход

    View Slide

  213. Обратимся к истории
    → REST подход
    ○ анархия и рекомендации
    → RPC подход
    ○ jax-ws/jax-rpc
    ○ corba
    ○ json rpc


    View Slide

  214. Обратимся к истории
    → REST подход
    ○ анархия и рекомендации
    → RPC подход
    ○ jax-ws/jax-rpc
    ○ corba
    ○ json rpc
    ○ thrift
    ○ protobuf/grpc
    ○ etc

    View Slide

  215. Задание 5
    ● пишем SDK для вызова микросервиса по теологии
    ● и тесты для него
    ● тестируем контракт
    ● тестируем взаимодействие
    ○ WireMock/Spring MockServer
    ○ Spring Boot Test
    ○ Context Scanning behaviour
    Тест SDK Тест Контракта Тест Взаимодействия

    View Slide

  216. Разбиваем микросервис на модули
    Кому отсыпать
    наносервисов?

    View Slide