$30 off During Our Annual Pro Sale. View Details »

Spring Boot Training Leroy Merlin

Spring Boot Training Leroy Merlin

Spring core + internals overview (IOC, BPP, Java Config); 
• Spring Data (main concepts, Repository interface, dynamic proxy); 
• Spring Boot (how magic happens, @Conditional, @OnBeanCondition); 
• Web & Spring MVC: 
◦ Servlet – do they still exist? 
Servlet – do they still exist?
◦ REST; 
◦ Controllers / RestControllers; 
◦ RestTemplate; 
◦ Tomcat (web.xml, without web.xml, without Tomcat). 

Kirill Tolkachev

June 13, 2019
Tweet

More Decks by Kirill Tolkachev

Other Decks in Technology

Transcript

  1. Spring {Boot,Cloud}
    Workshop

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  5. All your classes
    All your pages and xmls
    Regular web.xml with spring dispatcher
    servlet, spring listener and security filter name
    Security filters, users
    and roles
    Spring MVC beans
    Application beans,
    Import security.xml
    The project structure

    View Slide

  6. 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

  7. View Slide

  8. View Slide



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


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


    View Slide

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

    View Slide

  11. How does it work?
    User type in his browser: http://localhost:8080/myApp
    create Spring
    ContextLoaderListener
    Start tomcat
    start applicationContext
    In bootsrap
    In bootsrap
    Create filter
    mapping
    Create
    security filter
    login
    Create spring
    dispatcher
    servlet
    Delegate to
    Controller

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  15. WebApplicationInitializer

    View Slide

  16. ServletContainerInitializer

    View Slide

  17. WebApplicationInitializer

    View Slide

  18. ServletContainerInitializer

    View Slide

  19. View Slide

  20. § 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

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

    View Slide

  22. § 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

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

    View Slide

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

    View Slide

  25. Как? 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

  26. SPI
    org.springframework.web.SpringServletContainerInitializer
    content

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  33. @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

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

    View Slide

  35. Spring
    ServletContainer
    Initializer
    tomcat 7
    Spring
    Jar

    View Slide

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

    View Slide

  37. WebApplicationInitializer
    W
    AI
    WAI
    tomcat 7
    JARS

    View Slide

  38. W
    AI
    WAI
    SpringServletContainerInitializer
    tomcat 7

    View Slide

  39. 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

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

    View Slide

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

    View Slide

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

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

    View Slide


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


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

    View Slide

  44. View Slide

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

    View Slide

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

    View Slide

  47. View Slide




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



    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  52. Как выглядит наш 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

  53. А я не понял

    View Slide

  54. Web Starter ТАЩИТ!

    View Slide

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



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



    View Slide

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

    View Slide

  57. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  61. JarLauncher

    View Slide

  62. Анатомия 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

  63. 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

  64. 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

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

    View Slide




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

    conference...OurMainClass




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

    View Slide

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

    View Slide

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

    View Slide

  69. Хочу executable jar: Maven



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

    true




    springBoot {
    executable = true
    }
    Gradle bootJar {
    launchScript()
    }
    Spring boot 1.x.x
    Spring boot 2.x.x

    View Slide

  70. Demo. Executable jar

    View Slide

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

    View Slide

  72. Demo. jar structure

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  80. Типы ConfigurableApplicationContext

    View Slide

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

    View Slide

  82. Web Context Generic Context

    View Slide

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

    View Slide

  84. Web Context Generic Context

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  90. Не знаю как, но мы тоже так сделаем

    View Slide

  91. Задание
    1. Печатать предупреждение при вызове
    @Deprecated
    2. Печатать время исполнения методов
    @Benchmark
    3. * Отправить email на адрес alarm.email если
    вызвали @Deprecated или @Benchmark

    View Slide

  92. AOP
    ● Joint Point
    ● Pontcut
    ● Advise
    ● Aspect

    View Slide

  93. Pointcut
    ● execution
    ● within
    ● this
    ● target
    ● args
    ● bean
    ● @annotation

    View Slide

  94. Пример
    @Aspect
    @Component
    public class TestAspect {
    @Pointcut("execution(* *.doSomething(..)) && args(list,..) ")
    public void callMethod(List list) {
    ...
    }
    }

    View Slide

  95. Что за 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

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

    View Slide

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

    View Slide

  98. @SpringBootApplication

    View Slide

  99. @SpringBootApplication
    → @ComponentScan
    → @Configuration
    → @EnableAutoConfiguration

    View Slide

  100. @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

  101. @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

  102. @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

  103. @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

  104. @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

  105. @EnableAutoConfiguration
    ImportSelector
    Web Starter Boot Starter

    Starter
    Mongo Starter

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  109. 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

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

    View Slide

  111. 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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  117. @ConditionalOn***

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  121. 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

  122. 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

  123. 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

  124. 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

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

    View Slide

  126. 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

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

    View Slide

  128. 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

  129. 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

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

    View Slide

  131. 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

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

    View Slide

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

    View Slide

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

    View Slide

  135. § 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

  136. § 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

  137. § 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

  138. § 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

  139. § 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

  140. § 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

  141. § 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

  142. § 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

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

    View Slide

  144. А как убрать 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

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

    View Slide

  146. Анатомия 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

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

    View Slide

  148. View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  155. @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

  156. @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

  157. @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

  158. @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

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

    View Slide

  160. 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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  168. 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

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

    View Slide

  170. 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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  183. → Если вы настраиваете руками:
    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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  191. Задание 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

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

    View Slide

  193. Конфликт портов — 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

  194. 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

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

    View Slide

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

    View Slide

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

    View Slide

  198. Дальше пишем 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

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

    View Slide

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

    View Slide

  201. Как задать 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

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

    View Slide

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

    View Slide

  204. 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

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

    View Slide

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

    View Slide

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

    View Slide

  208. 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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    → RPC подход

    View Slide

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

    View Slide

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


    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  221. Конец 1й части
    приходите на Spring Boot Ripper

    View Slide