Slide 1

Slide 1 text

Кодогенерация Кирилл Меркушев руководитель группы автоматизации процессов разработки и тестирования в Яндексе

Slide 2

Slide 2 text

2 50%

Slide 3

Slide 3 text

3 Программировать Читать Сопровождать 50%

Slide 4

Slide 4 text

4 Поменяли шаблон = переписали сотни строк

Slide 5

Slide 5 text

5 Кодогенерация - это просто и очень полезно

Slide 6

Slide 6 text

6 5 причин попробовать

Slide 7

Slide 7 text

7 5 причин попробовать 2 способа сделать больше

Slide 8

Slide 8 text

8 5 причин попробовать 2 способа сделать больше Откуда дети исходники

Slide 9

Slide 9 text

9 5 причин попробовать 2 способа сделать больше Откуда дети исходники Процессор аннотаций

Slide 10

Slide 10 text

10 5 причин попробовать 2 способа сделать больше Откуда дети исходники Процессор аннотаций Мавен плагин

Slide 11

Slide 11 text

11 * * для привлечения внимания Тестируем приложение «Полетели» lanwen/heisenbug17 - POST /ticket/ - GET /tickets/{uuid}

Slide 12

Slide 12 text

12 $ cloc Java 366

Slide 13

Slide 13 text

13 $ cloc 50459 (600 files) Яндекс Паспорт (500+ эндпоинтов, 400 объектов) 20730 (220 files) До После Java

Slide 14

Slide 14 text

14 Project Lombok projectlombok.org 1 Зависимость @Data аннотация

Slide 15

Slide 15 text

15 Project Lombok 1 … projectlombok.org

Slide 16

Slide 16 text

16 Project Lombok 1 Зависимость @Data аннотация IDE plugin projectlombok.org

Slide 17

Slide 17 text

17 Jsonschema2pojo 2 Plugin сборки Json joelittlejohn/jsonschema2pojo

Slide 18

Slide 18 text

18 { "flights": [{ "number": "", "airline": "", "departure": {} }] } blog.qatools.ru/maven/json2pojo

Slide 19

Slide 19 text

19 Jsonschema2pojo 2 joelittlejohn/jsonschema2pojo …

Slide 20

Slide 20 text

20 Json-schema!

Slide 21

Slide 21 text

21 Json-schema! XSD (XML Schema)

Slide 22

Slide 22 text

22 JAXB/XJC 3 Plugin сборки XSD (XML Schema)

Slide 23

Slide 23 text

23 
 org.jvnet.jaxb2.maven2
 maven-jaxb2-plugin 
 
 -Xxew
 -Xfluent-api
 
 //. . . JAXB (демо)

Slide 24

Slide 24 text

24 
 org.jvnet.jaxb2.maven2
 maven-jaxb2-plugin 
 
 -Xxew
 -Xfluent-api
 
 //. . . JAXB (демо) Магия расширений

Slide 25

Slide 25 text

25 with-методы (fluent-builder) new UserMeta() .withLogin("Gandalf") .withLang("Syndarin") Immutable Builders toString/hashCode/equals . . . x100500

Slide 26

Slide 26 text

26 Hamcrest Matchers 4 Конфиг Зависимость yandex-qatools/hamcrest-pojo-matcher-generator

Slide 27

Slide 27 text

27 По матчеру assertThat( someOwner, both(withEmail(containsString(«@»))) .and(withUid(is(uid)) ); в каждую семью для email для uid java.lang.AssertionError: Expected: email a string containing «@» but: email was null полная свобода

Slide 28

Slide 28 text

28

Slide 29

Slide 29 text

29

Slide 30

Slide 30 text

30 rest-assured.io when().get("/info/{uid}", 5) .then().statusCode(200);

Slide 31

Slide 31 text

31 RARC 5 Plugin сборки RAML спецификация qameta/rarc

Slide 32

Slide 32 text

32 #%RAML 0.8 title: Tickets baseUri: https://api.example.com /ticket: /{uuid}: get: displayName: fetch

Slide 33

Slide 33 text

33 #%RAML 0.8 title: Tickets baseUri: https://api.example.com /ticket: /{uuid}: get: displayName: fetch ApiTickets.tickets(ticketsConfig()) .ticket() .uuid().withUuid("1") .fetch(identity()).prettyPeek();

Slide 34

Slide 34 text

34 #%RAML 0.8 title: Tickets baseUri: https://api.example.com /ticket: /{uuid}: get: displayName: fetch ApiTickets.tickets(ticketsConfig()) .ticket() .uuid().withUuid("1") .fetch(identity()).prettyPeek();

Slide 35

Slide 35 text

35 #%RAML 0.8 title: Tickets baseUri: https://api.example.com /ticket: /{uuid}: get: displayName: fetch ApiTickets.tickets(ticketsConfig()) .ticket() .uuid().withUuid("1") .fetch(identity()).prettyPeek();

Slide 36

Slide 36 text

36 #%RAML 0.8 title: Tickets baseUri: https://api.example.com /ticket: /{uuid}: get: displayName: fetch ApiTickets.tickets(ticketsConfig()) .ticket() .uuid().withUuid("1") .fetch(identity()).prettyPeek();

Slide 37

Slide 37 text

37 $ cloc Java 16 XSD 56 72 366 vs

Slide 38

Slide 38 text

38 Подводные камни

Slide 39

Slide 39 text

39 Почему не kotlin/groovy/ scala/go … ?

Slide 40

Slide 40 text

40 Почему не kotlin/ groovy/scala/go … ? Нельзя!

Slide 41

Slide 41 text

41 Почему не kotlin/ groovy/scala/go … ? Нельзя! Уже есть схема

Slide 42

Slide 42 text

42 Почему не kotlin/ groovy/scala/go … ? Нельзя! Уже есть схема Кодогенерация - не только про бины

Slide 43

Slide 43 text

43 Почему не kotlin/ groovy/scala/go … ? Нельзя! Уже есть схема Кодогенерация - не только про бины lanwen/heisenbug17

Slide 44

Slide 44 text

44 Тонкая настройка?

Slide 45

Slide 45 text

45 Схема «Биндинги»

Slide 46

Slide 46 text

46 «Биндинги» Wat? Схема

Slide 47

Slide 47 text

47 «Биндинги» @XmlJavaTypeAdapter(Adapter1 .class) @XmlSchemaType(name = "dateTime") protected ZonedDateTime plannedDateTime; Схема Результат

Slide 48

Slide 48 text

48 «Биндинги» 
 ... 
 bindings.xjb

Slide 49

Slide 49 text

49 Программирование на xml/json/yaml?

Slide 50

Slide 50 text

50 Программирование на xml/json/yaml? Нет логики

Slide 51

Slide 51 text

51 Программирование на xml/json/yaml? Нет логики Протокол (доки, микросервисы)

Slide 52

Slide 52 text

52 Программирование на xml/json/yaml? Нет логики Протокол (доки, микросервисы) Как список покупок

Slide 53

Slide 53 text

53 Кодогенерация - это просто и очень полезно

Slide 54

Slide 54 text

54 2 Задачи

Slide 55

Slide 55 text

55 2 2 + Задачи Решения

Slide 56

Slide 56 text

56 2 2 + + 2 Задачи Решения Слайда

Slide 57

Slide 57 text

57 Тестовые методы списком констант public final class TestMethodConsts { psf String TEST_METHOD_shouldCreate = "ru.lanwen.heisenbug.EticketResourceTest#shouldCreate"; } КОНСТАНТА = "полный.референс#метода"

Slide 58

Slide 58 text

58 Генерация кода Написание исходников

Slide 59

Slide 59 text

59 Генерация кода Написание исходников 1

Slide 60

Slide 60 text

60 Написание исходников StringBuilder 1

Slide 61

Slide 61 text

61 Написание исходников StringBuilder square/javapoet jknack/handlebars.java 1

Slide 62

Slide 62 text

62 public final class TestMethodConsts { public static final String TEST_METHOD_shouldCreate = "ru.lanwen.heisenbug.ETest#shouldCreate"; }

Slide 63

Slide 63 text

63 square/javapoet FieldSpec.builder( ClassName.get(String.class), key, PUBLIC, STATIC, FINAL ).initializer("$S", value) .build()

Slide 64

Slide 64 text

64 square/javapoet FieldSpec.builder( ClassName.get(String.class), key, PUBLIC, STATIC, FINAL ).initializer("$S", value) .build() Поле

Slide 65

Slide 65 text

65 square/javapoet FieldSpec.builder( ClassName.get(String.class), key, PUBLIC, STATIC, FINAL ).initializer("$S", value) .build() Тип, имя и модификаторы

Slide 66

Slide 66 text

66 square/javapoet FieldSpec.builder( ClassName.get(String.class), key, PUBLIC, STATIC, FINAL ).initializer("$S", value) .build() Значение

Slide 67

Slide 67 text

67 jknack/handlebars.java template.apply( Context.newContext() .data("package", pkg) .data("class", className) .data("consts", consts.entrySet()), writer );

Slide 68

Slide 68 text

68 Только модель template.apply( Context.newContext() .data("package", pkg) .data("class", className) .data("consts", consts.entrySet()), writer ); jknack/handlebars.java

Slide 69

Slide 69 text

69 Генерация кода Написание исходников 1 2

Slide 70

Slide 70 text

70 Кодогенерация Sed, bash 2

Slide 71

Slide 71 text

71 Кодогенерация Sed, bash 2 Annotation Processing Maven, Gradle

Slide 72

Slide 72 text

72 abstract class AbstractProcessor { } Процессор аннотаций

Slide 73

Slide 73 text

73 MatcherFactoryGenerator extends AbstractProcessor Процессор аннотаций

Slide 74

Slide 74 text

74 @SupportedSourceVersion(SourceVersion.RELEASE_8) @SupportedAnnotationTypes({"your.awesome.Annotation"}) public class MatcherFactoryGenerator extends AbstractProcessor { } Процессор аннотаций

Slide 75

Slide 75 text

75 @SupportedSourceVersion(SourceVersion.RELEASE_8) @SupportedAnnotationTypes({"your.awesome.Annotation"}) public class MatcherFactoryGenerator extends AbstractProcessor { } resources/META-INF/services/ javax.annotation.processing.Processor Процессор аннотаций FQCN

Slide 76

Slide 76 text

76 Тесты! E2E unit-тесты Отдельный модуль google/compile-testing

Slide 77

Slide 77 text

77 Compilation compilation = javac() .withProcessors(new MyAnnotationProcessor()) .compile(JavaFileObjects.forResource("HelloWorld.java")); assertThat(compilation).succeeded(); Тесты! unit-тесты google/compile-testing

Slide 78

Slide 78 text

78 public final class Project { public static final String VERSION = "1.0.0"; } Версия проекта константой

Slide 79

Slide 79 text

79 Maven plugin public class AbstractMojo { } bit.ly/mvn-plugin-dev

Slide 80

Slide 80 text

80 Maven plugin RestClientGenerateMojo extends AbstractMojo { } bit.ly/mvn-plugin-dev

Slide 81

Slide 81 text

81 Maven plugin @Mojo( name = "generate-client", defaultPhase = LifecyclePhase.GENERATE_SOURCES ) @Execute(goal = "generate-client") public class RestClientGenerateMojo extends AbstractMojo { } bit.ly/mvn-plugin-dev

Slide 82

Slide 82 text

82 @Parameter( required = true, readonly = true, defaultValue = "${project}" ) private MavenProject project; Знания мавена Maven plugin bit.ly/mvn-plugin-dev

Slide 83

Slide 83 text

83 . . . private MavenProject project; @Override public void execute() { new Codegen().generate(); project.addCompileSourceRoot(outputDir); } Магия генерации Maven plugin bit.ly/mvn-plugin-dev

Slide 84

Slide 84 text

84 Кодогенерируй!

Slide 85

Slide 85 text

85 Итого

Slide 86

Slide 86 text

86 Огромное количество готовых инструментов lanwen/heisenbug17 Project Lombok 1 Jsonschema2pojo 2 JAXB/XJC 3 Hamcrest Matchers 4 RARC 5

Slide 87

Slide 87 text

87 Огромное количество готовых инструментов lanwen/heisenbug17 Есть слабые стороны, при большой выгоде

Slide 88

Slide 88 text

88 Огромное количество готовых инструментов lanwen/heisenbug17 Хватит это терпеть писать toString/equals вручную! Есть слабые стороны, при большой выгоде

Slide 89

Slide 89 text

89 Спасибо Меркушев Кирилл lanwen руководитель группы автоматизации процессов разработки и тестирования в Яндексе