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

Boot yourself, Spring is coming VoxxedDays Minsk 2018

Boot yourself, Spring is coming VoxxedDays Minsk 2018

Many years ago Java developers used "new" in order to create Java services. They had many handmade configurations mixed with business logic; they even were using copy-paste techniques. They wrote many lines of the same ugly code, which sometimes even worked.

Then Spring happened and things have changed… We received a lot of "magic" from Spring black box and our code became much cleaner and more simple.

And then Spring Boot happened… On the one hand, it solved thousands of problems: versions conflict, configuration issues, infrastructure's beans declarations, problems with environments, and even running or deploying applications including building jar/war… On the other hand, Spring Boot uses so much magic that mostly we come across two scenarios:

Everything is working without any effort even though nobody knows why.
Nothing is working and nobody knows why.
In this talk, we'll try to reveal at least a part of Spring Boot magic by discovering Spring Boot concepts, conventions and the way it works. And even though after this talk you will understand that there is no magic (mostly), you will enjoy Spring Boot even more, because you will be able to solve Spring Boot problems or conflicts without calling 911 (mostly).

Kirill Tolkachev

May 26, 2018
Tweet

More Decks by Kirill Tolkachev

Other Decks in Technology

Transcript

  1. Spring Boot
    The Ripper

    View Slide

  2. @tolkv
    @lavcraft
    @jekaborisov
    @jeka1978

    View Slide

  3. За что мы не любим Spring Boot 2.0 в РФ
    private static boolean isAlpha(char ch) {
    return ch >= 'a' && ch <= 'z';
    }
    ..
    @ConfigurationProperties

    View Slide

  4. За что мы не любим Spring Boot 2.0 в РФ
    private static boolean isAlpha(char ch) {
    return ch >= 'a' && ch <= 'z';
    }
    ..
    @ConfigurationProperties
    как:
    пройти:
    в-библиотеку: не знаю

    View Slide

  5. ЗАБАСТОВКА БИНОВ ПОДАВЛЕНА,
    СИНГЛТОНЫ НЕ ВЫЙДУТ ИЗ
    КОНТЕЙНЕРА ДО КОНЦА СРОКА

    View Slide

  6. ЛЕНИВЫЕ СИНГЛТОНЫ МОГУТ НЕ
    СОЗДАВАТЬСЯ С ВЕРСИИ 4.3

    View Slide

  7. Application Listener-ы требуют получать
    дополнительные ивенты

    View Slide

  8. ApplicationListener
    Они объявили голодовку и их жизни в
    опасности

    View Slide

  9. Йорген Холлер не подписал заявление
    XmlBeanDefinitionReader-а об уходе на пенсию

    View Slide

  10. Они ещё Spring Boot-у послужат,
    прокомментировал он на Javaday 2016.

    View Slide

  11. ClassPathBeanDefinitionScanner ищет
    компоненты, но на находит сервисы

    View Slide

  12. BeanPostProcessor-ы требуют
    льготных условий. Spring
    держится лишь на нас

    View Slide

  13. BeanFactory изменяет Single Responsibility

    View Slide

  14. Теперь он выглядит вот так:

    View Slide

  15. Ретросперктива
    Bean
    Factory

    View Slide

  16. Application
    Listener
    ClassPatBeanDefinitionScanner
    BPP
    BPP
    Bean
    Factory
    BPP

    View Slide

  17. Application
    Listener
    ClassPatBeanDefinitionScanner
    BPP
    BPP
    Bean
    Factory
    BPP
    Context

    View Slide

  18. Application
    Listener
    ClassPatBeanDefinitionScanner
    BPP
    BPP
    Bean
    Factory
    BPP
    Context
    WAI
    EPP
    SApp
    ENV
    system prop
    application.
    yml
    SpringFactoriesLoader
    RDB
    CFL
    Starter mn,,
    spring.factories

    View Slide

  19. Spring Boot
    The Ripper

    View Slide

  20. View Slide

  21. _Демо_

    View Slide

  22. Что не любит программист
    Думать о:
    1. Зависимостях...
    2. Настройках и конфигурациях
    Делать:
    3. Деплоймент и запуск (тест/локально/прод)

    View Slide

  23. Что не любит программист
    Думать о:
    1. Зависимостях...
    2. Настройках и конфигурациях
    Делать:
    3. Деплоймент и запуск (тест/локально/прод)

    View Slide

  24. Зависимости

    View Slide

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

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

    View Slide


  26. org.springframework.boot
    spring-boot-starter-parent
    1.5.13.RELEASE


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

    View Slide

  27. View Slide

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

    View Slide

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

    View Slide

  30. View Slide




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



    View Slide

  32. Тоже самое в build.gradle
    dependencyManagement {
    imports {
    mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Dalston.RELEASE'
    }
    }

    View Slide

  33. Какие теперь зависимости?
    'org.springframework.boot:spring-boot-starter-web'
    'org.springframework.boot:spring-boot-starter-data-jpa'
    'com.h2database:h2'

    View Slide

  34. Настройки контекста

    View Slide

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

    View Slide

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

    View Slide

  37. Загадочные аргументы
    Дано: RipperApplication.class
    public… main(String[] args) {
    SpringApplication.run(?,args);
    }
    1. RipperApplication.class
    2. String.class
    3. "context.xml"
    4. new ClassPathResource("context.xml")
    5. Package.getPackage("conference.spring.boot.ripper")

    View Slide

  38. SpringApplication.run(Object[] sources, String[] args)

    View Slide

  39. Загадочные аргументы
    Дано: RipperApplication.class
    public… main(String[] args) {
    SpringApplication.run(?,args);
    }
    1. RipperApplication.class
    2. String.class
    3. "context.xml"
    4. new ClassPathResource("context.xml")
    5. Package.getPackage("conference.spring.boot.ripper")
    Голосуем!

    View Slide

  40. Все верны

    View Slide

  41. SpringApplication.run(Object[] sources, String[] args)
    # APPLICATION SETTINGS (SpringApplication)
    spring.main.sources= # class name, package name, xml location
    spring.main.web-environment= # true/false
    spring.main.banner-mode=console # log/off

    View Slide

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

    View Slide

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

    View Slide

  44. Типы ConfigurableApplicationContext

    View Slide

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

    View Slide

  46. Web Context Generic Context

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  53. Железный закон №1
    В любой непонятной ситуации посылай ворона

    View Slide

  54. Может @EnableSomeStarter ?

    View Slide

  55. Ты еще @Import(SomeStarterConf.class)
    предложи сделать!

    View Slide

  56. Мы будем использовать spring.factories!

    View Slide

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

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

    View Slide

  59. Железный закон №1.1
    В любой непонятной ситуации посылай ворона

    View Slide

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

    View Slide

  61. @SpringBootApplication

    View Slide

  62. @SpringBootApplication
    → @ComponentScan
    → @Configuration
    → @EnableAutoConfiguration

    View Slide

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

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

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

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

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

  68. @EnableAutoConfiguration
    ImportSelector
    Boot Starter

    Starter
    Web Starter
    Iron Starter

    View Slide

  69. ImportSelector
    Iron Starter
    Boot Starter

    Starter
    Web Starter
    @EnableAutoConfiguration

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

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

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

    View Slide

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

    View Slide

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

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  83. Железный закон №1.2
    Посылай ворона только в продакшене

    View Slide

  84. @ConditionalOnProduction — Демо

    View Slide

  85. Паззлер
    @Configuration
    @ConditionalOnСуроваяЗима
    public class UndeadArmyConfiguration {
    ...
    }
    @Configuration
    public class DragonIslandConfiguration {
    @Bean
    @ConditionalOnСуроваяЗима
    public DragonGlassFactory dragonGlassFactory() {
    return new DragonGlassFactory();
    }
    ...
    }

    View Slide

  86. 1. 100 $
    2. 200 $
    3. 300 или 400 $
    4. между 500 и 1000 $
    5. Больше 1000 $
    * Спросить погоду стоит
    денег – 100 золотых
    драконов за раз
    * $ = золотой дракон
    ConditionalOnСуроваяЗима

    View Slide

  87. 1. 100 $
    2. 200 $
    3. 300 или 400 $
    4. между 500 и 1000 $
    5. Больше 1000 $
    * Спросить погоду стоит
    денег – 100 золотых
    драконов за раз
    * $ = золотой дракон
    @Configuration
    @ConditionalOnСуроваяЗима
    public class UndeadArmyConfiguration {
    ...
    }
    @Configuration
    public class DragonIslandConfiguration {
    @Bean
    @ConditionalOnСуроваяЗима
    public DragonGlassFactory dragonGlassFactory() {
    return new DragonGlassFactory();
    }
    ...
    }
    ConditionalOnСуроваяЗима

    View Slide

  88. Правильный ответ
    1. 100 $
    2. 200 $
    3. 300 или 400 $
    4. между 500 и 1000 $
    5. Больше 1000 $

    View Slide

  89. Правильный ответ
    1. 100 $
    2. 200 $
    3. 300 или 400 $
    4. между 500 и 1000 $
    5. Больше 1000 $
    300 – если UndeadArmyConfiguration в стартере
    400 – если UndeadArmyConfiguration в коде приложения

    View Slide

  90. Как это
    работает ?

    View Slide

  91. Как то так, через хитро
    закрученную жопу
    оно и работает

    View Slide

  92. Лечу
    куда хочу

    View Slide

  93. Железный закон №1.3
    Отправлять ворона только если есть получатели
    1. список получателей
    2. без списка не летим
    3. автокомплит
    Лететь то куда?

    View Slide

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

    View Slide

  95. Железный закон №1.3
    Отправлять ворона только если есть получатели
    1. список получателей
    2. без списка не летим
    3. автокомплит
    Лететь то куда?
    ← application.yml
    ← @ConditionalOnProperty
    ← @ConfigurationProperty

    View Slide

  96. Железный закон №1.4
    Отправляем ворона только если нет кастомного

    View Slide

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

    View Slide

  98. Железный закон №1.4
    Отправляем ворона только если нет кастомного
    @ConditionalOnMissingBean

    View Slide

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

    View Slide

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

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

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

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

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

    View Slide

  105. Железный закон №1.5
    ВКЛ/ВЫКЛ ворона
    @ConditionalOnProperty

    View Slide

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

  107. OnPropertyCondition
    @ConditionalOnProduction
    @ConditionalOnProperty(
    name = "ворон.вкл"
    )
    public IronBankApplicationListener applicationListener() {
    ...
    }

    View Slide

  108. OnPropertyCondition
    @ConditionalOnProduction
    @ConditionalOnProperty(
    name = "ворон.вкл",
    havingValue = "true"
    )
    public IronBankApplicationListener applicationListener() {
    ...
    }

    View Slide

  109. OnPropertyCondition
    @ConditionalOnProduction
    @ConditionalOnProperty(
    name = {
    "ворон.вкл",
    "зима.вкл",
    "старки.вкл"
    },
    havingValue = "true"
    )
    public IronBankApplicationListener applicationListener() {
    ...
    }

    View Slide

  110. OnPropertyCondition
    @ConditionalOnProduction
    @ConditionalOnProperty(
    name = {
    "ворон.вкл",
    "зима.вкл",
    "старки.вкл"
    },
    havingValue = "true"
    )
    public IronBankApplicationListener applicationListener() {
    ...
    }
    Не массив

    View Slide

  111. OnPropertyCondition
    @ConditionalOnProduction
    @ConditionalOnProperty(name = "ворон.вкл", havingValue="true")
    @ConditionalOnProperty(name = "зима.вкл", havingValue="true")
    @ConditionalOnProperty(name = "старки.вкл",havingValue="false")
    public IronBankApplicationListener applicationListener() {
    ...
    }

    View Slide

  112. OnPropertyCondition
    @ConditionalOnProduction
    @ConditionalOnProperty(name = "ворон.вкл", havingValue="true")
    @ConditionalOnProperty(name = "зима.вкл", havingValue="true")
    @ConditionalOnProperty(name = "старки.вкл",havingValue="false")
    public IronBankApplicationListener applicationListener() {
    ...
    }
    @ConditionalOnProperty Не @Repeatable

    View Slide

  113. AllNestedConditions
    &&
    AnyNestedCondition

    View Slide

  114. OnPropertyCondition
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ ElementType.TYPE, ElementType.METHOD })
    @Conditional(CompositeCondition.class)
    public @interface ConditionalOnRaven {
    }

    View Slide

  115. OnPropertyCondition
    public class OnRavenCondition extends AllNestedConditions {
    @ConditionalOnProperty(
    name = "ворон.куда",
    havingValue = "false")
    public static class OnRavenProperty { }
    @ConditionalOnProperty(
    name = "ворон.вкл",
    havingValue = "true",
    matchIfMissing = true)
    public static class OnRavenEnabled { }
    ...
    }

    View Slide

  116. OnPropertyCondition
    @ConditionalOnRaven
    public IronBankApplicationListener applicationListener() {
    ...
    }

    View Slide

  117. Сейчас будет спойлер

    View Slide

  118. Сезон 8 – Зима Тута
    Кофебрейк

    View Slide

  119. Внимание спойлеры

    View Slide

  120. В 8 сезоне
    Железный банк изменит правила выдачи кредитов
    — Демо

    View Slide

  121. Железный закон №2
    Без профиля - нельзя

    View Slide

  122. Demo
    ApplicationContextInitializer

    View Slide

  123. Environment
    Property sources:
    ● systemProperties
    ● system environment
    ● random property
    ● application.properties
    ● …
    Active Profiles
    Default Profiles
    ENV
    system prop
    application.yml
    ...

    View Slide

  124. Почему бы не определить профиль
    автоматически?

    View Slide

  125. Напишем… EnvironmentPostProcessor
    EPP

    View Slide

  126. demo

    View Slide

  127. ApplicationListener
    EPP
    ConfigFileApplicationListener

    View Slide

  128. Карлсон строит environment

    View Slide

  129. Карлсон дает листенеру environment и себя

    View Slide

  130. ConfigFileApplicationListener
    Слушает
    ● ApplicationPreparedEvent
    ● ApplicationEnvironmentPreparedEvent
    Загружает
    ● application.yml
    ● application.properties
    ● env vars
    ● cmd args

    View Slide

  131. onApplicationEnvironmentPreparedEvent
    SpringFactoriesLoader
    Сгоняй за
    EnvironmentPostProcessor-ами

    View Slide

  132. onApplicationEnvironmentPreparedEvent
    SpringFactoriesLoader
    SpringFactoriesLoader.
    loadFactories(
    EnvironmentPostProcessor.class
    …)

    View Slide

  133. EPP
    onApplicationEnvironmentPreparedEvent
    SpringFactoriesLoader
    EPP

    View Slide

  134. onApplicationEnvironmentPreparedEvent
    EPP
    EPP
    postProcessEnvironment
    ща, отсортирую и
    поедем...

    View Slide

  135. onApplicationEnvironmentPreparedEvent
    EPP
    EPP
    postProcessEnvironment
    Как то он
    на нас
    похож...

    View Slide

  136. onApplicationEnvironmentPreparedEvent
    EPP
    EPP
    postProcessEnvironment
    Я с вами
    пацаны...

    View Slide

  137. onApplicationEnvironmentPreparedEvent
    EPP
    EPP
    postProcessEnvironment

    View Slide

  138. ConfigFileApplicationListener
    Слушает
    ● ApplicationPreparedEvent
    ● ApplicationEnvironmentPreparedEvent
    Загружает
    ● application.yml
    ● application.properties
    ● env vars
    ● cmd args
    spring.factories
    SpringFactoriesLoader

    View Slide

  139. spring.factories
    ConfigFileApplicationListener
    Слушает
    ● ApplicationPreparedEvent
    ● ApplicationEnvironmentPreparedEvent
    Загружает
    ● application.yml
    ● application.properties
    ● env vars
    ● cmd args
    SpringFactoriesLoader

    View Slide

  140. А я слышала
    про другие события!
    ContextStartedEvent
    ContextStoppedEvent
    ContextRefreshedEvent
    ContextClosedEvent

    View Slide

  141. Это Spring Boot,
    Тут больше event-ов

    View Slide

  142. Application Events
    ApplicationStartingEvent
    ApplicationEnvironmentPreparedEvent
    ApplicationPreparedEvent
    ContextRefreshedEvent
    EmbeddedServletContainerInitializedEvent
    ApplicationReadyEvent
    ApplicationFailedEvent
    ...

    View Slide

  143. Application Events
    ApplicationStartingEvent
    ApplicationEnvironmentPreparedEvent
    ApplicationPreparedEvent
    ContextRefreshedEvent
    EmbeddedServletContainerInitializedEvent
    ApplicationReadyEvent
    ApplicationFailedEvent
    ContextClosedEvent

    View Slide

  144. Application Events
    ApplicationStartingEvent
    ApplicationEnvironmentPreparedEvent
    ApplicationPreparedEvent
    ContextRefreshedEvent
    EmbeddedServletContainerInitializedEvent
    ApplicationReadyEvent
    ApplicationFailedEvent
    ContextClosedEvent

    View Slide

  145. Application Events
    ApplicationStartingEvent
    ApplicationEnvironmentPreparedEvent
    ApplicationPreparedEvent
    ContextRefreshedEvent
    EmbeddedServletContainerInitializedEvent
    ApplicationReadyEvent
    ApplicationFailedEvent
    ContextClosedEvent

    View Slide

  146. Application Events
    ApplicationStartingEvent
    ApplicationEnvironmentPreparedEvent
    ApplicationPreparedEvent
    ContextRefreshedEvent
    EmbeddedServletContainerInitializedEvent
    ApplicationReadyEvent
    ApplicationFailedEvent
    ContextClosedEvent

    View Slide

  147. А это, забыли?
    ContextStartedEvent
    ContextStoppedEvent

    View Slide

  148. Найди их место
    ApplicationStartingEvent
    ApplicationEnvironmentPreparedEvent
    ApplicationPreparedEvent
    ContextRefreshedEvent
    EmbeddedServletContainerInitializedEvent
    ApplicationReadyEvent
    ApplicationFailedEvent
    ContextClosedEvent
    ContextStartedEvent
    ContextStoppedEvent
    1
    2
    3
    4
    5
    ?

    View Slide

  149. Эти ивенты
    никогда не работают

    View Slide

  150. А ты их вызывать не
    пробовал?

    View Slide

  151. А ты их вызывать не
    пробовал?
    ctx.start(); →
    ctx.stop(); →
    ContextStartedEvent
    ContextStoppedEvent

    View Slide

  152. Где вылетит эксепшен
    ctx.stop(); (1)
    ctx.start(); (2)
    ctx.close(); (3)
    ctx.start(); (4)

    View Slide

  153. Где вылетит эксепшен
    ctx.stop(); (1)
    ctx.start(); (2)
    ctx.close(); (3)
    ctx.start(); (4)
    1. на строчке (1)
    2. на строчке (2)
    3. на строчке (3)
    4. на строчке (4)

    View Slide

  154. Где вылетит эксепшен
    ctx.stop(); (1)
    ctx.start(); (2)
    ctx.close(); (3)
    ctx.start(); (4)
    1. на строчке (1)
    2. на строчке (2)
    3. на строчке (3)
    4. на строчке (4)
    5. не вылетит вообще

    View Slide

  155. Где вылетит эксепшен
    ctx.stop(); (1)
    ctx.start(); (2)
    ctx.close(); (3)
    ctx.start(); (4)
    1. на строчке (4)

    View Slide

  156. Stop before Start

    View Slide

  157. Где вылетит эксепшен
    ctx.stop(); (1)
    ctx.start(); (2)
    ctx.close(); (3)
    ctx.start(); (4)
    1. на строчке (4)

    View Slide

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

    View Slide

  159. Как запускать
    1. tomcat war
    2. idea
    3. java -jar/war

    View Slide

  160. Кто пакует Jar со всеми зависимостями



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



    View Slide

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

    View Slide

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

    View Slide

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

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

  165. Анатомия SpringBoot Jar
    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/
    ...
    JarLauncher

    View Slide

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

    View Slide




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



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

    View Slide

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

    View Slide

  169. Вот если бы можно:
    жмя и запустилось...
    java -jar
    – слишком сложно для нас

    View Slide

  170. Хочу executable jar: Maven



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

    true



    springBoot {
    executable = true
    }
    Gradle

    View Slide

  171. Demo. Executable jar

    View Slide

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

    View Slide

  173. Demo. Executable jar structure

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  177. Немного выводов
    1. Spring Boot вышел за пределы Spring Context
    2. Starter от команды Spring отличаются от public convention
    ○ не всегда в лучшую сторону
    3. в Spring Boot это целый мир, в котором нет магии

    View Slide

  178. View Slide

  179. @tolkv
    @lavcraft
    @jekaborisov
    @jeka1978

    View Slide

  180. Вопросы?

    View Slide