Slide 1

Slide 1 text

SnowOne 2020 KNOW-HOW AS CODE: ПРИКЛАДНАЯ КОДОГЕНЕРАЦИЯ ДЛЯ JAVA РАЗРАБОТЧИКА

Slide 2

Slide 2 text

2 # ЧТО ХОТИМ • Делать микросервисы (и монолиты) на Spring-e • Spring Framework • Spring Boot • Spring Data (JPA)

Slide 3

Slide 3 text

3 # Хотя, все происходит автомагически, не обойтись, без того, чтобы не написать немного кода: • Configuration • Entity • Repository • Service • Controller SPRING BOOT: МИНИМАЛЬНОЕ ПРИЛОЖЕНИЕ http://foxminded.com.ua/news/55-sravnenie-stekov-java-ee-i-spring-vozmozhnosti-i-ogranicheniya/

Slide 4

Slide 4 text

4 # А КАК ЭТО ОБЫЧНО ВЫГЛЯДИТ? @Entity public class User { @Id @GeneratedValue private Long id; @Transient private transient boolean isNew; @Column(nullable = false) private String name; }

Slide 5

Slide 5 text

5 # А КАК МЫ ХОТИМ? @Entity public class User { @Column(nullable = false) private String name; }

Slide 6

Slide 6 text

6 # А КАК МЫ ХОТИМ? @Entity class User { @Column(nullable = false) String name; }

Slide 7

Slide 7 text

7 # А КАК МЫ ХОТИМ? @Entity class User { @Column(nullable = false) String name @ManyToOne @Column(nullable = false) Country country }

Slide 8

Slide 8 text

8 # А КАК МЫ ХОТИМ? @Entity class User { @Column(nullable = false) String name @ManyToOne @Column(nullable = false) Country country }

Slide 9

Slide 9 text

9 # А КАК МЫ ХОТИМ? @Entity class User { @Column(nullable = false) String name @ManyToOne @JoinColumn(nullable = false) Country country }

Slide 10

Slide 10 text

12 # Когунь Андрей — программист • Работаю в компании КРОК • Преподаю Java • Провожу встречи Московского JUG @mskjug, #jugmsk О СЕБЕ

Slide 11

Slide 11 text

13 # ФОТО С ИВАНОМ

Slide 12

Slide 12 text

14 # Департамент разработки программного обеспечения • Заказная (в основном) разработка • Для “быстрой” разработки учетных систем реализован собственный фреймворк - jXFW О КРОК ДРПО

Slide 13

Slide 13 text

15 # ВЫБИРАЕМ ИЗ ДВУХ СТУЛЬЕВ • Готовый фреймворк • Свой фреймворк

Slide 14

Slide 14 text

16 # • Design / Сompile time vs Runtime ВЫБИРАЕМ ПОДХОД

Slide 15

Slide 15 text

17 # • Design / Сompile time vs Runtime • Чем раньше – тем лучше (дешевле) ВЫБИРАЕМ ПОДХОД

Slide 16

Slide 16 text

18 # • Design / Сompile time vs Runtime • Чем раньше – тем лучше (дешевле) • Используем Reflection? ВЫБИРАЕМ ПОДХОД

Slide 17

Slide 17 text

19 # • Design / Сompile time vs Runtime • Чем раньше – тем лучше (дешевле) • Используем Reflection? • Постараемся обойтись, не проблема сгенерировать немного больше кода ВЫБИРАЕМ ПОДХОД

Slide 18

Slide 18 text

20 # • Design / Сompile time vs Runtime • Чем раньше – тем лучше (дешевле) • Используем Reflection? • Постараемся обойтись, не проблема сгенерировать немного больше кода • Исходный код или байткод? ВЫБИРАЕМ ПОДХОД

Slide 19

Slide 19 text

21 #

Slide 20

Slide 20 text

22 # О ЧЕМ ПОГОВОРИМ • Кодогенерация в IDE • Java Annotation Processing (apt) • Lombok • JavaPoet • Kotlin

Slide 21

Slide 21 text

23 # О ЧЕМ ПОГОВОРИМ • Кодогенерация в IDE • Java Annotation Processing (apt) • Lombok • JavaPoet • Kotlin • Xtend

Slide 22

Slide 22 text

24 # О ЧЕМ ПОГОВОРИМ • Кодогенерация в IDE • Java Annotation Processing (apt) • Lombok • JavaPoet • Kotlin • Xtend – а что это?

Slide 23

Slide 23 text

25 # КОДОГЕНЕРАЦИЯ В IDE • Итеративный (и не самый быстрый) процесс • Генерировать можно исходный java код, любые другие ресурсы https://youtu.be/EEAiyRuaUXU

Slide 24

Slide 24 text

26 # CUBA Studio • код генерируется по шаблонам И ЗАПИЛИЛИ

Slide 25

Slide 25 text

27 # CUBA Studio • код генерируется по шаблонам И ЗАПИЛИЛИ

Slide 26

Slide 26 text

28 # РАЗ ЕСТЬ АННОТАЦИИ, БЕРЕМ ANNOTATION PROCESSING По Сгенерируем public interface SystemSettingRepository extends JpaRepository, QuerydslPredicateExecutor { } @Entity public class User { public String name; }

Slide 27

Slide 27 text

29 # РАЗ ЕСТЬ АННОТАЦИИ, БЕРЕМ ANNOTATION PROCESSING По Сгенерируем public interface SystemSettingRepository extends JpaRepository, QuerydslPredicateExecutor { User findByName(String name); } @Entity public class User { public String name; }

Slide 28

Slide 28 text

30 # РАЗ ЕСТЬ АННОТАЦИИ, БЕРЕМ ANNOTATION PROCESSING По Сгенерируем public interface SystemSettingRepository extends JpaRepository, QuerydslPredicateExecutor { User findПожалуйстаByName(String name); } @Entity public class User { public String name; }

Slide 29

Slide 29 text

31 # КАК РАБОТАЕТ ANNOTATION PROCESSING • Генерировать можно исходный java код, любые другие ресурсы • Процессор должен располагаться в отдельном от прикладного кода модуле (единице сборки) • Процессор не может менять код, только создавать новый

Slide 30

Slide 30 text

32 # КАК РАБОТАЕТ ANNOTATION PROCESSING • Генерировать можно исходный java код, любые другие ресурсы • Процессор должен располагаться в отдельном от прикладного кода модуле (единице сборки) • Процессор не может менять код, только создавать новый • Итеративный (и не самый быстрый) процесс

Slide 31

Slide 31 text

33 # ПРИМЕРЫ РЕАЛИЗАЦИИ ANNOTATION PROCESSING • Dagger (Android) • ButterKnife (Android) • QueryDSL (JPA) • Etc.

Slide 32

Slide 32 text

34 # ДЕЛАЕМ СВОЙ PROCESSOR ДЛЯ ENTITY @AutoService(Processor.class) public class EntityProcessor extends AbstractProcessor { @Override public synchronized void init(ProcessingEnvironment processingEnv) { super.init(processingEnv); // processingEnv.getFiler(); // processingEnv.getMessager(); } @Override public Set getSupportedAnnotationTypes() { Set set = new HashSet<>(); set.add(Entity.class.getCanonicalName()); return set; } @Override public SourceVersion getSupportedSourceVersion() { return SourceVersion.latestSupported(); } }

Slide 33

Slide 33 text

35 # ДЕЛАЕМ СВОЙ PROCESSOR ДЛЯ ENTITY @AutoService(Processor.class) public class EntityProcessor extends AbstractProcessor { … @Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { for (TypeElement annotation : annotations) { Set extends Element> annotatedElements = roundEnv.getElementsAnnotatedWith(annotation); // … } return true; } }

Slide 34

Slide 34 text

36 # ДЕЛАЕМ СВОЙ PROCESSOR ДЛЯ ENTITY … @Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { … JavaFileObject repositoryFile = filer.createSourceFile(repositoryClassName); try (PrintWriter out = new PrintWriter(repositoryFile.openWriter())) { if (packageName != null) { out.print("package "); out.print(packageName); out.println(";"); out.println(); } out.print("public class "); out.print(repositorySimpleClassName); out.println(" {"); out.println(); … } }

Slide 35

Slide 35 text

37 # ДЕЛАЕМ СВОЙ PROCESSOR ДЛЯ ENTITY … @Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { … JavaFileObject repositoryFile = filer.createSourceFile(repositoryClassName); try (PrintWriter out = new PrintWriter(repositoryFile.openWriter())) { if (packageName != null) { out.print("package "); out.print(packageName); out.println(";"); out.println(); } out.print("public class "); out.print(repositorySimpleClassName); out.println(" {"); out.println(); … } }

Slide 36

Slide 36 text

38 # ДЕЛАЕМ СВОЙ PROCESSOR ДЛЯ ENTITY … @Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { … JavaFileObject repositoryFile = filer.createSourceFile(repositoryClassName); try (PrintWriter out = new PrintWriter(repositoryFile.openWriter())) { if (packageName != null) { out.print("package "); out.print(packageName); out.println(";"); out.println(); } out.print("public class "); out.print(repositorySimpleClassName); out.println(" {"); out.println(); … } }

Slide 37

Slide 37 text

39 # НУ ЭТИ JAVA ПРОГРАММИСТЫ, НУ ВЫ ЗНАЕТЕ…

Slide 38

Slide 38 text

40 # УЛУЧШАЕМ ПОДДЕРЖКУ ГЕНЕРАЦИИ ИСХОДНОГО JAVA КОДА • JavaPoet - https://github.com/square/javapoet

Slide 39

Slide 39 text

41 # СГЕНЕРИРУЕМ МЕТОД С JAVAPOET @Autowired public void setQueryParamsBuilderFactory(QueryParamsBuilderFactory queryParamsBuilderFactory) { this.queryParamsBuilderFactory = queryParamsBuilderFactory; } • JavaPoet - https://github.com/square/javapoet

Slide 40

Slide 40 text

42 # СГЕНЕРИРУЕМ МЕТОД С JAVAPOET @Autowired public void setQueryParamsBuilderFactory(QueryParamsBuilderFactory queryParamsBuilderFactory) { this.queryParamsBuilderFactory = queryParamsBuilderFactory; } MethodSpec setQueryParamsBuilderFactory = MethodSpec .methodBuilder("setQueryParamsBuilderFactory") .addAnnotation(Autowired.class) .addModifiers(Modifier.PUBLIC) .addParameter(QueryParamsBuilderFactory.class, "factory") .addStatement("this.queryParamsBuilderFactory = factory") .build(); • JavaPoet - https://github.com/square/javapoet

Slide 41

Slide 41 text

43 # СГЕНЕРИРУЕМ МЕТОД С JAVAPOET @Autowired public void setQueryParamsBuilderFactory(QueryParamsBuilderFactory queryParamsBuilderFactory) { this.queryParamsBuilderFactory = queryParamsBuilderFactory; } MethodSpec setQueryParamsBuilderFactory = MethodSpec .methodBuilder("setQueryParamsBuilderFactory") .addAnnotation(Autowired.class) .addModifiers(Modifier.PUBLIC) .addParameter(QueryParamsBuilderFactory.class, "factory") .addStatement("this.queryParamsBuilderFactory = factory") .build(); • JavaPoet - https://github.com/square/javapoet

Slide 42

Slide 42 text

44 # ДЕЛАЕМ СВОЙ PROCESSOR ДЛЯ ENTITY C JAVAPOET … @Override public boolean process(Set extends TypeElement> annotations, RoundEnvironment roundEnv) { … JavaFile.builder(repositoryPackage, repositorySpec) .build() .writeTo(filer); }

Slide 43

Slide 43 text

45 # ЕСЛИ ЧТО-ТО ПОШЛО НЕ ТАК • Проверяем состояние и выводим информацию для разработчика messager.printMessage(Diagnostic.Kind.WARNING, "You do not set nullability", nameElement);

Slide 44

Slide 44 text

47 # НЕ ЗАБЫТЬ ПРОСТАВИТЬ НЕМНОГО АННОТАЦИЙ • Обязательно добавить @Generated

Slide 45

Slide 45 text

48 # НЕ ЗАБЫТЬ ПРОСТАВИТЬ НЕМНОГО АННОТАЦИЙ • А еще можно добавить @SuppressWarnings("all") • Обязательно добавить @Generated

Slide 46

Slide 46 text

50 # ЧЕГО НЕ ХВАТАЕТ? • Генерация в контексте одного класса, порядок срабатывания процессоров

Slide 47

Slide 47 text

51 # ЧЕГО НЕ ХВАТАЕТ? • Генерация в контексте одного класса, порядок срабатывания процессоров • Отсутствие инкрементальной компиляции • Отсутствие отношения генерируемого кода с исходным

Slide 48

Slide 48 text

52 # ЧЕГО НЕ ХВАТАЕТ? • Генерация в контексте одного класса, порядок срабатывания процессоров • Отсутствие инкрементальной компиляции • Отсутствие отношения генерируемого кода с исходным

Slide 49

Slide 49 text

53 # ЧЕГО НЕ ХВАТАЕТ? • Генерация в контексте одного класса, порядок срабатывания процессоров • Отсутствие инкрементальной компиляции • Отсутствие отношения генерируемого кода с исходным • Генерация только нового кода

Slide 50

Slide 50 text

54 # APT + LOMBOK? • Можно пользоваться готовыми аннотациями

Slide 51

Slide 51 text

55 # APT + LOMBOK? • Можно пользоваться готовыми аннотациями • А можно сделать поддержку своей: @Entity Не забудь добавить id и version

Slide 52

Slide 52 text

56 # APT + LOMBOK? • Можно пользоваться готовыми аннотациями • А можно сделать поддержку своей: @Entity Не забудь добавить id и version @MetaInfServices(JavacAnnotationHandler.class) public class EntityJavacHandler extends JavacAnnotationHandler { … }

Slide 53

Slide 53 text

57 # APT + LOMBOK? • Удачи с JCTree This is NOT part of any supported API. If you write code that depends on this, you do so at your own risk. This code and its internal interfaces are subject to change or deletion without notice. @Override public void handle(AnnotationValues annotation, JCTree.JCAnnotation ast, JavacNode annotationNode) { Context context = annotationNode.getContext(); Javac8BasedLombokOptions options = Javac8BasedLombokOptions.replaceWithDelombokOptions(context); options.deleteLombokAnnotations(); JavacHandlerUtil.deleteAnnotationIfNeccessary(annotationNode, Entity.class); …

Slide 54

Slide 54 text

58 # APT + LOMBOK? • JCTree • С этим разбирается Шерлок зато с вашим кодом будет разбираться Шерлок Это больно и неприятно,

Slide 55

Slide 55 text

59 # МОЖЕТ, НАПИШЕМ СВОЙ ЯЗЫК? • Описываем доменную модель на собственном диалекте • Реализуем maven (gradle) плагин • Генерацию запускаем из IDE

Slide 56

Slide 56 text

60 # МОЖЕТ, НАПИШЕМ СВОЙ ЯЗЫК? • Описываем доменную модель на собственном диалекте • Реализуем maven (gradle) плагин • Генерацию запускаем из IDE • Иван Пономарев, КУРС – JavaCC + JavaPoet

Slide 57

Slide 57 text

61 # МОЖЕТ, НАПИШЕМ СВОЙ ЯЗЫК? • Описываем доменную модель на собственном диалекте • Реализуем maven (gradle) плагин • Генерацию запускаем из IDE • Иван Пономарев, КУРС – JavaCC + JavaPoet

Slide 58

Slide 58 text

62 # РЕАЛИЗУЕМ MAVEN (GRADLE) ПЛАГИН • Контроль порядка генерации • Возможность генерировать общий код для нескольких модулей

Slide 59

Slide 59 text

63 # А ЧТО KOTLIN?

Slide 60

Slide 60 text

64 # А ЧТО KOTLIN? apt -> kapt

Slide 61

Slide 61 text

65 # А ЧТО KOTLIN? KAPT @Entity data class User { val name: String }

Slide 62

Slide 62 text

66 # А ЧТО KOTLIN? KAPT @Entity data class User { val name: String } • kotlin-metadata • KotlinPoet

Slide 63

Slide 63 text

67 # А ЧТО KOTLIN? COMPILER PLUGIN • Есть возможность реализовать плагин для компилятора, но:

Slide 64

Slide 64 text

68 # Xtend (http://xtend-lang.org) — это статически типизированный язык программирования, приемник Xpand, построенный с использованием Xtext и компилирующийся в Java исходный код. • Начало разработки – 2011 год • Текущая версия – 2.20.0 О XTEND https://jaxenter.com/pirates-of-the-jvm-the-infographic-132524.html

Slide 65

Slide 65 text

69 # • Как в Java – • Классы, методы, поля • Лямбда-выражения • var (val) О XTEND • Не как в Java – • Active annotations • Template Expressions • Switch Expression • Нет continue, break

Slide 66

Slide 66 text

70 # • Как в Java – • Классы, методы, поля • Лямбда-выражения • var (val) О XTEND – РАНЬШЕ • Не как в Java – • Active annotations • Template Expressions • Switch Expression • Нет b ? e1 : e2; • Нет continue, break

Slide 67

Slide 67 text

71 # • Как в Java – • Классы, методы, поля • Лямбда-выражения • var (val) • b ? e1 : e2; • “Сырые” строки • Switch Expression О XTEND – ТЕПЕРЬ • Не как в Java – • Active annotations • Нет continue, break • Template Expressions

Slide 68

Slide 68 text

72 # Возможность повлиять на результат компиляции Xtend Java @Active(EntityProcessor.class) public @interface Entity { … } АКТИВНЫЕ АННОТАЦИИ Xtend @Active(EntityProcessor) annotation Entity { … }

Slide 69

Slide 69 text

73 # Возможность повлиять на результат компиляции Xtend, из коробки: Xtend @Accessors(PUBLIC_GETTER, PUBLIC_SETTER) @ToString @EqualsHashCode @FinalFieldsConstructor … АКТИВНЫЕ АННОТАЦИИ

Slide 70

Slide 70 text

74 # Возможность повлиять на результат компиляции Xtend, из коробки: Xtend АКТИВНЫЕ АННОТАЦИИ Java @Entity public class User { public String name; } @Entity class User { String name }

Slide 71

Slide 71 text

75 # Возможность повлиять на результат компиляции Xtend, из коробки: Xtend АКТИВНЫЕ АННОТАЦИИ Java @Entity public class User { @Id @GeneratedValue public String id; @Version public Long ts; public String name; … } @Entity class User { String name }

Slide 72

Slide 72 text

76 # СЛАЙД СО ССЫЛКОЙ НА ДОКЛАД

Slide 73

Slide 73 text

77 # СЛАЙД СО ССЫЛКОЙ НА ДОКЛАД https://2018.jpoint.ru/talks/gnvv9ew1z2wigkemssm4m/ Осторожно Eclipse!

Slide 74

Slide 74 text

78 # ТАК ЧТО МНЕ ДЕЛАТЬ? You own Platform IDE plugin Frameworks DSLs maven (gradle) plugin apt (kapt)

Slide 75

Slide 75 text

79 # ВЫВОДЫ • Технологии идеальны, …. • Не обязательно переводить проект на некоторую технологию целиком • Лучше потратить время на разработку “интересного” кода, будь то генератор или бизнес-логика и не писать “скучный” код • Меньше кода написано – меньше ошибок

Slide 76

Slide 76 text

БУДУ РАД ОТВЕТИТЬ НА ВАШИ ВОПРОСЫ