Slide 1

Slide 1 text

Spring {Boot,Cloud} Workshop

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

The project structure

Slide 7

Slide 7 text

web.xml org.springframework.web.context.ContextLoaderListener springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /* mvc-dispatcher org.springframework.web.servlet.DispatcherServlet 1 mvc-dispatcher /

Slide 8

Slide 8 text

No content

Slide 9

Slide 9 text

No content

Slide 10

Slide 10 text

… contextClass …AnnotationConfigWebApplicationContext contextConfigLocation com.inwhite.conf.AppConfig …

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

WebApplicationInitializer

Slide 17

Slide 17 text

ServletContainerInitializer

Slide 18

Slide 18 text

WebApplicationInitializer

Slide 19

Slide 19 text

ServletContainerInitializer

Slide 20

Slide 20 text

No content

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

SPI org.springframework.web.SpringServletContainerInitializer content

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

@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)); }

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

Spring ServletContainer Initializer tomcat 7 Spring Jar

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

WebApplicationInitializer W AI WAI tomcat 7 JARS

Slide 39

Slide 39 text

W AI WAI SpringServletContainerInitializer tomcat 7

Slide 40

Slide 40 text

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("/*"); }

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

Как выглядит наш pom.xml org.springframework.boot spring-boot-starter-parent 2.1.4.RELEASE

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

No content

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

Это чужой parent!

Slide 48

Slide 48 text

No content

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

А я не понял

Slide 55

Slide 55 text

Web Starter ТАЩИТ!

Slide 56

Slide 56 text

Кто всё пакует org.springframework.boot spring-boot-maven-plugin

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

No content

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

JarLauncher

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

org.springframework.boot spring-boot-maven-plugin conference...OurMainClass MANIFEST.MF ... Start-Class: conference.spring.boot.ripper.OurMainClass ...

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

Хочу executable jar: Maven org.springframework.boot spring-boot-maven-plugin true springBoot { executable = true } Gradle

Slide 71

Slide 71 text

Demo. Executable jar

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

Demo. jar structure

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

Типы ConfigurableApplicationContext

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

Web Context Generic Context

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

Web Context Generic Context

Slide 86

Slide 86 text

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

Slide 87

Slide 87 text

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

Slide 88

Slide 88 text

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

Slide 89

Slide 89 text

Откуда 436 spring beans?

Slide 90

Slide 90 text

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

Slide 91

Slide 91 text

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

Slide 92

Slide 92 text

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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

@SpringBootApplication

Slide 95

Slide 95 text

@SpringBootApplication → @ComponentScan → @Configuration → @EnableAutoConfiguration

Slide 96

Slide 96 text

@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 { …

Slide 97

Slide 97 text

@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 { …

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

@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

Slide 101

Slide 101 text

@EnableAutoConfiguration ImportSelector Web Starter Boot Starter … Starter Mongo Starter

Slide 102

Slide 102 text

Spring Factories Loader Jar/Starter spring.factories SpringFactoriesLoader

Slide 103

Slide 103 text

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

Slide 104

Slide 104 text

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

Slide 105

Slide 105 text

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

Slide 106

Slide 106 text

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

Slide 107

Slide 107 text

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); }

Slide 108

Slide 108 text

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

Slide 109

Slide 109 text

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

Slide 110

Slide 110 text

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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

@ConditionalOn***

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

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

Slide 116

Slide 116 text

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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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 ФабрикаГильотин( "хрусть хрусть"); } }

Slide 119

Slide 119 text

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. Будет отлично работать

Slide 120

Slide 120 text

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. Будет отлично работать

Slide 121

Slide 121 text

Потому что ASM

Slide 122

Slide 122 text

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; } Что если нужно изменить?

Slide 123

Slide 123 text

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

Slide 124

Slide 124 text

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("/*"); }

Slide 125

Slide 125 text

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("/*"); }

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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); } }

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

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

Slide 131

Slide 131 text

§ 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); } }

Slide 132

Slide 132 text

§ 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); } }

Slide 133

Slide 133 text

§ 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); } } Работает по разному

Slide 134

Slide 134 text

§ 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); } }

Slide 135

Slide 135 text

§ 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

Slide 136

Slide 136 text

§ 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 плагины Обязательно

Slide 137

Slide 137 text

§ 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); } } Опционально

Slide 138

Slide 138 text

§ 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 контейнере Опционально

Slide 139

Slide 139 text

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

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

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

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

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

Slide 144

Slide 144 text

No content

Slide 145

Slide 145 text

Замочили web.xml

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

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

Slide 148

Slide 148 text

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

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

@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() { }

Slide 152

Slide 152 text

@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() { }

Slide 153

Slide 153 text

@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() { }

Slide 154

Slide 154 text

@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) { }

Slide 155

Slide 155 text

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

Slide 156

Slide 156 text

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

Slide 157

Slide 157 text

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

Slide 158

Slide 158 text

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

Slide 159

Slide 159 text

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

Slide 160

Slide 160 text

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

Slide 161

Slide 161 text

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

Slide 162

Slide 162 text

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

Slide 163

Slide 163 text

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

Slide 164

Slide 164 text

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": "петух" }] }] }

Slide 165

Slide 165 text

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

Slide 166

Slide 166 text

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

Slide 167

Slide 167 text

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

Slide 168

Slide 168 text

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

Slide 169

Slide 169 text

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

Slide 170

Slide 170 text

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

Slide 171

Slide 171 text

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

Slide 172

Slide 172 text

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

Slide 173

Slide 173 text

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

Slide 174

Slide 174 text

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

Slide 175

Slide 175 text

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

Slide 176

Slide 176 text

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

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

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

Slide 179

Slide 179 text

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

Slide 180

Slide 180 text

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

Slide 181

Slide 181 text

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

Slide 182

Slide 182 text

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

Slide 183

Slide 183 text

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

Slide 184

Slide 184 text

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

Slide 185

Slide 185 text

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

Slide 186

Slide 186 text

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

Slide 187

Slide 187 text

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

Slide 188

Slide 188 text

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

Slide 189

Slide 189 text

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

Slide 190

Slide 190 text

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": "петух" }] }] }

Slide 191

Slide 191 text

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

Slide 192

Slide 192 text

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

Slide 193

Slide 193 text

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

Slide 194

Slide 194 text

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

Slide 195

Slide 195 text

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

Slide 196

Slide 196 text

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

Slide 197

Slide 197 text

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

Slide 198

Slide 198 text

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

Slide 199

Slide 199 text

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

Slide 200

Slide 200 text

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

Slide 201

Slide 201 text

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

Slide 202

Slide 202 text

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

Slide 203

Slide 203 text

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

Slide 204

Slide 204 text

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);

Slide 205

Slide 205 text

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

Slide 206

Slide 206 text

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

Slide 207

Slide 207 text

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

Slide 208

Slide 208 text

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

Slide 209

Slide 209 text

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

Slide 210

Slide 210 text

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

Slide 211

Slide 211 text

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

Slide 212

Slide 212 text

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

Slide 213

Slide 213 text

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

Slide 214

Slide 214 text

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

Slide 215

Slide 215 text

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

Slide 216

Slide 216 text

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