diff --git a/core.md b/core.md index 42cd20d..84a1e6a 100644 --- a/core.md +++ b/core.md @@ -182,6 +182,11 @@ float x = 8.5F; + char: хранит одиночный символ в кодировке UTF-16 и занимает 2 байта, поэтому диапазон хранимых значений от 0 до 65535 +Классы обертки (Integer, Float, Long и тд) хранят внутри примитив, расширяя его дополнительными методами (наприер .equals()), возможностью хранить null и использоваться в коллекциях + +Метод valueOf() реализован во всех классах-обёртках в Java: +Метод Integer.valueOf(int) использует кеширование значений от –128 до 127 из внутреннего пула. Это оптимизация для часто используемых чисел. Объекты в пуле — это объекты в heap-памяти (куче), как и любые другие Java-объекты. Как и String pool + ## Какими значениями инициализируются переменные по умолчанию? + Числа инициализируются `0` или `0.0`; + `char` — `\u0000`; @@ -279,6 +284,119 @@ short s = 7; // 0000 0000 0000 0111 [к оглавлению](#java-core) +## Как применяют побитовые операции? + ++ Маски (битовые флаги) ++ +Маска — это число, в котором каждый бит используется как отдельный флаг (true/false). Это позволяет хранить несколько булевых значений в одной переменной int или long. + +` +final int READ = 1 << 0; // 0001 = 1 +final int WRITE = 1 << 1; // 0010 = 2 +final int EXECUTE = 1 << 2; // 0100 = 4 + +int rights = READ | WRITE; // 0011 = 3 (чтение + запись) + +// Проверка +boolean canRead = (rights & READ) != 0; // true +boolean canExecute = (rights & EXECUTE) != 0; // false + +// Добавим EXECUTE +rights |= EXECUTE; // теперь 0111 = 7 + +// Уберём WRITE +rights &= ~WRITE; // теперь 0101 = 5 (READ + EXECUTE) +` + +Здесь | используется для добавления флага, +& для проверки, +~ для обнуления конкретного бита. + +В Java часто вместо масок используют EnumSet, но маски полезны, если надо хранить очень много флагов компактно (например, при работе с сетью или драйверами). + +` +enum Permission { + READ(1 << 0), + WRITE(1 << 1), + EXECUTE(1 << 2); + + final int mask; + Permission(int mask) { this.mask = mask; } +} + +` ++ Быстрое умножение/деление на степени двойки + +Сдвиги влево и вправо работают быстрее, чем умножение/деление. + +` +int x = 5 << 1; // 10 (5 * 2) +int y = 20 >> 2; // 5 (20 / 4) + +int z = 7; + +int mul = z << 1; // 14 (7 * 2) +int div = z >> 1; // 3 (7 / 2) +` + ++ Оптимизация в алгоритмах +__Проверка чётности__ +` +int n = 10; +if ((n & 1) == 0) { + System.out.println("Чётное"); +} else { + System.out.println("Нечётное"); +} +` +__Быстрый модуль по степени двойки__ +` +int n = 77; +int mod = n & (8 - 1); // 77 % 8 = 5 +System.out.println(mod); // 5 +` + +__Переключение флага (toggle)__ +` +int flags = 0; + +// переключаем WRITE +flags ^= WRITE; // включили WRITE +flags ^= WRITE; // снова выключили WRITE + +` +__Очистка всех флагов, кроме одного__ +` +int flags = READ | WRITE | EXECUTE; +flags &= READ; // оставляем только READ +` + +__Интересные применения__ + +Хранение состояний: например, в графике (VISIBLE, ENABLED, SELECTED). + +Комбинация параметров: настройки работы функции (FULLSCREEN | VSYNC). + +Быстрая работа с большими массивами булевых значений (экономия памяти). + +[к оглавлению](#java-core) + +## Разница >> и >>> + +>>> и <<< сдвиги без учета знака +` +int x = -8; // 11111111111111111111111111111000 + +System.out.println(x >> 2); // -2 (111111...1110) — арифметический сдвиг +System.out.println(x >>> 2); // 1073741822 (001111...1110) — логический сдвиг + +` +Зачем нужен >>>, если есть >>? + +Ответ: для работы с int/long как беззнаковыми числами (например, при работе с сетевыми протоколами или бинарными файлами). + +[к оглавлению](#java-core) + ## Autoboxing и unboxing? __Автоупаковка__ - автоматическая инкапсуляция примитивного типа в эквивалентную ему класс-обёртку всякий раз, когда требуется объект данного типа. diff --git a/docker.md b/docker.md new file mode 100644 index 0000000..b6ca703 --- /dev/null +++ b/docker.md @@ -0,0 +1,1095 @@ +[Вопросы для собеседования](README.md) + +https://badtry.net/docker-tutorial-dlia-novichkov-rassmatrivaiem-docker-tak-iesli-by-on-byl-ighrovoi-pristavkoi/ +https://eternalhost.net/base/vps-vds/docker-zapusk-konteynera + +# Docker / Kubernetes ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Зачем _Docker_?](#Зачем-Docker) ++ [Что такое _Docker Image (образ)_?](#Что-такое-Docker-Image-(образ)) ++ [Что такое _Docker контейнер_?](#Что-такое-Docker-контейнер) ++ [Как менять контейнер?](#Как-менять-контейнер) ++ [Что такое _Dockerfile_?](#Что-такое-Dockerfile) ++ [Как пробрасывать локальную папку в контейнер Докера (монтирование папки)?](#Как-пробрасывать-локальную-папку-в-контейнер-Докера-(монтирование папки)) ++ [Что такое Docker Volumes?](#Что-такое-Docker-Volumesr) ++ [Как работают и пробрасываются Docker порты?](#Как-работают-и-пробрасываются-Docker-порты) ++ [Слои Docker образа, прослойка данных и кеширование](#Слои-Docker-образа,-прослойка-данных-и-кеширование) ++ [Что такое Docker-compose?](#Что такое Docker-compose) ++ [Как писать Микро Сервисы с Docker?](#Как-писать-Микро-Сервисы-с-Docker?) ++ [Что такое _Kubernetes_?](#Что-такое-Kubernetes) ++ [Компоненты и архитектура Kubernetes](#Компоненты-и-архитектура-Kubernetes) ++ [Kubectl](#Kubectl) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ [Что такое _Docker_?](#Что-такое-Docker) ++ + +## Что такое _Docker_? +На сайте Докера можно найти статью (https://www.docker.com/), в которой подробно рассказывается, что такое Docker. Из их слов - это стандартизированное ПО для разработки и развёртывания проектов. + +__Если говорить проще:__ Давайте на секунду забудем про Докер, и вспомним про такую ностальгическую штуку, как GameBoy Color. Если вы помните, игры для этой приставки поставлялись в виде картриджей. И я уверен в том, что производители видео игр пользуются успехом из-за своей простоты: + ++ Когда ты хочешь поиграть, ты просто вставляешь картридж в приставку, и игра сразу же работает. ++ Ты можешь поделиться своей игрой с друзьями, передав всего лишь картридж, который они вставят в приставку, и сразу же смогут играть. + +Docker следует похожему принципу - позволяет запускать своё ПО настолько просто, что это соизмеримо с вставкой картриджа и нажатием кнопки ON на приставке. Это основная суть, почему Docker настолько полезен - теперь кто угодно, у кого установлен Docker может запустить ваше приложение, выполнив для этого всего несколько команд. Раньше, вы, создавая приложения, к примеру на PHP, устанавливали локально PHP, MySql, возможно, NodeJs, при этом устанавливая зависимости в виде нужных расширений и библиотек. И, в случае передачи вашего скрипта какому-то знакомому, ему требовалось настраивать аналогичное окружение, аналогичных версий, иметь аналогичные расширения и конфигурацию, чтобы успешно запустить ваше приложение. Сейчас же, при использовании Докера, такой проблемы не возникнет впринципе. Теперь вам достаточно иметь установленную программу Docker, которая по одной вашей команде установит окружение, описанное в конфиге для запуска вашего приложения. + +Какое программное обеспечение можно запустить с помощью докера? В техническом плане, Docker чем-то похож на виртуальную машину: + +Докер - это движок, который запускает виртуальную операционную систему, имеющую чрезвычайно маленький вес (в отличие от Vagrant-а, который создаёт полноценную виртуальную ОС, Докер, имеет особые образы ПО, запускающиеся в виртуальной среде, не создавая полную копию ОС). Docker позволяет запустить ОС Linux в изолированной среде очень быстро, в течение нескольких минут. + +[к оглавлению](#Docker) + +## Зачем _Docker_? +__Кошмар при установке ПО__, с которым приходится сталкиваться. У вас когда-нибудь было такое, что вы пытаетесь установить ПО на ваш компьютер, а оно отказывается работать? Вы получаете несколько непонятных вам ошибок, из-за которых ничего не запускается. И после нескольких часов гугления, на десятой странице гугла...и на каком-то форуме, до этого неизвестного вам, вы наконец-то находите случайный комментарий, который помогает исправить вашу проблему. + +Аналогично, что делает написание PC игр более сложным, чем написание Game Boy игр - это то, что приходится проектировать систему с учётом большого множества существующих PC девайсов и спецификаций. Так как разные компьютеры имеют различные операционные системы, драйвера, процессоры, графические карты, и т.д. +И потому задача разработчика - написать приложение совместимое со всеми популярными системами, является достаточно затруднительной и трудоёмкой. + +Docker, как и Game Boy приставка, __берёт стандартизированные части программного обеспечения и запускает их__ так, как Game Boy запускал бы игру. + +В этом случае вы не должны беспокоиться об операционной системе, на которой пользователь будет запускать ваше приложение. Теперь, когда пользователи будут запускать приложение через Docker - конфигурация будет собрана автоматически, и код будет выполняться ВСЕГДА. + +__Как разработчик__, теперь вы не должны волноваться о том, на какой системе будет запущено ваше приложение. +__Как пользователь__, вам не нужно волноваться о том, что вы скачаете неподходящую версию ПО (нужного для работы программы). В Докере эта программа будет запущена в аналогичных условиях, при которых это приложение было разработано, потому, исключается факт получить какую-то новую, непредвиденную ошибку. Для пользователя все действия сводятся к принципу подключи и играй. + +[к оглавлению](#Docker) + +## Что такое _Docker Image (образ)_? +Docker образ (он же Docker Image), похож на Game Boy картридж - это просто программное обеспечение. Это стандартизированное программное обеспечение, которое запускается на любой приставке Game Boy. Вы можете дать игру вашему другу, и он сможет просто вставить картридж в приставку, и играть. + +Как в случае с картриджами, бывают различные игры, так и Docker имеет различные образы ПО: ubuntu, php (который наследуется от оригинального образа Ubuntu), nodejs, и т.д. + +```java +docker pull IMAGE_NAME // где - имя скачиваемого образа + +docker pull ubuntu:18.10 +``` +Эта команда сообщает Докеру о том, что нужно скачать образ Ubuntu 18.10 с Dockerhub.com - основной репозиторий Docker-образов, на котором вы и можете посмотреть весь их список и подобрать нужный образ для вашей программы. Представленные в хабе образы можно найти при помощи команд docker и search. К примеру, найти образ Ubuntu можно следующим образом: +````java +docker search ubuntu +```` + +Теперь, для того, чтобы посмотреть список всех загруженных образов, нужно выполнить: +````java +docker images +```` +Проводя аналогии, команда docker images выглядит как коллекция картриджей от приставки, которые у вас есть сейчас + +[к оглавлению](#Docker) + + +## Что такое _Docker контейнер_? +Теперь представьте, что мы обновили нашу приставку с Game Boy на GameCube. Игры хранятся на диске, который предназначен только для чтения самого образа игры. А прочие файлы (сохранения, кеш и т.д.) сохраняются внутри самой приставки, локально. + +Так же, как и игра на диске, исходный Docker Image (образ) - __неизменяемый__. + +Docker контейнер - это экземпляр запущенного образа. Аналогично тому, что вы вставляете диск в приставку, после чего игра начинается. А сам образ игры никак не модифицируется, все файлы, содержащие изменения хранятся где-то локально на приставке. + +Запуск Docker контейнера соответствует тому, что вы играете в свою Gamecube игру. Docker запускает ваш образ в своей среде, аналогично тому, как Gamecube запускает игру с диска, не модифицируя оригинальный образ, а лишь сохраняя изменения и весь прогресс в какой-то песочнице. + +Для запуска контейнера существует команда: +````java +docker run <опциональная команды, которая выполнится внутри контейнера> +```` + +__Давайте запустим наш первый контейнер Ubuntu__ +````java +docker run ubuntu:18.10 echo 'hello from ubuntu' +```` +Команда echo 'hello from ubuntu' была выполнена внутри среды Ubuntu. Другими словами, эта команда была выполнена в контейнере ubuntu:18.10. + +Теперь выполним команду для проверки списка запущенных контейнеров: +````java +docker ps +```` +Здесь пустота... это потому что docker ps показывает только список контейнеров, которые запущены в данный момент (наш же контейнер выполнил одну команду echo 'hello from ubuntu' и завершил свою работу). + +А для того, чтобы посмотреть список всех контейнеров без исключения, нужно добавить флаг -a, выполним: +````java +docker ps -a +```` + +После выполнения нужных операций внутри контейнера, то Docker-контейнер завершает работу. Это похоже на режим сохранения энергии в новых игровых консолях - если вы не совершаете действий какое-то время, то система выключается автоматически. + +Каждый раз, когда вы будете выполнять команду docker run, будет создаваться новый контейнер, на каждую из выполненных команд. + +__Выполнение неограниченное количество команда внутри контейнера__ + +Давайте добавим немного интерактивности в наше обучение. Мы можем подключиться к консоли виртуальной ОС (Ubuntu 18.10), и выполнять любое количество команд без завершения работы контейнера, для этого, запустим команду: +````java +docker run -it ubuntu:18.10 /bin/bash +```` +Опция -it вместе с /bin/bash даёт доступ к выполнению команд в терминале внутри контейнера Ubuntu. + +Теперь, внутри этого контейнера можно выполнять любые команды, применимые к Ubuntu. Вы же можете представлять это как мини виртуальную машину, условно, к консоли которой мы подключились по SSH. + +__Узнаём ID контейнера__ +Иногда является очень полезным узнать ID контейнера, с которым мы работаем. И как раз-таки, при выполнении команды docker run -it /bin/bash, мы окажемся в терминале, где все команды будут выполняться от имени пользователя root@. + +Теперь, все команды буду выполняться внутри операционной системы Ubuntu. Попробуем, например, выполнить команду ls, и посмотрим, список директорий, внутри этого образа Ubuntu.docker-ubuntu-ls + +Docker контейнер является полностью независимым от системы хоста, из которой он запускался. Как изолированная виртуальная машина. И в ней вы можете производить любые изменения, которые никак не повлияют на основную операционную систему. + +Это аналогично тому, как, если бы вы играли в Mario Kart на приставке Gamecube, и неважно, что вы делаете в игре, вы никак не сможете изменить само ядро игры, или изменить информацию, записанную на диске. + +Контейнер является полностью независимым и изолированным от основной операционной системы, аналогично виртуальной операционной системе. Вы можете вносить любые изменения внутри виртуалки, и никакие из этих изменений не повлияют на основную операционную систему. + +Теперь откройте новое окно терминала (не закрывая и не отключаясь от текущего), и выполните команду docker ps +docker-ps-new-window + +Только на этот раз вы можете увидеть, что контейнер с Ubuntu 18.10 в текущий момент запущен. + +Теперь вернёмся назад к первому окну терминала (который находится внутри контейнера), и выполним: +````java +mkdir /truedir //создаст папку truedir +exit //выйдет из контейнера, и вернётся в основную ОС +```` +Выполнив команду exit, контейнер будет остановлен (чтобы убедиться, можете проверить командой docker ps). Теперь, вы так же знаете, как выйти из Docker контейнера. + +Теперь, попробуем ещё раз просмотреть список всех контейнеров, и убедимся, что новый контейнер был создан docker ps -adocker-ps--a2 + +Так же, для того, чтобы запустить ранее созданный контейнер, можно выполнить команду docker start , + +где CONTAINER_ID - id контейнера, который можно посмотреть, выполнив команду docker ps -a (и увидеть в столбце CONTAINER_ID) + +В моём случае, CONTAINER_ID последнего контейнера = 7579c85c8b7e (у вас же, он будет отличаться) + +Запустим контейнер командой: +````java +docker start 7579c85c8b7e //ваш CONTAINER_ID +docker ps +docker exec -it 7579c85c8b7e /bin/bash //ваш CONTAINER_ID +```` +И теперь, если внутри контейнера выполнить команду ls, то можно увидеть, что ранее созданная папка truedir существует в этом контейнереdocker-exex-truedir + +Команда exec позволяет выполнить команду внутри запущенного контейнера. В нашем случае, мы выполнили /bin/bash, что позволило нам подключиться к терминалу внутри контейнера. + +Для выхода, как обычно, выполним exit. + +Теперь остановим и удалим Docker контейнеры командами: +````java +docker stop +docker rm +docker ps a // просмотрим список активных контейнеров +docker stop aa1463167766 // остановим активный контейнер +docker rm aa1463167766 // удалим контейнер +docker rm bb597feb7fbe // удалим второй контейнер +```` +В основном, нам не нужно, чтобы в системе плодилось большое количество контейнеров. Потому, команду docker run очень часто запускают с дополнительным флагом --rm, который удаляет запущенный контейнер после работы: +```java +docker run -it --rm ubuntu:18.10 /bin/bash +``` + +[к оглавлению](#Docker) + +## Как менять контейнер? +Во время запуска контейнера из существующего образа у пользователя есть возможность создавать или удалять файлы, аналогично работе на виртуальной машине. При этом изменения будут распространяться только в определенном контейнере. Доступна и возможность запуска с последующей остановкой контейнера, но после его удаления с помощью docker rm будут утеряны внесенные изменения. + +Соответственно, следует ознакомиться со способом сохранения текущего контейнера как нового образа. + +По завершении инсталляции Node.js в контейнере Ubuntu, на компьютере работает загруженный из образа контейнер. При этом он будет отличаться от использованного для его создания образа. В свою очередь, пользователю может понадобиться уже контейнер Node.js, чтобы использовать его при создании для новых образов. + +Соответственно, следует сохранить результаты в текущем образе предложенной ниже командой: +```java +docker commit -m "What you did to the image" -a "Author Name" container_id repository/new_image_name +``` +Добавление опции -m дает возможность указать сообщение подтверждения. Это позволит будущим пользователям образа понять, что именно было изменено. Что касается параметра -a — с его помощью можно указать, кто его создатель. container_id является тем же идентификатором, который был использован ранее, во время запуска интерактивной сессии в Docker. + +К примеру, с именем пользователя admin и идентификатором 2c8ec46adae1 команда должна иметь следующий вид: +```java +docker commit -m "added Node.js" -a "admin" 2c8ec46adae1 admin/ubuntu-nodejs +``` +Как запустить Docker контейнер на Linux + +После того, как образ будет подтвержден (commit) он сохраняется на компьютере локально. + +Завершающий этап — сохранение созданных образов в базу Docker Hub или другой репозиторий, откуда их может скачать любой желающий. Чтобы получить такую возможность, предварительно нужно создать аккаунт. + +Отправка образов в репозиторий начинается с авторизации на Docker Hub. +```java +docker login -u docker-registry-username +``` + +Чтобы вход был успешно осуществлен, потребуется ввести пароль Docker Hub. Если он правильный, авторизация пройдет успешно. + +Здесь важно знать, что если в реестре Docker имя пользователя отличается от локального, используемого при создании образа, обязательно нужно привязать этот образ к имени учетной записи в хабе. На примере контейнера с NodeJS команда привязки будет выглядеть так: +```java +docker tag admin/ubuntu-nodejs docker-registry-username/ubuntu-nodejs +``` + +После чего можно приступать к загрузке образа на сервер: +```java +docker push docker-registry-username/docker-image-name +``` + +__Автозагрузка контейнеров__ + +Часто встречается ситуация, когда контейнеры останавливаются вследствие определенных факторов. Простейший пример – произошла перезагрузка сервера. Чтобы избавиться от необходимости вручную запускать их, можно настроить автозапуск контейнеров. Для этого следует создать текстовые файлы со специальным форматом для сервиса systemcmd. Рассмотрим пример их создания на примере контейнера my-db, введя в терминал команду: +```java +cat /etc/systemd/system/my-db.service +``` +В пустой файл необходимо добавить следующий код и сохранить его: +```java +[Unit] +Description=MY DB (PG) docker container +Requires=docker.service +After=docker.service +[Service] +Restart=always +ExecStart=/usr/bin/docker start -a my-db +ExecStop=/usr/bin/docker stop -t 2 my-db +TimeoutSec=30 +[Install] +WantedBy=multi-user.target +``` + +После этого остается перезапустить демон systemcmd и включить автозагрузку контейнера mydb, набрав в терминале поочередно команды: +```java +systemctl daemon-reload +systemctl start my-db.service +systemctl enable my-db.service +``` + +[к оглавлению](#Docker) + +## Что такое _Dockerfile_? +Docker позволяет вам делиться с другими средой, в которой ваш код запускался и помогает в её простом воссоздании на других машинах. + +Dockerfile - это обычный конфигурационный файл, описывающий пошаговое создание среды вашего приложения. В этом файле подробно описывается, какие команды будут выполнены, какие образы задействованы, и какие настройки будут применены. А движок Docker-а при запуске уже распарсит этот файл (именуемый как Dockerfile), и создаст из него соответствующий образ (Image), который был описан. + +К примеру, если вы разрабатывали приложение на php7.2, и использовали ElasticSearch 9 версии, и сохранили это в Dockerfile-е, то другие пользователи, которые запустят образ используя ваш Dockerfile, получат ту же среду с php7.2 и ElasticSearch 9. + +С Dockerfile вы сможете подробно описать инструкцию, по которой будет воссоздано конкретное состояние. И делается это довольно-таки просто и интуитивно понятно. + +Если вам нужно воспроизвести среду (образ) на другом ПК, с докером вы так же имеете два варианта при создании образа: + ++ Вы можете запаковать ваш контейнер, создать из него образ (аналогично тому, что вы запишите на диск новую игру с собственными модификациями). Это похоже на способ, когда вы делитесь сохранениями напрямую. ++ Или же, можно описать Dockerfile - подробную инструкцию, которая приведёт среду к нужному состоянию. + +Я склоняюсь ко второму варианту, потому что он более подробный, гибкий, и редактируемый (вы можете переписать Dockerfile, но не можете перемотать состояние образа в случае прямых изменений). + +Пришло время попрактиковаться на реальном примере. Для начала, создадим файл cli.php в корне проекта с содержимым: +````php + --tag + - путь к файлу Dockerfile (. - текущая директория), + - имя, под которым образ будет создан +```` + +Выполним: +````java +docker build . --tag pyramid +```` +При том, что имя файла Dockerfile при указывании пути упускается, нужно указывать только директорию, в которой этот файл находится (а . означает, что файл находится в той директории, из которой была запущена консоль) + +После того, как команда выполнилась, мы можем обращаться к образу по его имени, которое было указано в , проверим список образов: docker images + +Теперь, запустим контейнер из нашего образа командой docker run pyramid + +__Сначала мы скопировали файл cli.php в Docker образ, который создался с помощью Dockerfile. Для того, чтобы удостовериться в том, что файл действительно был проброшен внутрь контейнера, можно выполнить команду docker run pyramid ls, которая в списке файлов покажет и cli.php.__ + +Однако, сейчас этот контейнер недостаточно гибкий. Нам бы хотелось, чтобы можно было удобно изменять количество строк, из скольки состоит пирамида. + +Для этого, отредактируем файл cli.php, и изменим, чтобы количество аргументов принималось из командной строки. Отредактируем вторую строку на: +````php +$n = $i = $argv[1] ?? 5; //а было $n = $i = 5 +// это значит, что мы принимаем аргумент из консоли, а если он не получен, то используем по-умолчанию 5 +```` +После чего, пересоберём образ: docker build . --tag pyramid +И запустим контейнер: docker run pyramid php /cli.php 9, получив вывод ёлки пирамиды в 9 строк + +Почему это работает? +Когда контейнер запускается, вы можете переопределить команду записанную в Dockerfile в поле CMD. + +Наша оригинальная CMD команда, записанная в Dockerfile php /cli.php - будет переопределена новой php /cli.php 9. +Но, было бы неплохо передавать этот аргумент самому контейнеру, вместо переписывания всей команды. Перепишем так, чтобы вместо команды php /cli.php 7 можно было передавать просто аргумент-число. + +Для этого, дополним Dockerfile: +````java +FROM php:7.2-cli +COPY cli.php /cli.php +RUN chmod +x /cli.php +ENTRYPOINT ["php", "/cli.php"] +## аргумент, который передаётся в командную строку +CMD ["9"] +```` +Мы немного поменяли формат записи. В таком случае, CMD будет добавлена к тому, что выполнится в ENTRYPOINT. + +["php", "/cli.php"] на самом деле запускается, как php /cli.php. И, учитывая то, что CMD будет добавлена после выполнения текущей, то итоговая команда будет выглядеть как: php /cli.php 9 - и пользователь сможет переопределить этот аргумент, передавая его в командную строку, во время запуска контейнера. + +Теперь, заново пересоберём образ +````java +docker build . --tag pyramid +```` +И запустим контейнер с желаемым аргументом +````java +docker run pyramid 3 +```` +[к оглавлению](#Docker) + + +## Как пробрасывать локальную папку в контейнер Докера (монтирование папки)? +Монтирование директории в Docker контейнер - это предоставление доступа контейнеру на чтение содержимого вашей папки из основной операционной системы. Помимо чтения из этой папки, так же, контейнер может её изменять, и такая связь является двусторонней: при изменении файлов в основной ОС изменения будут видны в контейнере, и наоборот. + +__Монтирование директории в контейнер позволяет ему читать и писать данные в эту директорию, изменяя её состояние.__ + +Для того, чтобы смонтировать папку из основной системы в контейнер, можно воспользоваться командой +docker run -v : ..., +где DIRECTORY - это путь к папке, которую нужно смонтировать, +CONTAINER_DIRECTORY - путь внутри контейнера. + +Только путь к монтируемой папке должен быть прописан полностью: C:\projects\docker-example, или на *nix-системах можно воспользоваться конструкцией $(pwd) + +Выполним команду: +````java +docker run -it -v C:\projects\docker-example\cli:/mounted ubuntu:18.10 /bin/bash +ls +ls mounted +touch mounted/testfile +```` +При выполнении этой команды, указанная папка смонтируется в папку /mounted, внутри файловой системы контейнера, а команда touch mounted/testfile создаст новый файл под названием testfile, который вы можете увидеть из основной ОС. + +Теперь вы можете увидеть, что после выполнения этой команды в текущей директории появился новый файл testfile. И это говорит о том, что двусторонняя связь работает - при изменении директории на основной ОС всё отразится на смонтированную папку внутри контейнера, а при изменениях изнутри контейнера всё отразится на основную ОС. + +Монтирование папки позволяет вам изменять файлы вашей основной системы прямо во время работы внутри Docker контейнера. + +Это удобная особенность, которая позволяет нам редактировать код в редакторе на основной ОС, а изменения будут сразу же применяться внутри контейнера. +[к оглавлению](#Docker) + + +## Что такое Docker Volumes? +С Docker Volum-ами мы имеем контейнер, который хранит постоянные данные где-то на нашем компьютере (это актуально, потому что после завершения работы контейнер удаляет все пользовательские данные, не входящие в образ). Вы можете прикрепить Volume-данные к любому из запущенных контейнеров. + +Вместо того, чтобы каждый раз, при запуске контейнера, писать, какие из папок вы хотите смонтировать, вы просто можете создать один контейнер с общими данными, который потом будете прикреплять. + +В Dockerfile прописывается параметр volumes: и указывается название переменной и путь к данным, которые хотим сохранять (внутри конкретного контейнера). Вне контейнера можно указать значения по умолчанию +```java +services: + php: + volumes: + - bddata:/var/lib/postgresql/data/ + +volumes: + bddata: null + +``` +Лично я, не использую это очень часто на практике, потому что есть много других методов по управлению данными. Однако, это может быть очень полезно для контейнеров, которые должны сохранять какие-то важные данные, или данные, которыми нужно поделиться между несколькими контейнерами. + +[к оглавлению](#Docker) + + +## Как работают и пробрасываются Docker порты? + +Docker позволяет нам получить доступ к какому-то из портов контейнера, пробросив его наружу (в основную операционную систему). По умолчанию, мы не можем достучаться к каким-либо из портов контейнера. Однако, в Dockerfile опция EXPOSE позволяет нам объявить, к какому из портов мы можем обратиться из основной ОС. + +Для этого, на по-быстрому, запустим Docker-образ php-apache, который работает на 80 порту. + +Для начала, создадим новую папку apache (перейдём в неё cd apache), в которой создадим файл index.php, на основе которого мы и поймём, что всё работает. +````php +: +```` +И мы можем указать любое соответствие портов, но сейчас просто укажем, что порт системы 80 будет слушать 80 порт контейнера: +````java +docker run -p 80:80 own_php_apache +```` +Здесь, вы уже наверное заметили, что добавился новый параметр -p 80:80, который говорит Docker-у: я хочу, чтобы порт 80 из apache был привязан к моему локальному порту 80. + +И теперь, если перейти по адресу localhost:80, то должны увидеть успешный ответ: +[к оглавлению](#Docker) + + +## Слои Docker образа, прослойка данных и кеширование +Каждый раз, когда вы собираете образ, он кешируется в отдельный слой. Ввиду того, что образы являются неизменяемыми, их ядро никогда не модифицируются, потому применяется система кеширования, которая нужна для увеличения скорости билдинга. + +Каждая команда в Dockerfile сохраняется как отельный слой образа. + +Рассмотрим это на примере нашего прошлого Dockerfile-а: +````java +FROM php:7.2-apache +# Копирует код ядра +COPY . /var/www/html +WORKDIR /var/www/html +EXPOSE 80 +```` +Когда вы пишите свой Dockerfile, вы добавляете слои поверх существующего основного образа (указанного в FROM), и создаёте свой собственный образ (Image). + ++ FROM: говорит Докеру взять за основу этот существующий образ. А все новые команды будут добавлены слоями поверх этого основного образа. ++ COPY: копирует файлы с основной ОС в образ ++ WORKDIR: устанавливает текущую папку образа в /var/www/html + +Слой Образа Докера это как точка сохранения в игре Super Mario. Если вы хотите изменить какие-то вещи, произошедшие до этой точки сохранения, то вам придётся перезапустить этот уровень полностью. Если вы хотите продолжить прогресс прохождения, вы можете начать с того места, где остановились. + +Docker начинает кешировать с "того места, где остановился" во время билдинга Dockerfile. Если в Докерфайле не было никаких изменений с момента последнего билдинга, то образ будет взят полностью из кеша. Если же вы измените какую-то строку в Dockerfile - кеш будет взят только тех слоёв команд, которые находятся выше изменённой команды. + +Для иллюстрации этого, добавим новые строки в Dockerfile: +````java +FROM php:7.2-apache +WORKDIR /var/www/html +# Copy the app code +COPY . /var/www/html +RUN apt-get update && apt-get upgrade -y && apt-get install -y curl php7.2-mbstring php7.2-zip php7.2-intl php7.2-xml php7.2-json php7.2-curl +RUN echo "Hello, Docker Tutorial" +EXPOSE 80 +```` +После чего, пересоберём образ: +````java +docker build . --tag own_php_apache +```` +Выполнив эту команду, из вывода в консоль можете увидеть, что некоторые слои были взяты из кеша. Это как раз те команды, выше которых в Dockerfile не было добавлено/изменено содержимого. + +И можно заметить, что в случае изменения Dockerfile, билдинг занимает больше времени, потому что не используется кеш. Где бы вы не написали команду, все закешированные команды, которые находятся ниже в Dockerfile, будут перебилжены заново. А те, что находятся выше, будут по-прежнему браться из кеша. + +Когда вы используете команду COPY, она копирует указанную директорию в контейнер. И, в случае изменения содержимого любого из файлов этой директории, кеш команды COPY будет сброшен. Docker сверяет изменения во время билдинга в каждом из файлов. Если они были изменены, кеш будет сброшен, как и для всех последующих слоёв. + +Если честно, то это действительно крутая функция. Docker следит за изменениями в файлах и использует кеш всегда, когда это нужно (когда были произведены изменения в каких-то из файлов). Изменение ваших файлов потенциально может затрагивать будущие команды, из-за чего, и все последующие слои билдятся заново, а не берутся из кеша. + +Какие выводы из этого можно сделать: + ++ Команды, которые вероятнее всего не будут меняться в будущем, нужно помещать как можно выше в Dockerfile. ++ Команды копирования данных нужно помещать ниже, потому что файлы при разработке изменяются довольно часто. ++ Команды, которые требуют много времени на билдинг, нужно помещать выше. + ++ В заключение, так же хочу сказать, как можно уменьшить размер слоёв Docker образов. +В Dockerfile вы можете иметь несколько команд (RUN) на выполнение: +```java +RUN apt-get update +RUN apt-get install -y wget +RUN apt-get install -y curl +``` + +В результате выполнения этой команды, будет создано 3 разных слоя в образе. Вместо этого, все команды стараются объединить в одну строку: +```java +RUN apt-get update && apt-get install -y wget curl +``` +Если команда становится длинной, и нечитаемой, то для переноса на следующую строку делаем так: +```java +RUN apt-get update && apt-get install -y wget curl && \ +&& apt-get clean -y \ +&& docker-php-ext-install soap mcrypt pdo_mysql zip bcmath +``` +Если же команда становится слишком большой, и неудобной для чтения, то можно создать новый shell скрипт, в который поместить длинную команду, и запускать этот скрипт одной простой командой RUN. + +Технически, только команды ADD, COPY, и RUN создают новый слой в Docker образе, остальные команды кешируются по-другому (https://docs.docker.com/develop/develop-images/dockerfile_best-practices/#minimize-the-number-of-layers) +[к оглавлению](#Docker) + + +## Что такое Docker-Compose? +Инструмент Docker Compose входит в комплект официального программного обеспечения для Docker. Он позволяет решать различные задачи, связанные с управлением одновременно несколькими контейнерами, составляющих в целом один проект. + +Самый очевидный пример – веб-сайт, где для авторизации пользователей необходимо подключение к базе данных. Для такого проекта нужно два сервиса – один отвечает за функционирование сайта, а другой за базу данных. + +Docker-compose написан в формате YAML который по своей сути похож на JSON или XML. Но YAML имеет более удобный формат для его чтения, чем вышеперечисленные. В формате YAML имеют значения пробелы и табуляции, именно пробелами отделяются названия параметров от их значений. + +Создадим новый файл docker-compose.yml, для рассмотрения синтаксиса Docker Compose: +```java +version: '3' + +services: +app: +build: +context: . +ports: +- 8080:80 +``` + +Теперь, построчно разберёмся с заданными параметрами, и что они значат: ++ version: какая версия docker-compose используется (3 версия - самая последняя на даный момент). ++ services: контейнеры которые мы хотим запустить. ++ app: имя сервиса, может быть любым, но желательно, чтобы оно описывало суть этого контейнера. ++ build: шаги, описывающие процесс билдинга. ++ context: где находится Dockerfile, из которого будем билдить образ для контейнера. ++ ports: маппинг портов основной ОС к контейнеру. + +```java +docker-compose build - собирает проект из Dockerfile +docker-compose up - запускает все сервисы контейнера +docker-compose down - останавливает все сервисы контейнера +Ctrl+C для остановки контейнера +``` + +[к оглавлению](#Docker) + + +## Как писать Микро Сервисы с Docker? Что такое микросервисы? + +[к оглавлению](#Docker) + + +## Что-такое-Kubernetes +Это платформа с открытым исходным кодом для развертывания, масштабирования, управления и контроля контейнеризованных приложений либо сервисов. + +__Kubernetes делает следующее:__ ++ Управляет и запускает контейнеры. ++ Балансирует сетевой трафик между узлами кластера Kubernetes и количеством реплик контейнеров. Можно раскатить один образ на несколько Воркер нодов, получив разные IP порты и DNS имя, а кубер сделает Load Balancing между этими контейнерами ++ Осуществляет контроль состояния, автоматические развертывания и откаты реплик контейнеров внутри узлов кластера Kubernetes. ++ Осуществляет распределение нагрузки между узлами кластера Kubernetes. Например управление ресурсами при развертывания нового контейнера ++ Предоставляет автоматическое монтирование систем хранения для контейнеров. Например прикрепить локальный или облачный диск к одному или нескольким докер контейнерам ++ Предоставляет декларативный API и CLI для управления ++ И еще множество полезных, и не очень, модулей и сервисов, которые можно развернуть для управления автоматизацией, инфраструктурой и контейнерами + +__Kubernetes не делает следующее:__ ++ Не собирает контейнеры с исходным кодом вашего приложения или сервиса ++ Не предоставляет процессы и решения непрерывной интеграции (CI) ++ Не включает в себя решения и системы сбора журналов и метрик ++ Не включает в себя решения и системы хранения данных. Только подключение сторонних ++ Не включает в себя решения и системы хранения контейнеров (registry) ++ Не включает в себя решения и системы от всех бед и болячек инфраструктуры + +Kubernetes или K8S — это не просто система оркестрации. Техническое определение оркестрации — это выполнение определенного рабочего процесса: сначала сделай A, затем B, затем C. Напротив, Kubernetes содержит набор независимых, компонуемых процессов управления, которые непрерывно переводят текущее состояние к предполагаемому состоянию. Неважно, как добраться от А до С. Не требуется также и централизованный контроль. Это делает систему более простой в использовании, более мощной, надежной, устойчивой и расширяемой. + +Основной фактор использования Kubernetes в технологических компаниях, где ведется активная разработка приложений, - это гибкий подход к разработке. Сегодня подход в построении архитектуры приложений изменился - приложения больше не выглядят как монолит кода или один большой сервис, где весь функционал был в одном репозитории. Раньше сборки проектов занимали достаточно много времени, но с приходом контейнеров и таких методологий как DevOps приложения стали модульными, и теперь за каждую функцию или группу функций отвечают определенные сервисы этого приложения. Это можно сравнить с кирпичиками конструктора LEGO: из всех деталей складывается наше приложение, каждый сервис мы можем достать, чтобы что-то изменить и протестировать и вставить обратно в нашу конструкцию. Главная идея состоит в том, чтобы быстро внедрять новый функционал в уже имеющееся приложение. + +Но что если у нас таких сервисов тысячи, каждый за что-то отвечает и работает сам по себе? А если еще развернуты несколько реплик для отказоустойчивости? Как управлять всем и уделить внимание каждому из них? Как понять, что сервис правильно работает и взаимодействует с другими? Для этого и есть специальные системы, оркестраторы в своем роде, такие как HashiCorp Nomad, Docker Swarm и Kubernetes. + +Бизнес чаще всего ценит такие возможности К8S как собирать и тестировать только часть приложения, с которой мы работаем, что в разы уменьшает объем необходимых ресурсов; добавлять и убирать сервисы «на лету», тестировать новый функционал в разных регионах и смотреть, как он себя показывает. За счет этого Kubernetes, который дает унификацию и гибкость в способе обслуживания и содержания сервисов приложения. + +__Kubernetes предоставляет:__ + ++ Быструю и автоматическую масштабируемость. При росте нагрузки можно быстро добавить необходимые узлы приложения, а также быстро их вывести, чтобы не тратить драгоценные ресурсы ++ Гибкий подход к эксплуатации. Мы можем быстро и легко построить структуру приложения, так как вся структура описывается в конфигурационных файлах - манифестах ++ Гибкий подход в управлении. Kubernetes не потребует перестройки инфраструктуры и прочего, если вы захотели провести тестирование, внедрить новый сервис или сделать деплой по методологии blue-green ++ Универсальность. С помощью манифестов легко переехать, если вы захотели поменять провайдера или переезжали в свой собственный кластер ++ Низкий порог вхождения в использование. Kubernetes довольно легок в освоении манифестов, потому что большую часть работы он делает за вас + +Если вы задумываетесь о преимуществах, описанных выше, и можете точно сказать, что вам нужна гибкость в разработке и быстрое внедрение сервисов, адаптируемый подход и универсальность в управлении большим количеством сервисов и их реплик, то думаю, что пора попробовать Kubernetes и у себя. + +Однако, если ваш проект имеет постоянную нагрузку и не требует высокой степени гибкости и быстрого масштабирования, новый функционал появляется редко и у вас есть команда, уверенно работающая с существующим окружением, то на данный момент возможности K8S для бизнеса избыточны, но "посмотреть" на технологию в фоновом режиме все же стоит, так как те или иные условия могут и поменяться. + +[к оглавлению](#Docker) + +## Компоненты и архитектура Kubernetes + +Kubernetes, так как он был реализован как облачное решение, предпочтительнее разворачивать именно в облаке. + +Если вы решились ставить Kubernetes внутри, на своих собственных ресурсах, то вам придется позаботиться о развёртывании и обслуживании такой инфраструктуры вокруг кластера как: репозиторий для контейнеров, внешние или внутренние балансировщики, сетевые хранилища, хранилище секретов, решения для сбора логов и метрик. Также важна и внутренняя инфраструктура “кубера”: CertManager, Ingress, Istio и другие. + +1) Основной компонент K8S состоит из __Кластера (Cluster)__ + +Сам кластер состоит из __узлов или нодах (Nodes)__, в которых помимо контейнеров компонентов самого кластера, размещаются контейнеры наших проектов и сервисов. + +2) __Worker nodes__ - сервер, на котором запускаются и работают контейнеры + +Состоит из компонентов: + ++ kubelet - сервис или агент, который контролирует запуск компонентов (контейнеров) кластера ++ kube-proxy - конфигурирует правила сети на узлах + +3) Плоскость управления (__Master nodes__) управляет рабочими узлами и подами в кластере. Там располагаются компоненты, которые управляют узлами кластера и предоставляют доступ к API. + +__Control plane__ или Kubernete Master, состоит из компонентов: + ++ kube-apiserver - предоставляет API кубера ++ kube-scheduler - планирует размещение подов на узлах кластера ++ kube-controller-manager - запускает контроллеры ++ kubelet - сервис или агент, который контролирует запуск основных компонентов (контейнеров) кластер ++ etcd - распределенное key-value хранилище для всех данных кластера. Необязательно располагается внутри мастера, может стоять как отдельный кластер + +Когда запускаем команды управления они всегда посылаются именно на Master nodes. С воркер нодами напрямую не работаем. + +__Виды контроллеров__ ++ Deployments - контроллер, который управляет состоянием развертывания подов, которое описывается в манифесте, следит за удалением и созданием экземпляров подов. Управляет контроллерами ReplicaSet. ++ ReplicaSet - гарантирует, что определенное количество экземпляров подов всегда будет запущено в кластере. ++ StatefulSets - так же как и Deployments, управляет развертыванием и масштабированием набора подов, но сохраняет набор идентификаторов и состояние для каждого пода. ++ DaemonSet - гарантирует, что на каждом узле кластера будет присутствовать экземпляр пода. ++ Jobs - создает определенное количество подов и смотрит, пока они успешно не завершат работу. Если под завершился с ошибкой, повторяет создание, которое мы описали определенное количество раз. Если под успешно отработал, записывает это в свой журнал. ++ CronJob - запускает контроллеры Jobs по определенному расписанию. + +[к оглавлению](#Docker) + +## Kubernet Cluster + +Состоит минимум из одного (для непрерывной и безопасной работы может быть больше), Мастер нода. И минимум один Воркер нод (на практике их обычно больше) + +В мастер ноде указываем откуда брать Docker Images (из Dockerhub, AWS Container Registry ECR, Google Container Registry, Azure Container Registry и тд). Контейнеры разворачиваются на воркер нодах, заполняя память и процессоры. Если ресурсов будет не хватать, кубер может предложить масштабироваться, добавив еще воркер нодов. +```java +//minikube исп для управления при локальном запуске. при работе с AWS, Google кластером и тд нужно использовать соответствующий кластер и систему управления +minikube start -p NAME - запустить с нашим именем +minikube start --cpus=2 --memory=4gb/4000mb --disk-size=20gb - с конкретными ресурсами +minikube start --no-vtx-check - если ошибка и просить включить виртуализацию в биосе +minikube ssh - зайти внутрь виртуальной машины (можно через VirtualBox) + + +kubectl get componentstatuses - состояние кластера +kubectl cluster-info - инфа о кластере +kubectl get nodes +kubectl version --client + +``` + +Чтобы загрузить Докер образ в кластер нужно + +[к оглавлению](#Docker) + +## Kubectl +При работе с кластером нам потребуется инструмент командной строки kubectl. https://Kubernetes.io/ru/docs/tasks/tools/install-kubectl/ + +Его можно установить на локальную машину и управлять несколькими кластерами с одной точки. + +Основные команды, которые мы часто будем использовать: + ++ Kubectl get [имя объекта] -n [Имя_пространства_имен] - команда get, как несложно понять из ее названия, выводит нужную нам информацию в основном виде таблицы. Также с помощью нее можно получить yaml, который хранится внутри кластера. Очень часто применяется ключ -n для указания пространства имен, где лежат объекты. + +Примеры: +```java +kubectl get services -n Имя_неймспейса - выводит все сервисы в пространстве имён + +kubectl get pods --all-namespaces - выводит все поды во всех пространств имён + +kubectl get pods -o wide - выводит все поды в текущем пространстве имён с подробностями в виде расширенной таблицы + +kubectl get pods -n Имя_неймспейса - выводит все поды в пространстве имён +``` + ++ Kubectl apply -f [имя манифеста yaml] - команда apply применяет манифест к кластеру, управляет созданием объектов в кластере с помощью манифестов yaml. Если в манифесте не указано пространство имен, его можно задать с помощью ключа -n [Имя_пространства_имен]. + +Примеры: +```java +kubectl apply -f ./my-manifest.yaml - создать ресурсы + +kubectl apply -f ./my1.yaml -f ./my2.yaml - создать ресурсы из нескольких файлов + +kubectl apply -f ./dir - создать ресурсы из всех файлов манифеста в директории + +kubectl apply -f https://K8S.io/manifest - создать ресурсы из URL-адреса +``` + +Отличная шпаргалка по командам есть в документации https://Kubernetes.io/ru/docs/reference/kubectl/cheatsheet/ + +[к оглавлению](#Docker) + +## Основные Обьекты Kubernetes + +Контейнер не является обьектом K8S - это обьект Докера) + +1) Pod - самый маленький обьект Кубера, который мы можем создать. Состоит из контейнера (обычно) или нескольких контейнеров (если надо). Чаще один, чтобы при масштабировании не плодить лишние копии +2) Deployment - состоит из одного или нескольких одинаковых Подов, даже если в разных Нодах +3) Service - дает доступ к Подам, которые в Деплое, через ClusterIP, NodePort, LoadBalancer, ExternalName +4) Nodes - сервера, где все это работает +5) Cluster - обьединение нодов + +Т.е. расположены от меньше к большему и каждый следующий содержит предыдущие + +[к оглавлению](#Docker) + +## Kubernetes Pods + +1) Запуск и управление из консоли + +```java +kubectl run name --image=imageName --port=80 - создание пода с именем name из образа imageName (если нет локально скачает с Докерхаба) на порту 80 +kubectl get pods - список Подов +kubectl delete pods name - удалить Под с именем name +kubectl describe pods name - описание Пода name. Нода, на которой он развернут, его IP внутри кластера, контейнеры внутри Пода, Volums, последние Events и много другой инфы +kubectl exec name date - запустить команду в Поде, например date +kubectl exec -it name sh - запустить команду в Поде, например sh - консоль, и остаться внутри Пода +kubectl logs name - логи Пода name +kubectl port-forward name 7788:80 - переносим с локального порта 7788 на порт 80 Пода name +Ctrl+C - выйти из Пода +``` +2) Для запуска из YML файла + +Сам файл, например простой pod-my-web-ver1.yaml: +```java +apiVersion : v1 +kind: Pod +metadata: + name: my-web + labels: + env : prod + app : main + tier : fronted + owner : Shell26 +spec: + containers: + - name : container-apache + image: httpd:latest + ports: + - containerPort: 80 + + - name : container-api + image: tomcat:8.5.38 + ports: + - containerPort: 8080 +``` +И запускаем файл через консоль: +```java +kubectl apply -f ./pod-my-web-ver1.yaml +``` + +Если изменили файл, убиваем старый Под собранный из файла и запускаем новый: +```java +kubectl delete -f ./pod-my-web-ver1.yaml +kubectl apply -f ./pod-my-web-ver1.yaml +``` +__Без удаления можно менять только поле image, иначе ошибка. Достаточно повторно запустить kubectl apply__ + +[к оглавлению](#Docker) + +## Kubernetes Deployment + +Если что-то случится с Кластером и Нода умерла, то Кластер перезапустит Ноды, но Поды автоматически не пересоберутся. Для этого Поды создают через Девелопмент, а он управляет Подами + +1) Из консоли: +```java +kubectl create deployments name --image httpd:latest +kubectl get deploy +kubectl describe deployments name +kubectl scale deployment name --replicas 4 - создаст 4 копии и ReplicaSet, раскидав копии по разным Нодам +kubectl get rs - покажет ReplicaSet +kubectl autoscale deployment name --min=4 --max=6 --cpu-percent=80 - автоматически масштабирует для загрузки cpu на 80 и создаст обьект HorizontalPodAutoscaler +kubectl get hpa - покажет HorizontalPodAutoscaler +kubectl rollout status deployment/name +kubectl set image deployment/name oldimagename=newimagename - обновим в нашем Деплое контейнер oldimagename на новый newimagename +kubectl rollout undo deployment/name - откатит на прошлую версию +kubectl rollout history deployment/name - покажет историю обновлений +kubectl rollout undo deployment/name --to-revision=2 - откатит на версию 2 +kubectl rollout restart deployment/name - пересоберет текущую версию +``` + +2) Из файла-манифеста deployment-simple.yaml: + +```java +apiVersion : apps/v1 +kind: Deployment +metadata: + name: my-web-deployment + labels: + app : my-k8s-application +spec: + replicas: 2 //сколько реплик делать на Нодах, если надо + selector: //с какими Подами будет работать Deployment + matchLabels: //с Подами которые совпадают по Lables + project: shell //именно такой + template: //тут часть с Подами + metadata: + labels: + project: shell + spec: + containers: + - name : shell-web + image: httpd:latest + ports: + - containerPort: 80 +... //можно в этом же файле разделим тремя точками, можно в отдельном файле +apiVersion: autoscaling/v2 +kind: HorizontalPodAutoscaler +metadata: + name: my-autoscaling +spec: + scaleTargetRef: //что он автоскейлит? + apiVersion: apps/v2 + kind: Deployment //Деплоймент + name: my-web-deployment //конкретно этот Деплоймент + minReplicas: 3 + maxReplacas: 5 + metrics: //как скейлит, основываясь на чем? + - type: Resource //либо такой вариант + resource: + name: cpu + target: + type: Utilization + averageUtilization: 90 + - type: Resource //либо такой вариант + resource: + name: memory + target: + type: Utilization + averageUtilization: 70 +``` + +Запускаем Манифест-файл: +```java +kubectl apply -f ./deployment-simple.yaml +``` +В итоге создаст два обьекта, Deployment и HorizontalPodAutoscaler + +[к оглавлению](#Docker) + +## Kubernetes Services +__Типы:__ + ++ ClusterIP - IP только внутри K8S Кластера. Выбирается по-умолчанию. Один такой сервис существует всегда, Kubernetes создает его автоматически ++ NodePort - Определенный порт на всех K8S Воркер Нодах ++ ExternalName - DNS CNAME Record ++ LoadBalancer - Только для облачных кластеров (AWS, Google, Azure) + +1) Из консоли: +```java +kubectl expose deployment name-deploy --type=NodePort --port 80 - создаем Сервис для управлением Деплоем name-deploy, типа NodePort на порту 80 + kubectl get services - + kubectl delete svc name +``` +2) Манифест-файл deployment-service.yaml: +```java +apiVersion : apps/v1 +kind: Deployment +metadata: + name: my-web-deployment + labels: + app : my-k8s-application +spec: + replicas: 2 // + selector: // + matchLabels: // + project: shell //Сервис коннектится сюда, а не выше (Девелопмент) + template: // + metadata: + labels: + project: shell + spec: + containers: + - name : shell-web + image: httpd:latest + ports: + - containerPort: 80 +... +apiVersion: v1 +kind: Service +metadata: + name: my-service + labels: + env : prod + owner : Me +spec: + type: LoadBalancer + selector: //чем управляет сервис, не Деплоем, а какими Подами + project: shell //выбираем Поды с этим лейблом + ports: + - name : app-listener + protocol : TCP + port : 80 //Порт для LoadBalancer + targetPort: 80 //Порт для Pod +``` + +Запускаем Манифест-файл: +```java +kubectl apply -f ./deployment-service.yaml +``` + +[к оглавлению](#Docker) + +## Kubernetes Ingress Controller + +Отдельное приложение, которое позволяет управлять Сервисами внутри Кластера, с помощью одного публичного Сервиса, к которому есть публичный доступ. Вместо создания отдельных публичных Сервисов для каждого приложения внутри Кластера. Ingress Rules позволяют понять, к какому именно Ноду внутри Кластера обращается пользователь + +Было: +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/docker1.png) + +Стало: +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/docker2.png) + +Ingress Controller нужно подключать отдельно, например: +https://docs.google.com/spreadsheets/d/191WWNpjJ2za6-nbG4ZoUMXMpUK8KlCIosvQB0f-oq3k/edit#gid=907731238 + +Деплоим Ingress Controller к Кластеру +```java +kubectl apply -f https://projectcontour.io/quickstart/contour.yaml - адрес зависит от реализации Ingress Controller +kubectl get services -n projectcontour - посмотреть Ingress Controller +``` + +Например, у нас в кластере 3 Deployments: web1, web2 и tomcat. Web1 и web2 скалированы по 2 копии. Т.е. у нас висит 3 Деплоя, с 5 Подами (2+2+1) +```java +kubectl create deployment web1 --image=dockerhub/web1 +kubectl create deployment web2 --image=dockerhub/web1 +kubectl create deployment tomcat --image=tomcat:8.5.38 +kubectl get deploy + +kubectl scale deployment web1 --replicas 2 +kubectl scale deployment web2 --replicas 2 +kubectl get pods +``` + +Создадим 3 Сервиса, для каждого Деплоя. +```java +kubectl expose deployment web1 --port=80 +kubectl expose deployment web2 --port=80 +kubectl expose deployment tomcat --port=8080 +kubectl get services -o wide - чуть больше информации +``` +Нужно добавить Ingress Rules (ingress-rules.yaml): + +```java +apiVersion: networking.k8s.io/v1beta1 +kind: Ingress +metadata: + name: ingtess-hosts + +spec: + rules: + - host: www.web1.shell.net + http: + paths: + - backend: + serviceName: web1 //на какой Сервис отправлять клиента, если запрос приходит на этот Хост + servicePort: 80 //на какой Порт отправлять клиента, если запрос приходит на этот Хост + + - host: www.shell.net //можно указать путь на страницу, вместо url + http: + paths: "/web2" + - backend: + serviceName: web2 + servicePort: 80 + + - host: cat.shell.net + http: + paths: + - backend: + serviceName: tomcat + servicePort: 8080 +``` +Запускаем Манифест-файл: +```java +kubectl apply -f ./ingress-rules.yaml +kubectl get ingress +kubectl describe ingress +``` + +[к оглавлению](#Docker) + +## Helm Charts + +https://helm.sh/ + +Если мы хотим что-то поменять в уже задеплоенном приложении, например образ, нужно отредактировать yaml файл, запустить команды на деплой Деплоймента и Сервиса. Если опять что-то поменяли, нужно все повторить. + +Helm Charts позволяет сделать поля в yaml файле динамическими, менять их из консоли или использовать несколько yaml файлов с разными параметрами(prod, test). Качаем файл с офф сайта в директорию, и запускаем из строки команду helm + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/docker3.png) + +```java +helm create Chart-Auto /автогенерация, много лишнего для начала +``` +Например возьму yaml файл с описанием Deployments и сделаю динамическими поля с названием проекта (берется из команды, при осздании Деплоимента) "image" и "replicaCount". Значения по-умолчанию хранятся в values.yaml + +deploymets.yaml : +```java +apiVersion : apps/v1 +kind: Deployment +metadata: + name: {{ .Release.Name }}-deployment //.Release.Name берется из имени Деплоимента, когда создаем + labels: + app : {{ .Release.Name }}-application +spec: + replicas: {{ .Values.replicaCount }} + selector: + matchLabels: + project: {{ .Release.Name }} + template: + metadata: + labels: + project: {{ .Release.Name }} + spec: + containers: + - name : {{ .Release.Name }}-web + image: {{ .Values.container.image }} + ports: + - containerPort: 80 +``` +values.yaml : +```java +container: + image: dockerhub/web1 + +replicaCount: 2 +``` + +Запускаем Helm : +```java +helm install name1 path-to-Chart/ +helm list + +helm install name2 path-to-Chart/ --set container.image=dockerhub/web2 --set replicaCount=3 - перезапускаем меняя параметры +helm upgrade name2 path-to-Chart/ --set replicaCount=4 - обновляем +helm install name3 path-to-Chart/ -f prod_values.yaml - берем не дефолтные значения, а из другого yaml файла + +helm package path-to-Chart/ - упаковывает в файл path-to-Chart-0.1.0.tgz +helm install name4 -f path-to-Chart-0.1.0.tgz - запускаем из файла +``` + +Можно брать готовые Helm Charts из сети +```java +helm search repo - ищет в конкретном репозитории +helm repo add bitnami https://charts.bitname.com/bitnami - добавляем репо bitnami +helm install name123 bitnami/apache + +helm search hub apache - ищет в общем хабе +``` +[к оглавлению](#Docker) + diff --git a/img/Spring1.png b/img/Spring1.png new file mode 100644 index 0000000..755a22a Binary files /dev/null and b/img/Spring1.png differ diff --git a/img/Spring2.png b/img/Spring2.png new file mode 100644 index 0000000..44d8a24 Binary files /dev/null and b/img/Spring2.png differ diff --git a/img/Spring3.png b/img/Spring3.png new file mode 100644 index 0000000..3d7db15 Binary files /dev/null and b/img/Spring3.png differ diff --git a/img/Spring4.png b/img/Spring4.png new file mode 100644 index 0000000..2d0a3ad Binary files /dev/null and b/img/Spring4.png differ diff --git a/img/Spring5.png b/img/Spring5.png new file mode 100644 index 0000000..fd2ba3c Binary files /dev/null and b/img/Spring5.png differ diff --git a/img/Spring6.png b/img/Spring6.png new file mode 100644 index 0000000..d5aaec2 Binary files /dev/null and b/img/Spring6.png differ diff --git a/img/Spring7.png b/img/Spring7.png new file mode 100644 index 0000000..2ad1d53 Binary files /dev/null and b/img/Spring7.png differ diff --git a/img/Spring8.png b/img/Spring8.png new file mode 100644 index 0000000..753da16 Binary files /dev/null and b/img/Spring8.png differ diff --git a/img/Spring9.png b/img/Spring9.png new file mode 100644 index 0000000..300acef Binary files /dev/null and b/img/Spring9.png differ diff --git a/img/docker1.png b/img/docker1.png new file mode 100644 index 0000000..67dd84b Binary files /dev/null and b/img/docker1.png differ diff --git a/img/docker2.png b/img/docker2.png new file mode 100644 index 0000000..efad929 Binary files /dev/null and b/img/docker2.png differ diff --git a/img/docker3.png b/img/docker3.png new file mode 100644 index 0000000..4a9c95e Binary files /dev/null and b/img/docker3.png differ diff --git a/java9-16.md b/java9-16.md index b71ab84..7fff5f3 100644 --- a/java9-16.md +++ b/java9-16.md @@ -12,6 +12,18 @@ # Java 9 ++ Полная поддержка клиента HTTP 2.0: Вопрос в скорости, и HTTP 2.0 предоставляет более высокие результаты, колеблющиеся от 11.81% до 47.7% по сравнении с клиентом HTTP 1.1 + ++ Проект Jigsaw (в переводе “головоломка”) направлен на модуляризацию Java. Это значит, что программный код разбивается на части и организуется по модулям в зависимости от задач, которые эти модули выполняют. Это позволяет использовать модули повторно, упрощает их организацию и отладку. Что ведет к оптимизированной и отлаженной разработке ПО. Это ключевое отличие Java 9 от Java 8. + ++ Jshell: Новый инструмент командной строки. Если разработчик хочет автономно запустить несколько строк Java, то это можно выполнить без необходимости заворачивать все в отдельный метод или проект. Это интерактивный инструмент, позволяющий тестировать небольшие части кода без необходимости создавать новые классы. Он оснащен функциями истории и автозаполнения, а также рядом других особенностей, включая сохранение и загрузку написанных выражений. + Что касается точек с запятой — можно забыть про них: Существуют различные альтернативы наподобие плагинов REPL для популярных IDE или веб-консоли Java REPL, но ни одна из них не является официальной. + ++ Microbenchmark: Теперь производительность отдельных небольших частей кода можно измерить стандартизированным методом. Анализ JMH за наносекунды уникален для Java 9 + ++ G1 — дефолтный сборщик мусора. Очень часто сталкиваемся с заблуждением, что в Java есть только один сборщик мусора, хотя по факту их 4. Parallel / Throughput Collector считался дефолтным в прошлых версиях, но теперь его заменил G1, который был представлен в Java 7 и был разработан для лучшей поддержки куч размером более 4GB. Он вызывает меньше GC пауз, но если они все же происходят, то длятся дольше. + ++ Изображения с мульти-разрешением. Этот API позволяет инкапсулировать набор изображений с разными разрешениями в единый объект. Таким образом, разработчик может получить изображение с определенным разрешением или все варианты внутри одного. # Java 10 # Java 11 diff --git a/oop.md b/oop.md index 5cdb287..6c48481 100644 --- a/oop.md +++ b/oop.md @@ -15,6 +15,7 @@ + [Что такое _статическое_ и _динамическое связывание_?](#Что-такое-статическое-и-динамическое-связывание) + [Принцип SOLID?](#Принцип-SOLID) + [Что такое Монолит, Микросервис?](#Что-такое-Монолит,-Микросервис) ++ [Что такое Continuous Integration, Continuous Delivery/Deployment?](#Что-такое-Continuous-Integration,-Continuous-Delivery/Deployment) + [Что выбрать Монолит или Микросервис?](#Что-выбрать-Монолит-или-Микросервис) + [Взаимодействие между микросервисами](#Взаимодействие-между-микросервисами) + [OpenAPI](#OpenAPI) @@ -39,7 +40,7 @@ __Объектно-ориентированное программировани + _Наследование_ - создание новой сущности на базе уже существующей. + _Полиморфизм_ - возможность иметь разные формы для одной и той же сущности. + _Абстракция_ - набор общих характеристик. -+ _Посылка сообщений_ - форма связи, взаимодействия между сущностями. ++ _Пересылка сообщений_ - форма связи, взаимодействия между сущностями. + _Переиспользование_- все что перечислено выше работает на повторное использование кода. Это единственно верный порядок парадигм ООП, так как каждая последующая использует предыдущие. @@ -61,11 +62,13 @@ __Наследование__ – это свойство системы, поз [к оглавлению](#ООП) ## Что такое _«полиморфизм»_? -__Полиморфизм__ – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. +__Полиморфизм__ – это свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. Т.е. использовать разные объекты одинаково, через общий интерфейс. + +Пример: у нас есть метод draw(). Для круга он рисует окружность, для квадрата — квадрат. Но мы обращаемся к ним одинаково: figure.draw(). Преимуществом полиморфизма является то, что он помогает снижать сложность программ, разрешая использование одного и того же интерфейса для задания единого набора действий. Выбор же конкретного действия, в зависимости от ситуации, возлагается на компилятор языка программирования. Отсюда следует ключевая особенность полиморфизма - использование объекта производного класса, вместо объекта базового (потомки могут изменять родительское поведение, даже если обращение к ним будет производиться по ссылке родительского типа). -Полиморфизм бывает _динамическим_ (переопределение) и _статическим_ (перегрузка). +Полиморфизм бывает _динамическим_ (переопределение методов в потомках) и _статическим_ (перегрузка методов -одинаковое имя, разные параметры). _Полиморфная переменная_, это переменная, которая может принимать значения разных типов, а _полиморфная функция_, это функция у которой хотя бы один аргумент является полиморфной переменной. Выделяют два вида полиморфных функций: @@ -78,15 +81,19 @@ _Полиморфная переменная_, это переменная, ко ## Что такое _«абстракция»_? _Абстрагирование_ – это способ выделить набор общих характеристик объекта, исключая из рассмотрения частные и незначимые. Соответственно, __абстракция__ – это набор всех таких характеристик. +Пример: когда мы говорим «машина», нам важно, что у неё есть двигатель и колёса, а цвет ковриков внутри — неважно. + [к оглавлению](#ООП) ## Что представляет собой _«обмен сообщениями»_? -Объекты взаимодействуют, посылая и получая сообщения. Сообщение — это запрос на выполнение действия, дополненный набором аргументов, которые могут понадобиться при выполнении действия. В ООП посылка сообщения (вызов метода) — это единственный путь передать управление объекту. Если объект должен «отвечать» на это сообщение, то у него должна иметься соответствующий данному сообщению метод. Так же объекты, используя свои методы, могут и сами посылать сообщения другим объектам. Обмен сообщениями реализуется с помощью динамических вызовов, что приводит к чрезвычайно позднему связыванию (extreme late binding). +Объекты взаимодействуют, посылая и получая сообщения. Сообщение — это запрос на выполнение действия + данные. + +В ООП посылка сообщения (вызов метода) — это единственный путь передать управление объекту. Если объект должен «отвечать» на это сообщение, то у него должна иметься соответствующий данному сообщению метод. Так же объекты, используя свои методы, могут и сами посылать сообщения другим объектам. [к оглавлению](#ООП) ## Расскажите про основные понятия ООП: _«класс»_, _«объект»_, _«интерфейс»_. -__Класс__ – это способ описания сущности, определяющий состояние и поведение, зависящее от этого состояния, а также правила для взаимодействия с данной сущностью (контракт). +__Класс__ – шаблон описания сущности, определяющий состояние и поведение, зависящее от этого состояния, а также правила для взаимодействия с данной сущностью (контракт). С точки зрения программирования класс можно рассматривать как набор данных (полей, атрибутов, членов класса) и функций для работы с ними (методов). @@ -99,69 +106,63 @@ __Интерфейс__ – это набор методов класса, дос [к оглавлению](#ООП) ## В чем заключаются преимущества и недостатки объектно-ориентированного подхода в программировании? -Преимущества: - -+ Объектная модель вполне естественна, поскольку в первую очередь ориентирована на человеческое восприятие мира, а не на компьютерную реализацию. -+ Классы позволяют проводить конструирование из полезных компонентов, обладающих простыми инструментами, что позволяет абстрагироваться от деталей реализации. -+ Данные и операции над ними образуют определенную сущность, и они не разносятся по всей программе, как нередко бывает в случае процедурного программирования, а описываются вместе. Локализация кода и данных улучшает наглядность и удобство сопровождения программного обеспечения. -+ Инкапсуляция позволяет привнести свойство модульности, что облегчает распараллеливание выполнения задачи между несколькими исполнителями и обновление версий отдельных компонентов. -+ Возможность создавать расширяемые системы. -+ Использование полиморфизма оказывается полезным при: - + Обработке разнородных структур данных. Программы могут работать, не различая вида объектов, что существенно упрощает код. Новые виды могут быть добавлены в любой момент. - + Изменении поведения во время исполнения. На этапе исполнения один объект может быть заменен другим, что позволяет легко, без изменения кода, адаптировать алгоритм в зависимости от того, какой используется объект. - + Реализации работы с наследниками. Алгоритмы можно обобщить настолько, что они уже смогут работать более чем с одним видом объектов. - + Возможности описать независимые от приложения части предметной области в виде набора универсальных классов, или фреймворка, который в дальнейшем будет расширен за счет добавления частей, специфичных для конкретного приложения. -+ Повторное использование кода: - + Сокращается время на разработку, которое может быть отдано другим задачам. - + Компоненты многоразового использования обычно содержат гораздо меньше ошибок, чем вновь разработанные, ведь они уже не раз подвергались проверке. - + Когда некий компонент используется сразу несколькими клиентами, улучшения, вносимые в его код, одновременно оказывают положительное влияние и на множество работающих с ним программ. - + Если программа опирается на стандартные компоненты, ее структура и пользовательский интерфейс становятся более унифицированными, что облегчает ее понимание и упрощает использование. - -Недостатки: - -+ В сложных иерархиях классов поля и методы обычно наследуются с разных уровней. И не всегда легко определить, какие поля и методы фактически относятся к данному классу. -+ Код для обработки сообщения иногда «размазан» по многим методам (иначе говоря, обработка сообщения требует не одного, а многих методов, которые могут быть описаны в разных классах). -+ Документирование классов - задача более трудная, чем это было в случае процедур и модулей. Поскольку любой метод может быть переопределен, в документации должно говориться не только о том, что делает данный метод, но и о том, в каком контексте он вызывается. -+ Неэффективность и неэкономное распределения памяти на этапе выполнения (по причине издержек на динамическое связывание и проверки типов на этапе выполнения). -+ Излишняя универсальность. Часто содержится больше методов, чем это реально необходимо текущей программе. А поскольку лишние методы не могут быть удалены, они становятся мертвым грузом. + +✅ Ближе к человеческому восприятию мира. + +✅ Код разбивается на логичные части. + +✅ Удобнее поддерживать и расширять. + +✅ Код можно переиспользовать. + +✅ Легко добавлять новые объекты и изменять поведение программы. + +❌ Иногда сложно понять, где что наследуется. + +❌ Методы могут быть «размазаны» по разным классам. + +❌ Документировать сложнее. Поскольку любой метод может быть переопределен, в документации должно говориться не только о том, что делает данный метод, но и о том, в каком контексте он вызывается. + +❌ Дополнительные расходы памяти и производительности. + +❌ Избыточность. Часто содержится больше методов, чем это реально необходимо текущей программе. А поскольку лишние методы не могут быть удалены, они становятся мертвым грузом. [к оглавлению](#ООП) ## Что подразумевают в плане принципов ООП выражения _«является»_ и _«имеет»_? -__«является»__ подразумевает наследование. -__«имеет»__ подразумевает ассоциацию (агрегацию или композицию). +__«является»__ подразумевает наследование. Класс _cat_ является потомком класса _animal_ +__«имеет»__ агрегация или композиция. Класс _car_ имеет класс _engine_ + + [к оглавлению](#ООП) ## В чем разница между _композицией_ и _агрегацией_? -Ассоциация обозначает связь между объектами. Композиция и агрегация — частные случаи ассоциации «часть-целое». +Ассоциация обозначает связь между объектами. -Агрегация предполагает, что объекты связаны взаимоотношением «part-of» (часть). Композиция более строгий вариант агрегации. Дополнительно к требованию «part-of» накладывается условие, что экземпляр «части» может входить только в одно целое (или никуда не входить), в то время как в случае агрегации экземпляр «части» может входить в несколько целых. +Агрегация — «часть» может существовать отдельно и принадлежать нескольким «целым». (Книга находится в библиотеке, её можно перенести в другую). ->Например, книга состоит из страниц и мы не можем вырвать страницу из книги и вложить в другую книгу. Страницы четко привязаны к конкретной книге, поэтому это композиция. -В тоже время мы можем взять и перенести книгу из одной библиотеки в другую - это уже агрегация. +Композиция — «часть» жёстко привязана к «целому». (Страница принадлежит конкретной книге). [к оглавлению](#ООП) ## Что такое _статическое_ и _динамическое связывание_? -Связывание означает наличие связи между ссылкой и кодом. Например, переменная, на которую вы ссылаетесь, привязана к коду, в котором она определена. Аналогично, вызываемый метод привязан к месту в коде, где он определен. Присоединение вызова метода к телу метода. Если связывание проводится компилятором (компоновщиком) перед запуском программы, то оно называется _статическим_ или _ранним связыванием (early binding)_. +Связывание означает наличие связи между ссылкой и кодом. Например, переменная, на которую вы ссылаетесь, привязана к коду, в котором она определена. Аналогично, вызываемый метод привязан к месту в коде, где он определен. -В свою очередь, _позднее связывание (late binding)_ это связывание, проводимое непосредственно во время выполнения программы, в зависимости от типа объекта. Позднее связывание также называют _динамическим (dynamic)_ или _связыванием на стадии выполнения (runtime binding)_. В языках, реализующих позднее связывание, должен существовать механизм определения фактического типа объекта во время работы программы, для вызова подходящего метода. Иначе говоря, компилятор не знает тип объекта, но механизм вызова методов определяет его и вызывает соответствующее тело метода. Механизм позднего связывания зависит от конкретного языка, но нетрудно предположить, что для его реализации в объекты должна включаться какая-то дополнительная информация. +_Статическим_ или _ранним связыванием (early binding)_ — компилятор заранее знает, какой метод будет вызван. (Напр. перегрузка методов). + +_Позднее связывание (late binding) или динамическое_ проводимое непосредственно во время выполнения программы, в зависимости от типа объекта (Напр. переопределение методов в наследниках). Механизм позднего связывания зависит от конкретного языка Итак, фундаментальное различие между статическим и динамическим связыванием в Java состоит в том, что первое происходит рано, во время компиляции на основе типа ссылочной переменной, а второе – позднее, во время выполнения, с использованием конкретных объектов. -Для всех методов Java используется механизм позднего (динамического) связывания, если только метод не был объявлен как `static` или `final` (приватные методы являются `final` по умолчанию). +В Java почти все методы — динамические, кроме `static`, `final` и `private`. Ключевые различия между ранним и поздним связыванием в языке Java: -1) Статическое связывание происходит во время компиляции, а динамическое – во время выполнения. - -2) Поскольку статическое связывание происходит на ранней стадии жизненного цикла программы, его называют ранним связыванием. Аналогично, динамическое связывание называют также поздним связыванием, поскольку оно происходит позже, во время работы программы. +1) Статическое связывание используется в языке Java для разрешения перегруженных методов, в то время как динамическое связывание используется в языке Java для разрешения переопределенных методов. -3) Статическое связывание используется в языке Java для разрешения перегруженных методов, в то время как динамическое связывание используется в языке Java для разрешения переопределенных методов. +2) Аналогично, приватные, статические и терминальные методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять, а все виртуальные методы разрешаются при помощи динамического связывания. -4) Аналогично, приватные, статические и терминальные методы разрешаются при помощи статического связывания, поскольку их нельзя переопределять, а все виртуальные методы разрешаются при помощи динамического связывания. - -5) В случае статического связывания используются не конкретные объекты, а информация о типе, то есть для обнаружения нужного метода используется тип ссылочной переменной. С другой стороны, при динамическом связывании для нахождения нужного метода в Java используется конкретный объект. +3) В случае статического связывания используются не конкретные объекты, а информация о типе, то есть для обнаружения нужного метода используется тип ссылочной переменной. С другой стороны, при динамическом связывании для нахождения нужного метода в Java используется конкретный объект. [к оглавлению](#ООП) @@ -208,58 +209,65 @@ __Принцип инверсии зависимостей (DIP)__
Монолитная архитектура - система с одним сервисом, который отвечает за работу со всей предметной областью бизнеса (приложения) При начале работы над монолитным проектом Достоинства: -+ Быстрая разработка -+ Возможность внесения радикальных изменений (например изменить архитетуру, класс-дизайн, схему бд) -+ Безболезненое тестирование -+ Легкое развертывание -+ Простое hardware масштабирование + +✅ Быстрая разработка + +✅ Возможность внесения радикальных изменений (например изменить архитетуру, класс-дизайн, схему бд) + +✅ Безболезненое тестирование + +✅ Легкое развертывание + +✅ Простое hardware масштабирование С увеличением размера проекта и команды разработки появляются Минусы: -+ Медленное внесение изменений + +❌ Медленное внесение изменений + Команды А и Б работают не пересекаясь. Выкатывается новая версия проекта с багами в коде команды Б. Откатывается на предыдущуюю версию весь код и команды Б и команды А. -+ Уязвимая надежность - если падает часть кода, то не работает все приложение. -+ Трудности в hardware масштабировании. + +❌ Уязвимая надежность - если падает часть кода, то не работает все приложение. + +❌ Трудности в hardware масштабировании. + Одна часть требует оперативу, другая ядра. Приходится апгрейдить "вертикально" - покупать больше оперативных планок и процессоры с большим кол-вом ядер для всех сервисов. А не по потребностям -+ Дороговизна обновления технологического стека -Например переход на новую версию Java. Чтобы проверить эффективность, нужно переписывать весь проект + +❌ Дороговизна обновления технологического стека. Например переход на новую версию Java. Чтобы проверить эффективность, нужно переписывать весь проект Микросервисная архитектура — система, которая содержит больше одного обособленнго сервиса. Размер значения не имеет. Обособленный сервис — является независимой единицей развертывания, имеет своё хранилище, свою БД (?), отвечает за свою часть предметной области Достоинства: -+ Возможность масштабирования разработки -+ Дешевизна эксперементов с новым стеком -+ Инкрементальное обновление технолошического стека -+ Повышенная надежность и отказоустойчивость + +✅ Возможность масштабирования разработки + +✅ Дешевизна эксперементов с новым стеком + +✅ Инкрементальное обновление технолошического стека + +✅ Повышенная надежность и отказоустойчивость Минусы: -+ Сложность проектирования архитектуры -+ Долгое внесение радикальных изменений -+ Нетривиальное тестирование -+ Повышенные требования к Continuous Integration/Continuous Delivery (деплою) +❌ Сложность проектирования архитектуры -Непрерывная интеграция (CI) — процесс автоматического принятия изменений. Первичный процесс обновления ПО, в рамках которого все изменения на уровне кода вносятся в единый центральный репозиторий. Такое внесение принято называть слиянием. После каждого слияния (которое проходит по несколько раз в день) в изменяемой системе происходит автоматическая сборка (часто приложение упаковывается в Docker) и тестирование (проверка конкретных модулей кода, UI, производительности, надёжности API). Таким образом разработчики страхуются от слишком поздних обнаружений проблем в обновлениях. -+ Возможность локальной сборки проекта -+ На каждый коммит в кдаленный репо запускаем сборки проекта в системе (Jenkins, TravisCI) -+ Сломанная сборка запрещает слияние +❌ Долгое внесение радикальных изменений -Непрерывная доставка (CD) — CI + CD. Автоматизированый процесс доставки изменений до продакшена. Теперь новая версия не только создаётся и тестируется при каждом изменении кода, регистрируемом в репозитории, но и может быть оперативно запущена по одному нажатию кнопки развёртывания. Однако запуск развёртывания всё ещё происходит вручную — ту самую кнопку всё же надо кому-то нажать. Этот метод позволяет выпускать изменения небольшими партиями, которые легко изменить или устранить в случае необходимости. -+ Поддерживаем артефакты стабильными -+ Развертываем по кнопке +❌ Нетривиальное тестирование + +❌ Повышенные требования к Continuous Integration/Continuous Delivery (деплою) Сложности проектирования Микросервисов + Сложно разделить предметную область на части -Не стоит делить монолит на микросервисы по функциональным возможностям. Это приведет к распределенному монолиту - архитектура, включающая недостатки и того и другого. Монолит нужно разбивать по пренадлежности к какой-то области бизнеса. Отличить одно от другого довольно сложно. Т.е. по принципу единой ответственности. +Не стоит делить монолит на микросервисы по функциональным возможностям. Это приведет к распределенному монолиту - архитектура, включающая недостатки и того и другого. Монолит нужно разбивать по пренадлежности к какой-то области бизнеса. Отличить одно от другого довольно сложно. Т.е. по принципу единой ответственности. Соответствие принципам Low coupling, high cohesion способствует быстрому внесению изменений и легкому тетсированию. Сильная связанность high cohesion - части системы, которые изменяются вместе, должны находиться ближе друг к другу -Слабая связанность low coupling - части системы, которые изменяются параллельно, должны иметь как можно меньше зависимостей друг на друга +Слабая связанность low coupling - части системы, которые изменяются параллельно, должны иметь как можно меньше зависимостей друг на друга b внутри сервиса и между сервисами ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP1.png) -И внутри сервиса и между сервисами + Задержки при межсервисном взаимодействии влияют на производительность + сеть ненадежна + Межсервисное взаимодействие влияет на доступность @@ -267,6 +275,19 @@ __Принцип инверсии зависимостей (DIP)__
[к оглавлению](#ООП) +## Что такое Continuous Integration, Continuous Delivery/Deployment? + +Непрерывная интеграция (CI) — процесс автоматического принятия изменений. Первичный процесс обновления ПО, в рамках которого все изменения на уровне кода вносятся в единый центральный репозиторий. Такое внесение принято называть слиянием. После каждого слияния (которое проходит по несколько раз в день) в изменяемой системе происходит автоматическая сборка (часто приложение упаковывается в Docker) и тестирование (проверка конкретных модулей кода, UI, производительности, надёжности API). Таким образом разработчики страхуются от слишком поздних обнаружений проблем в обновлениях. ++ Возможность локальной сборки проекта ++ На каждый коммит в кдаленный репо запускаем сборки проекта в системе (Jenkins, TravisCI) ++ Сломанная сборка запрещает слияние + +Непрерывная доставка (CD) — CI + CD. Автоматизированый процесс доставки изменений до продакшена. Теперь новая версия не только создаётся и тестируется при каждом изменении кода, регистрируемом в репозитории, но и может быть оперативно запущена по одному нажатию кнопки развёртывания. Однако запуск развёртывания всё ещё происходит вручную — ту самую кнопку всё же надо кому-то нажать. Этот метод позволяет выпускать изменения небольшими партиями, которые легко изменить или устранить в случае необходимости. ++ Поддерживаем артефакты стабильными ++ Развертываем по кнопке + +[к оглавлению](#ООП) + ## Что выбрать Монолит или Микросервис? Микро если: + Предметная область хорошо делится на части @@ -292,9 +313,8 @@ https://martinfowler.com/ + Доступность и отказоустойчивость + Архитектура -_Транспорт_ -+ стандарт JSON внутри HTTP -HTTP - простой, множество инструментов, человекочитаемость. Из-за текстового формата большие объемы и нужно оптимизировать +__Транспорт__ ++ стандарт JSON внутри HTTP (REST, RPC, GraphQL). Простой, множество инструментов, человекочитаемость. Из-за текстового формата большие объемы и нужно оптимизировать + бинарные протоколы: gRPC, Thrift, Avro и т.д. Выше производительность, оптимизированно представление данных, можно определить схему сообщения из коробки, но меньше инструментов и не читаемы человеком @@ -302,32 +322,38 @@ HTTP - простой, множество инструментов, челове Что выбрать? + Начинать с HTTP + Бинарные не бесплатные -+ Большинство проблем не из-за протокола ++ Большинство проблем не из-за протокола! + Для публичных API всегда HTTP либо несколько реализаций одна из которых HTTP -_API_ +__API__ + Application Programming Interface - интерфейс, определяющий способ и схему взаимодействия с сервисом + Remote Procedure Call (RPC) + Только POST метод. Все параметры в теле запроса. Любой статус !=200, считается ошибкой Выбирают: Простая реализация, Внутренние API, HTTP только как транспорт + Representational State Transfer (REST) + Архитектурный стиль, в основе которого лежат ресурсы и их идентификация, изменение их состояния через представление. REST != HTTP Выбирают: Своего рода стандарт, Внутренние API, Публичные API, HTTP как протокол + GraphQL + Язык запросов для API. Например можно задать конкретный набор полей, которые хотим получить в ответе на запрос Выбирают: Типо-безопасность, API для Web и мобильных клиентов, тут свой инструментарий -Проектирвоание +__Проектирвоание__ + Клиент-сервер + Stateless - сервис не должен хранить состояние в памяти, это важно для масштабирования + Кеширование __Типы сообщений__ + Команды ++ Императивное управление (звучит как "сделать что-то"), вызывают сильную связанность, зато понятны - названия обычно отражают логику ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP2.png) + События ++ Декларативное управление (звучит как "что-то произошло"), снижают связанность, менее понятны - не знаем что скрыто в notifyOrderCreated ![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/OOP3.png) @@ -423,15 +449,16 @@ Database Schema as a Code - текущая схема должна быть пр [к оглавлению](#ООП) ## Тестирование микросервисов -+ Компонентные для тестирования логики приложения -+ Модульные для тестирования вычислений -+ Компонентных должно быть больше, чем модульных, т.к. изменяемая среда, много зависимочти между классами ++ Компонентные тесты проверяют работу сервисов и их взаимодействие ++ Модульные тесты для тестирования отдельных функций + +Компонентных должно быть больше, чем модульных, т.к. изменяемая среда, много зависимочти между классами -Тесты должны быть независимыми и уметь работать параллельно. Тесты не должны очищать БД или буффер брокера сообщений перед/после своего запуска/завершения +Тесты должны быть независимыми и уметь работать параллельно. Для тестирования API лучше делать реальные HTTP вызовы, валидировать ответ согласно схеме (JSON). RESTAssured как пример инструмента. -При тестирование взаимодействия с БД на каждый запуск тестов локально поднимается БД. Embedded или TestContainers +При тестирование взаимодействия с БД на каждый запуск тестов локально поднимается БД. Embedded или TestContainers. Тесты не должны очищать БД или буффер брокера сообщений перед/после своего запуска/завершения При тестировании взаимодействия микросервисов на каждый запуск тестов локально HTTP-сервер, создаем моки операций сервисов, выполняем, моки валидируем. WireMock, MockServer diff --git a/spring.md b/spring.md index 336d9f9..dba386c 100644 --- a/spring.md +++ b/spring.md @@ -9,6 +9,7 @@ + [Жизненный цикл Context](#Жизненный-цикл-context) + [Как завершить работу контекста](#Как-завершить-работу-контекста) + [Bean](#Bean) ++ [Жизненный цикл бинов](#Жизненный-цикл-бинов) + [Как настроить класс как Spring Bean](#Как-настроить-класс-как-spring-bean) + [Статический Bean](#Статический-bean) + [Inversion of Control](#Inversion-of-control) @@ -16,6 +17,7 @@ + [Как реализуется DI в Spring Framework?](#Как-реализуется-di-в-spring-framework) + [Связывание и @Autowired](#Связывание-и-@Autowired) + [MVC](#mvc) ++ [Шаблон проектирования Front Controller](#Шаблон-проектирования-Front-Controller) + [Связывание форм](#Связывание-форм) + [Исключения в Spring MVC](#Исключения-spring-mvc) + [Локализация в приложениях Spring MVC](#Локализация-в-приложениях-spring-mvc) @@ -32,7 +34,10 @@ + [@Profile](#Profile) + [@LookUp](#LookUp) + [@Target и @Retention](#@Target-и-@Retention) ++ [@Resource](#@Resource) ++ [@Inject](#@Inject) + [@Autowired vs @Resource vs @Inject](#@Autowired-vs-resource-vs-inject) ++ [@Conditional](#@Conditional) + [Как управлять транзакциями в Spring](#Как-управлять-транзакциями-в-spring) + [Как Spring работает с DAO](#Как-spring-работает-с-dao) + [Model vs ModelMap vs ModelAndView](#Model-vs-modelMap-vs-modelAndView) @@ -98,12 +103,25 @@ __Spring ApplicationContext Container__ ApplicationContext является б + AnnotationConfigApplicationContext — метаданные конфигурируются с помощью аннотаций прямо на классах. ++ WebApplicationContext — для веб-приложений + + GenericGroovyApplicationContext - эта конфигурация работает по сути так же, как и Xml, только с Groovy-файлами. К тому же, GroovyApplicationContext нормально работает и с Xml-файлом. Принимает на вход строку с конфигурацией контекста. Чтением контекста в данном случае занимается класс GroovyBeanDefinitionReader. Groovy — объектно-ориентированный язык программирования разработанный для платформы Java как альтернатива языку Java с возможностями Python, Ruby и Smalltalk. Groovy использует Java-подобный синтаксис с динамической компиляцией в JVM байт-код и напрямую работает с другим Java кодом и библиотеками. Язык может использоваться в любом Java проекте или как скриптовый язык. При этом мы можем указать несколько файлов конфигурации Spring. +Отличия ApplicationContext и BeanFactory +1. ApplicationContext загружает все бины при запуске, а BeanFactory - по требованию. +2. ApplicationContext расширяет BeanFactory и предоставляет функции, которые подходят для корпоративных приложений: + a. поддержка внедрения зависимостей на основе аннотаций; + b. удобный доступ к MessageSource (для использования в интернационализации); + c. публикация ApplicationEvent - для бинов, реализующих интерфейс ApplicationListener, с помощью интерфейса ApplicationEventPublisher; + d. простая интеграция с функциями Spring AOP. +3. ApplicationContext поддерживает автоматическую регистрацию BeanPostProcessor и BeanFactoryPostProcessor. Поэтому всегда желательно использовать ApplicationContext, потому что Spring 2.0 (и выше) интенсивно использует BeanPostProcessor. +4. ApplicationContext поддерживает практически все типы scope для бинов, а BeanFactory поддерживает только два - Singleton и Prototype. +5. В BeanFactory не будут работать транзакции и Spring AOP. Это может привести к путанице, потому что конфигурация с виду будет корректной + ## Жизненный цикл Context + Контейнер создается при запуске приложения + Контейнер считывает конфигурационные данные (парсинг XML, JavaConfig) @@ -139,9 +157,15 @@ https://habr.com/ru/post/222579/ В Spring Framework существуют такие свойства, определяющие бины: + class - Этот атрибут является обязательным и указывает конкретный класс Java-приложения, который будет использоваться для создания бина. -+ name - Уникальный идентификатор бина. В случае конфигурации с помощью xml-файла, вы можете использовать свойство “id” и/или “name” для идентификации бина. ++ name - Уникальный идентификатор бина. В случае конфигурации с помощью xml-файла, вы можете использовать свойство “id” и/или “name” для идентификации бина. Атрибут name также может принимать массив String, что позволяет использовать несколько имен. Первый элемент массива будет являться именем и уникальным идентификатором бина, а остальные будут его псевдонимами. -+ scope - Это свойство определяет область видимости создаваемых объектов. __singleton__ - Определяет один единственный бин для каждого контейнера Spring IoC (используется по умолчанию); __prototype__ - контейнер Spring IoC создаёт новый экземпляр бина на каждый полученный запрос т.е. иметь любое количество экземпляров бина; __request__ - Создаётся один экземпляр бина на каждый HTTP запрос. Касается исключительно ApplicationContext; __session__ - Создаётся один экземпляр бина на каждую HTTP сессию. Касается исключительно ApplicationContext; __web soccet__ - Создаётся один экземпляр бина для определенного сокета. __application__ - Создаётся один экземпляр бина для жизненного цикла бина. Похоже на синглтон, но когда бобы ограничены областью приложения, значения, однажды установленное в applicationScopedBean, будет сохранено для всех последующих запросов, сеансов и даже для другого приложения сервлета, которое будет обращаться к этому Бобу, при условии, что оно выполняется в том же ServletContext. В то время как одноэлементные бобы ограничены только одним контекстом приложения. ++ scope - Это свойство определяет область видимости создаваемых объектов. +- __singleton__ - Определяет один единственный бин для каждого контейнера Spring IoC (используется по умолчанию); +- __prototype__ - контейнер Spring IoC создаёт новый экземпляр бина на каждый полученный запрос т.е. иметь любое количество экземпляров бина; +- __request__ - Создаётся один экземпляр бина на каждый HTTP запрос. Касается исключительно ApplicationContext; +- __session__ - Создаётся один экземпляр бина на каждую HTTP сессию. Касается исключительно ApplicationContext; +- __web socket__ - Создаётся один экземпляр бина для определенного сокета. +- __application__ - Создаётся один экземпляр бина для жизненного цикла бина. Похоже на синглтон, но когда бобы ограничены областью приложения, значения, однажды установленное в applicationScopedBean, будет сохранено для всех последующих запросов, сеансов и даже для другого приложения сервлета, которое будет обращаться к этому Бобу, при условии, что оно выполняется в том же ServletContext. В то время как одноэлементные бобы ограничены только одним контекстом приложения. + constructor-arg - Определяет конструктор, использующийся для внедрения зависимости. Более подробно – далее. @@ -155,6 +179,10 @@ https://habr.com/ru/post/222579/ + lazy-initialization mode - Режим ленивой инициализации даёт контейнеру IoC команду создавать экземпляр бина при первом запросе, а не при запуске приложения. +Классы, аннотированные @Configuration, проксируются через CGLIB. Классы @Component или обычные классы не проксируются и не перехватывают вызовы методов с аннотациями @Bean, что означает, что вызовы не будут маршрутизироваться через контейнер и каждый раз будет возвращаться новый экземпляр бина. + +CGLIB (Code Generation Library) - Это библиотека инструментария байтов, используемая во многих средах Java, таких как Hibernate или Spring. Инструментарий байт-кода позволяет манипулировать или создавать классы после фазы компиляции программы. + __Жизненный цикл бинов:__ + Загрузка описаний бинов, создание графа зависимостей(между бинами) + Создание и запуск BeanFactoryPostProcessors @@ -174,6 +202,112 @@ __Жизненный цикл бинов:__ Интерфейс BeanPostProcessor имеет всего два метода: postProcessBeforeInitialization и postProcessAfterInitialization +## Жизненный цикл бинов + +1. __Парсирование конфигурации и создание BeanDefinition__ + +Цель первого этапа — это создание всех BeanDefinition. Объекты BeanDefinition — это набор метаданных будущего бина, макет, по которому нужно будет создавать бин в случае необходимости. То есть для каждого бина создается свой объект BeanDefinition, в котором хранится описание того, как создавать и управлять этим конкретным бином. Проще говоря, сколько бинов в программе - столько и объектов BeanDefinition, их описывающих. + +BeanDefinition содержат (среди прочего) следующие метаданные: +- Имя класса с указанием пакета: обычно это фактический класс бина. +- Элементы поведенческой конфигурации бина, которые определяют, как бин должен вести себя в контейнере (scope, обратные вызовы жизненного цикла и т.д.). +- Ссылки на другие bean-компоненты, которые необходимы для его работы. Эти ссылки также называются зависимостями. +- Другие параметры конфигурации для установки во вновь созданном объекте - например, ограничение размера пула или количество соединений, используемых в бине, который управляет пулом соединений. + +Эти метаданные преобразуются в набор свойств, которые составляют каждое BeanDefinition. В следующей таблице описаны эти свойства: + +При конфигурации через аннотации с указанием пакета для сканирования или JavaConfig используется класс AnnotationConfigApplicationContext. Регистрируются все классы с @Configuration для дальнейшего парсирования, затем регистрируется специальный BeanFactoryPostProcessor, а именно BeanDefinitionRegistryPostProcessor, который при помощи класса ConfigurationClassParser парсирует JavaConfig, загружает описания бинов (BeanDefinition), создаёт граф зависимостей (между бинами) и создаёт: +```java +Map beanDefinitionMap = new ConcurrentHashMap<>(256); + +``` +в которой хранятся все описания бинов, обнаруженных в ходе парсинга конфигурации. + +2. __Настройка созданных BeanDefinition__ + +После первого этапа у нас имеется коллекция Map, в которой хранятся BeanDefinition-ы. BeanFactoryPostProcessor-ы на этапе создания BeanDefinition-ов могут их настроить как нам необходимо. BeanFactoryPostProcessor-ы могут даже настроить саму BeanFactory ещё до того, как она начнет работу по созданию бинов. В интерфейсе BeanFactoryPostProcessor всего один метод: +```java +public interface BeanFactoryPostProcessor { +void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException; +} +``` + +3. __Создание кастомных FactoryBean (только для XML-конфигурации)__ + +4. __Создание экземпляров бинов__ + +Сначала BeanFactory из коллекции Map с объектами BeanDefinition достаёт те из них, из которых создаёт все BeanPostProcessor-ы, необходимые для настройки обычных бинов. Создаются экземпляры бинов через BeanFactory на основе ранее созданных BeanDefinition + +5. __Настройка созданных бинов__ + +На данном этапе бины уже созданы, мы можем лишь их донастроить. + +Интерфейс BeanPostProcessor позволяет вклиниться в процесс настройки наших бинов до того, как они попадут в контейнер. ApplicationContext автоматически обнаруживает любые бины с реализацией BeanPostProcessor и помечает их как “post-processors” для того, чтобы создать их определенным способом. Например, в Spring есть реализации BeanPostProcessor-ов, которые обрабатывают аннотации @Autowired, @Inject, @Value и @Resource. + +Интерфейс несет в себе два метода: postProcessBeforeInitialization(Object bean, String beanName) и postProcessAfterInitialization(Object bean, String beanName). У обоих методов параметры абсолютно одинаковые. Разница только в порядке их вызова. Первый вызывается до init-метода, второй - после. + +Как правило, BeanPostProcessor-ы, которые заполняют бины через маркерные интерфейсы или тому подобное, реализовывают метод postProcessBeforeInitialization (Object bean, String beanName), тогда как BeanPostProcessor-ы, которые оборачивают бины в прокси, обычно реализуют postProcessAfterInitialization (Object bean, String beanName). + +Прокси — это класс-декорация над бином. Например, мы хотим добавить логику нашему бину, но джава-код уже скомпилирован, поэтому нам нужно на лету сгенерировать новый класс. Этим классом мы должны заменить оригинальный класс так, чтобы никто не заметил подмены. + +Есть два варианта создания этого класса: +- либо он должен наследоваться от оригинального класса (CGLIB) и переопределять его методы, добавляя нужную логику; +- либо он должен имплементировать те же самые интерфейсы, что и первый класс(Dynamic Proxy). + +По конвенции спринга, если какой-то из BeanPostProcessor-ов меняет что-то в классе, то он должен это делать на этапе postProcessAfterInitialization(). Таким образом мы уверены, что initMethod у данного бина, работает на оригинальный метод, до того, как на него накрутился прокси. + +Хронология событий: +1. Сначала сработает метод postProcessBeforeInitialization() всех имеющихся BeanPostProcessor-ов. +2. Затем, при наличии, будет вызван метод, аннотированный @PostConstruct. +3. Если бин имплементирует InitializingBean, то Spring вызовет метод afterPropertiesSet() - не рекомендуется к использованию как устаревший. +4. При наличии, будет вызван метод, указанный в параметре initMethod аннотации @Bean. +5. В конце бины пройдут через postProcessAfterInitialization (Object bean, String beanName). Именно на данном этапе создаются прокси стандартными BeanPostProcessor-ами. Затем отработают наши кастомные BeanPostProcessor-ы и применят нашу логику к прокси-объектам. После чего все бины окажутся в контейнере, который будет обязательно обновлен методом refresh(). +6. Но даже после этого мы можем донастроить наши бины ApplicationListener-ами. +7. Теперь всё + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring1.png) + +6. __Бины готовы к использованию__ + +Их можно получить с помощью метода ApplicationContext#getBean(). + +8. __Закрытие контекста__ + +Когда контекст закрывается (метод close() из ApplicationContext), бин уничтожается. + +Если в бине есть метод, аннотированный @PreDestroy, то перед уничтожением вызовется этот метод. + +Если бин имплементирует DisposibleBean, то Spring вызовет метод destroy() - не рекомендуется к использованию как устаревший. + +Если в аннотации @Bean определен метод destroyMethod, то будет вызван и он. + +__@PostConstruct__ + +Spring вызывает методы, аннотированные @PostConstruct, только один раз, сразу после инициализации свойств компонента. За данную аннотацию отвечает один из BeanPostProcessor-ов. + +Метод, аннотированный @PostConstruct, может иметь любой уровень доступа, может иметь любой тип возвращаемого значения (хотя тип возвращаемого значения игнорируется Spring-ом), метод не должен принимать аргументы. Он также может быть статическим, но преимуществ такого использования метода нет, т.к. доступ у него будет только к статическим полям/методам бина, и в таком случае смысл его использования для настройки бина пропадает. + +Одним из примеров использования @PostConstruct является заполнение базы данных. Например, во время разработки нам может потребоваться создать пользователей по умолчанию. + +__@PreDestroy__ + +Метод, аннотированный @PreDestroy, запускается только один раз, непосредственно перед тем, как Spring удаляет наш компонент из контекста приложения. + +Как и в случае с @PostConstruct, методы, аннотированные @PreDestroy, могут иметь любой уровень доступа, но не могут быть статическими. + +Целью этого метода может быть освобождение ресурсов или выполнение любых других задач очистки до уничтожения бина, например, закрытие соединения с базой данных. + +Обратите внимание, что аннотации @PostConstruct и @PreDestroy являются частью Java EE, а именно пакета javax.annotation модуля java.xml.ws.annotation. И поскольку Java EE устарела в Java 9, то с этой версии пакет считается устаревшим (Deprecated). С Java 11 данный пакет вообще удален, поэтому мы должны добавить дополнительную зависимость для использования этих аннотаций: +```java + +javax.annotation +javax.annotation-api +1.3.2 + +``` + + + ## Как настроить класс как Spring Bean 1) XML конфигурация ```java @@ -203,6 +337,24 @@ MyService service = ctx.getBean(MyService.class); ## Статический Bean Если в классе будет статический метод, то при инициализации впервую очередь создастся статический метод (из-за особенностей статических полей), а потом уже Bean, который "навешивается" на статический метод. +При этом Spring не позволяет внедрять бины напрямую в статические поля, нужно создать нестатический сеттер-метод +```java +@Component +public class TestDataInit { + @Autowired + private static OrderItemService orderItemService; //будет null +} + +@Component +public class TestDataInit { + private static OrderItemService orderItemService; + @Autowired + public void setOrderItemService(OrderItemService orderItemService) { + TestDataInit.orderItemService = orderItemService; + } +} + +``` [к оглавлению](#spring) ## Inversion of Control @@ -210,6 +362,12 @@ MyService service = ctx.getBean(MyService.class); Объекты, создаваемые контейнером, также называются управляемыми объектами (beans). Обычно, конфигурирование контейнера, осуществляется путём внедрения аннотаций (начиная с 5 версии J2SE), но также, есть возможность, по старинке, загрузить XML-файлы, содержащие определение bean’ов и предоставляющие информацию, необходимую для создания bean’ов. +Плюсы такого подхода: +- отделение выполнения задачи от ее реализации; +- легкое переключение между различными реализациями; +- большая модульность программы; +- более легкое тестирование программы путем изоляции компонента или проверки его зависимостей и обеспечения взаимодействия компонентов через контракты. + Объекты могут быть получены одним из двух способов: __Dependency Lookup Поиск зависимости__ — шаблон проектирования, в котором вызывающий объект запрашивает у объекта-контейнера экземпляр объекта с определённым именем или определённого типа. @@ -255,7 +413,7 @@ private Dependency dependency; ``` ## Связывание и @Autowired -Процесс внедрения зависимостей в бины при инициализации называется Spring Bean Wiring. Считается хорошей практикой задавать явные связи между зависимостями, но в Spring предусмотрен дополнительный механизм связывания @Autowired. Аннотация может использоваться над полем или методом для связывания по типу. Чтобы аннотация заработала, необходимо указать небольшие настройки в конфигурационном файле спринг с помощью элемента . +Процесс внедрения зависимостей в бины при инициализации называется Spring Bean Wiring. Считается хорошей практикой задавать явные связи между зависимостями, но в Spring предусмотрен дополнительный механизм связывания @Autowired. Аннотация может использоваться над конструктор, поле, сеттер-метод или метод конфигурации для связывания по типу. Если в контейнере не будет обнаружен необходимый для вставки бин, то будет выброшено исключение, либо можно указать @Autowired(required = false), означающее, что внедрение зависимости в данном месте не обязательно. Чтобы аннотация заработала, необходимо указать небольшие настройки в конфигурационном файле спринг с помощью элемента . Типы связывания: + autowire byName, @@ -263,6 +421,34 @@ private Dependency dependency; + autowire by constructor, + autowiring by @Autowired and @Qualifier annotations +Начиная со Spring Framework 4.3, аннотация @Autowired для конструктора больше не требуется, если целевой компонент определяет только один конструктор. Однако, если доступно несколько конструкторов и нет основного/стандартного конструктора, по крайней мере один из конструкторов должен быть аннотирован @Autowired, чтобы указать контейнеру, какой из них использовать. + +Мы также можем указать Spring предоставить все бины определенного типа из ApplicationContext, добавив аннотацию @Autowired в поле или метод с массивом или коллекцией этого типа, как показано в следующем примере: +```java +@Autowired +private MovieCatalog[] movieCatalogs; +или: +@Autowired +private Set movieCatalogs; +или: +@Autowired +public void setMovieCatalogs(Set movieCatalogs) { +this.movieCatalogs = movieCatalogs; +} +``` + +Даже коллекции типа Map могут быть подключены автоматически, если тип ключа - String. Ключами будут имена бинов, а значениями - сами бины, как показано в следующем примере: +```java +public class MovieRecommender { +private Map movieCatalogs; +@Autowired +public void setMovieCatalogs(Map movieCatalogs){ + this.movieCatalogs = movieCatalogs; +} +// ... +} +``` + [к оглавлению](#spring) ## MVC @@ -300,6 +486,149 @@ Spring MVC предоставляет разработчику следующи + Высокий уровень абстракции для веб-приложений. + В веб-приложениях можно использовать различные части Spring, а не только Spring MVC. +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring2.png) + +## Шаблон проектирования Front Controller + +Паттерн Front Controller обеспечивает единую точку входа для всех входящих запросов. Все запросы обрабатываются одним фрагментом кода, который затем может делегировать ответственность за обработку запроса другим объектам приложения. Он также обеспечивает интерфейс для общего поведения, такого как безопасность, интернационализация и передача определенных представлений определенным пользователям. + +В Spring в качестве Front Controller выступает DispatcherServlet, все действия проходят через него. Как правило в приложении задаётся только один DispatcherServlet с маппингом “/”, который перехватывает все запросы. Это и есть реализация паттерна Front Controller. + +Однако иногда необходимо определить два и более DispatcherServlet-а, которые будут отвечать за свой собственный функционал. Например, чтобы один обрабатывал REST-запросы с маппингом “/api”, а другой обычные запросы с маппингом “/default”. Spring предоставляет нам такую возможность, и для начала нужно понять, что: + +- Spring может иметь несколько контекстов одновременно. Одним из них будет корневой контекст, а все остальные контексты будут дочерними. +- Все дочерние контексты могут получить доступ к бинам, определенным в корневом контексте, но не наоборот. Корневой контекст не может получить доступ к бинам дочерних контекстов. +- Каждый дочерний контекст внутри себя может переопределить бины из корневого контекста. + +Каждый DispatcherServlet имеет свой дочерний контекст приложения. DispatcherServlet по сути является сервлетом(он расширяет HttpServlet), основной целью которого является обработка входящих вебзапросов, соответствующих настроенному шаблону URL. Он принимает входящий URI и находит правильную комбинацию контроллера и вида. Веб-приложение может определять любое количество DispatcherServlet-ов. Каждый из них будет работать в своем собственном пространстве имен, загружая свой собственный дочерний WebApplicationContext (на рисунке - Servlet WebApplicationContext) с вьюшками, контроллерами и т.д. Например, когда нам нужно в одном Servlet WebApplicationContext определить обычные контроллеры, а в другом REST-контроллеры. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring3.png) + +WebApplicationContext расширяет ApplicationContext (создаёт и управляет бинами и т.д.), но помимо этого он имеет дополнительный метод getServletContext(), через который у него есть возможность получать доступ к ServletContext-у. + +ContextLoaderListener создает корневой контекст приложения (на рисунке - Root WebApplicationContext) и будет использоваться всеми дочерними контекстами, созданными всеми DispatcherServlet. Напомню, что корневой контекст приложения будет общим и может быть только один. Root WebApplicationContext содержит компоненты, которые видны всем дочерним контекстам, такие как сервисы, репозитории, компоненты инфраструктуры и т.д. После создания корневого контекста приложения он сохраняется в ServletContext как атрибут, имя которого: +```java +WebApplicationContext.class.getName() + ".ROOT" +``` +Чтобы из контроллера любого дочернего контекста обратиться к корневому контексту приложения, мы можем использовать класс WebApplicationContextUtils, содержащий статические методы: +```java +@Autowired +ServletContext context; +ApplicationContext ac =WebApplicationContextUtils.getWebApplicationContext(context); +if(ac == null){ + return "root application context is null"; +} +``` +__ContextLoaderListener vs DispatcherServlet__ + +1. ContextLoaderListener создает корневой контекст приложения. +2. Каждый DispatcherServlet создаёт себе один дочерний контекст. +3. Дочерние контексты могут обращаться к бинам, определенным в корневом контексте. +4. Бины в корневом контексте не могут получить доступ к бинам в дочерних контекстах (напрямую). +5. Все контексты добавляются в ServletContext. +6. Мы можем получить доступ к корневому контексту, используя класс WebApplicationContextUtils. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring4.png) + +## В чем разница между Filters, Listeners и Interceptors? + +__Filter__ + +Это интерфейс из пакета javax.servlet, имплементации которого выполняют задачи фильтрации либо по пути запроса к ресурсу (сервлету, либо по статическому контенту), либо по пути ответа от ресурса, либо в обоих направлениях. + +Фильтры выполняют фильтрацию в методе doFilter. Каждый фильтр имеет доступ к объекту FilterConfig, из которого он может получить параметры инициализации, и ссылку на ServletContext, который он может использовать, например, для загрузки ресурсов, необходимых для задач фильтрации. Фильтры настраиваются в дескрипторе развертывания веб-приложения. + +В веб-приложении мы можем написать несколько фильтров, которые вместе называются цепочкой фильтров. Веб-сервер решает, какой фильтр вызывать первым, в соответствии с порядком регистрации фильтров. + +Когда вызывается метод doFilter(ServletRequest request, ServletResponse response, FilterChain chain) первого фильтра, веб-сервер создает объект FilterChain, представляющий цепочку фильтров, и передаёт её в метод. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring5.png) + +__Interceptor__ + +Это интерфейс из пакета org.aopalliance.intercept, предназначенный для аспектноориентированного программирования. В Spring, когда запрос отправляется в Controller, перед тем как он в него попадёт, он может пройти через перехватчики Interceptor (0 или более). Это одна из реализаций АОП в Spring. Вы можете использовать Interceptor для выполнения таких задач, как запись в Log, добавление или обновление конфигурации перед тем, как запрос обработается Controller-ом. + +Стек перехватчиков: он предназначен для связывания перехватчиков в цепочку в определенном порядке. При доступе к перехваченному методу или полю перехватчик в цепочке перехватчиков вызывается в том порядке, в котором он был определен. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring6.png) + +Мы можем использовать Interceptor-ы для выполнения логики до попадания в контроллер, после обработки в контроллере, а также после формирования представления. Также можем запретить выполнение метода контроллера. Мы можем указать любое количество перехватчиков. + +Перехватчики работают с HandlerMapping и поэтому должны реализовывать интерфейс HandlerInterceptor или наследоваться от готового класса HandlerInterceptorAdapter. В случае реализации HandlerInterceptor нам нужно переопределить 3 метода, а в случае HandlerInterceptor, только необходимые нам: + +- public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) - вызывается после того, как HandlerMapping определил соответствующий контроллер, но до того, как HandlerAdapter вызовет метод контроллера. С помощью этого метода каждый перехватчик может решить, прервать цепочку выполнения или направить запрос на испольнение дальше по цепочке перехватчиков до метода контроллера. Если этот метод возвращает true, то запрос отправляется следующему перехватчику или в контроллер. Если метод возвращает false, то исполнение запроса прекращается, обычно отправляя ошибку HTTP или записывая собственный ответ в response. + +- public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) - отработает после контроллера, но перед формированием представления. Мы можем использовать этот метод для добавления дополнительных атрибутов в ModelAndView или для определения времени, затрачиваемого методом-обработчиком на обработку запроса клиента. Вы можете добавить больше объектов модели в представление, но вы не можете изменить HttpServletResponse, так как он уже зафиксирован. + +- public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) - отработает после формирования представления. Вызывается только в том случае, если метод preHandle этого перехватчика успешно завершен и вернул true! + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring7.png) + +Следует знать, что HandlerInterceptor связан с бином DefaultAnnotationHandlerMapping, который отвечает за применение перехватчиков к любому классу, помеченному аннотацией @Controller. + +Чтобы добавить наши перехватчики в конфигурацию Spring, нам нужно переопределить метод addInterceptors () внутри класса, который реализует WebMvcConfigurer: +```java +@Override +public void addInterceptors(InterceptorRegistry registry) { +// LogInterceptor applies to all URLs. +registry.addInterceptor(new LogInterceptor()); +// This interceptor applies to URL /admin/oldLogin. +// Using OldURLInterceptor to redirect to new URL. +registry.addInterceptor(new OldLoginInterceptor()) +.addPathPatterns("/admin/oldLogin"); +// This interceptor applies to URLs like /admin/* +// Exclude /admin/oldLogin +registry.addInterceptor(new AdminInterceptor()) +.addPathPatterns("/admin/*")// +.excludePathPatterns("/admin/oldLogin"); +} +``` + +__Filter vs. Interceptor__ + +- Перехватчик основан на механизме Reflection, а фильтр основан на обратном вызове функции. +- Фильтр зависит от контейнера сервлета, тогда как перехватчик не зависит от него. +- Перехватчики могут работать только с запросами к контроллерам, в то время как фильтры могут работать почти со всеми запросами (например, js, .css и т.д.). +- Перехватчики в отличии от фильтров могут обращаться к объектам в контейнере Spring, что даёт им более изощренный функционал. + +Порядок работы: +1. Фильтры до; +2. Перехватчики до; +3. Метод контроллера; +4. Перехватчики после; +5. Фильтры после. + +HandlerInterceptor в основном похож на Servlet Filter, но в отличие от последнего он просто позволяет настраивать предварительную обработку с возможностью запретить выполнение самого обработчика и настраивать постобработку. + +Согласно документации Spring, фильтры более мощные, например, они позволяют обмениваться объектами запроса и ответа, которые передаются по цепочке. Это означает, что фильтры работают больше в области запроса/ответа, в то время как HandlerInterceptors являются бинами и могут обращаться к другим компонентам в приложении. Обратите внимание, что фильтр настраивается в web.xml, а HandlerInterceptor в контексте приложения. + +__Java Listener__ + +Listener (Слушатель) - это класс, который реализует интерфейс javax.servlet.ServletContextListener. Он инициализируется только один раз при запуске вебприложения и уничтожается при остановке веб-приложения. Слушатель сидит и ждет, когда произойдет указанное событие, затем «перехватывает» событие и запускает собственное событие. Например, мы хотим инициализировать пул соединений с базой данных до запуска веб-приложения. ServletContextListener - это то, что нам нужно, он будет запускать наш код до запуска веб-приложения. + +Все ServletContextListeners уведомляются об инициализации контекста до инициализации любых фильтров или сервлетов в веб-приложении. + +Все ServletContextListeners уведомляются об уничтожении контекста после того, как все сервлеты и фильтры уничтожены. + +Чтобы создать свой Listener нам достаточно создать класс, имплементирующий интерфейс ServletContextListener и поставить над ним аннотацию @WebListener: +```java +@WebListener +public class MyAppServletContextListener +implements ServletContextListener{ +//Run this before web application is started +@Override +public void contextInitialized(ServletContextEvent arg0) { + System.out.println("ServletContextListener started"); +} +@Override +public void contextDestroyed(ServletContextEvent arg0) { + System.out.println("ServletContextListener destroyed"); +} +} +``` + + + ## Связывание форм @ModelAttribute - связывает параметр метода или возвращаемое значение метода с именованным атрибутом модели, а затем возвращает его view веб-представлению. @@ -454,7 +783,7 @@ AspectJ де-факто является стандартом реализаци + @Sheduler - Таймер. Раз в сколько-то секунд обрабатывать. + @Resource - Java аннотация, которой можно внедрить зависимость. + @Requared - применяется к методам-сеттерам и означает, что значение метода должно быть установлено в XML-файле. Если этого не будет сделано, то мы получим BeanInitializationException. -+ @RequestMapping - позволяет задать шаблон маппинга URI в методе обработчике контроллера. ++ @RequestMapping - используется для мапинга (связывания) с URL для всего класса или для конкретного метода обработчика. + @ResponseBody - позволяет отправлять Object в ответе. Обычно используется для отправки данных формата XML или JSON. + @ResponseEntity - используется для формирования ответа HTTP с пользовательскими параметрами (заголовки, http-код и т.д.). ResponseEntity необходим, только если мы хотим кастомизировать ответ, добавив к нему статус ответа. Во всех остальных случаях будем использовать @ResponseBody. + @PathVariable - задает динамический маппинг значений из URI внутри аргументов метода обработчика, т.е. позволяет вводить в URI переменную пути в качестве параметра @@ -463,13 +792,13 @@ AspectJ де-факто является стандартом реализаци + @Scope - указывает scope у spring bean. + @Configuration, @ComponentScan и @Bean - для java based configurations. + AspectJ аннотации для настройки aspects и advices, @Aspect, @Before, @After,@Around, @Pointcut и др. - ++ @PageableDefault - устанавливает значение по умолчанию для параметра разбиения на страницы ## Различия @Component, @Service, @Repository, @Controller Они все служат для обозначения класса как Бин. + @Component - Spring определяет этот класс как кандидата для создания bean. + @Service - класс содержит бизнес-логику и вызывает методы на уровне хранилища. Ничем не отличается от классов с @Component. -+ @Repository - указывает, что класс выполняет роль хранилища (объект доступа к DAO). При этом автоматически перехватывает спецефические Java исключения и пробрасывает их дальше как неконтролируемые исключения доступа к данным Spring. Для этого в контексте прописывается класс PersistenceExceptionTranslationPostProcessor. ++ @Repository - указывает, что класс выполняет роль хранилища (объект доступа к DAO). При этом отлавливает определенные исключения персистентности и пробрасывает их как одно непроверенное исключение Spring Framework. Для этого Spring оборачивает эти классы в прокси, и в контекст должен быть добавлен класс PersistenceExceptionTranslationPostProcessor + @Controller - указывает, что класс выполняет роль контроллера MVC. Диспетчер сервлетов просматривает такие классы для поиска @RequestMapping. ## Различия @Controller и @RestController @@ -481,12 +810,13 @@ AspectJ де-факто является стандартом реализаци Если есть два одинаковых бина (по типу и имени) спринг не знает какой именно использовать и выдаёт exeption. Если над одним из этих бинов установленна @Primary, то его использовать предпочтительнее. Но если нам нужно использовать в работе оба этих бина, можно над каждым поставить @Qualifier и задать имя, для идентификации этих бинов. ## @Profile -Используя аннотацию @Profile - мы сопоставляем bean-компонент с этим конкретным профилем; аннотация просто берет имена одного (или нескольких) профилей. Отвечает за то - какие бины буду создаваться, в зависимости от профайла. +Используя аннотацию @Profile - мы сопоставляем bean-компонент с этим конкретным профилем; аннотация просто берет имена одного (или нескольких) профилей. Отвечает за то - какие бины буду создаваться, в зависимости от профайла. Фактически реализована с помощью гораздо более гибкой аннотации @Conditional. Рассмотрим базовый сценарий - у нас есть компонент, который должен быть активным только во время разработки, но не должен использоваться в производстве. Мы аннотируем этот компонент с профилем «dev», и он будет присутствовать в контейнере только во время разработки - в производственном процессе dev просто не будет активен. Или можно задать @Profile("postgres") и @Profile("mysql"), а в application.properties указать, бин с каким профилем использовать = spring.profiles.active = mysql +По умолчанию, если профиль бина не определен, то он относится к профилю “default”. Spring также предоставляет способ установить профиль по умолчанию, когда другой профиль не активен, используя свойство «spring.profiles.default». [к оглавлению](#spring) ## @LookUp @@ -494,8 +824,35 @@ AspectJ де-факто является стандартом реализаци __ПРИМЕР__ - Обычно бины в приложении Spring являтся синглтонами, и для внедрения зависимостей мы используем конструктор или сеттер. Но бывает и другая ситуация: имеется бин Car – синглтон (singleton bean), и ему требуется каждый раз новый экземпляр бина Passenger. То есть Car – синглтон, а Passenger – так называемый прототипный бин (prototype bean). Жизненные циклы бинов разные. Бин Car создается контейнером только раз, а бин Passenger создается каждый раз новый – допустим, это происходит каждый раз при вызове какого-то метода бина Car.Вот здесь то и пригодится внедрение бина с помощью Lookup метода. Оно происходит не при инициализации контейнера, а позднее: каждый раз, когда вызывается метод. - +```java +@Component +public class Car { + @Lookup + public Passenger createPassenger() { + return null; + } + public String drive(String name) { + Passenger passenger = createPassenger(); + passenger.setName(name); + return "car with " + passenger.getName(); + } +} +``` Суть в том, что вы создаете метод-заглушку в бине Car и помечаете его специальным образом – аннотацией @Lookup. Этот метод должен возвращать бин Passenger, каждый раз новый. Контейнер Spring под капотом создаст подкласс и переопределит этот метод и будет вам выдавать новый экземпляр бина Passenger при каждом вызове аннотированного метода. Даже если в вашей заглушке он возвращает null (а так и надо делать, все равно этот метод будет переопределен). +```java +@Component +@Scope("prototype") +public class Passenger { + private String name; + public String getName() { + return name; + } + public void setName(String name) { + this.name = name; + } +} +``` +Теперь при вызове метода drive() мы можем везти каждый раз нового пассажира. Имя его передаётся в аргументе метода drive(), и затем задается сеттером во вновь созданном экземпляре пассажира. [к оглавлению](#spring) @@ -517,6 +874,64 @@ __ПРИМЕР__ - Обычно бины в приложении Spring явля [к оглавлению](#spring) +## @Resource +Java-аннотация @Resource может применяться к классам, полям и методам. Она пытается получить зависимость: сначала по имени, затем по типу, затем по описанию (Qualifier). Имя извлекается из имени аннотируемого сеттера или поля, либо берется из параметра name. При аннотировании классов имя не извлекается из имени класса по умолчанию, поэтому оно должно быть указано явно. + +Указав данную аннотацию у полей или методов с аргументом name, в контейнере будет произведен поиск компонентов с данным именем, и в контейнере должен быть бин с таким именем: +```java +@Resource(name="namedFile") +private File defaultFile; +``` + +Если указать её без аргументов, то Spring Framework поможет найти бин по типу. Если в контейнере несколько бинов-кандидатов на внедрение, то нужно использовать аннотацию @Qualifier: +```java +@Resource +@Qualifier("defaultFile") +private File dependency1; +@Resource +@Qualifier("namedFile") +private File dependency2; +``` + +__Разница с @Autowired:__ +- ищет бин сначала по имени, а потом по типу; +- не нужна дополнительная аннотация для указания имени конкретного бина; +- @Autowired позволяет отметить место вставки бина как необязательное @Autowired(required = false); +- при замене Spring Framework на другой фреймворк, менять аннотацию @Resource не нужно + +[к оглавлению](#spring) + +## @Inject +Размещается над полями, методами, и конструкторами с аргументами. @Inject как и @Autowired в первую очередь пытается подключить зависимость по типу, затем по описанию и только потом по имени. Это означает, что даже если имя переменной ссылки на класс отличается от имени компонента, но они одинакового типа, зависимость все равно будет разрешена: +```java +@Inject +private ArbitraryDependency fieldInjectDependency; +//fieldInjectDependency - отличается от имени компонента, настроенного в контексте приложения: + +@Bean +public ArbitraryDependency injectDependency() { +ArbitraryDependency injectDependency = new ArbitraryDependency(); +return injectDependency; +} +``` + +Разность имён injectDependency и fieldInjectDependency не имеет значения, зависимость будет подобрана по типу ArbitraryDependency. Если в контейнере несколько бинов-кандидатов на внедрение, то нужно использовать аннотацию @Qualifier: +```java +@Inject +@Qualifier("defaultFile") +private ArbitraryDependency defaultDependency; + +@Inject +@Qualifier("namedFile") +private ArbitraryDependency namedDependency; + +//При использовании конкретного имени (Id) бина используем @Named: +@Inject +@Named("yetAnotherFieldInjectDependency") +private ArbitraryDependency yetAnotherFieldInjectDependency +``` + + ## @Autowired vs @Resource vs @Inject Аннотации для внедрения зависимостей. @@ -524,6 +939,59 @@ __ПРИМЕР__ - Обычно бины в приложении Spring явля @Inject (java) или @Autowired (spring) в первую очередь пытается подключить зависимость по типу, затем по описанию и только потом по имени. +## @Conditional +Часто бывает полезно включить или отключить весь класс @Configuration, @Component или отдельные методы @Bean в зависимости от каких-либо условий. + +Аннотация @Conditional указывает, что компонент имеет право на регистрацию в контексте только тогда, когда все условия соответствуют. Может применяться: +- над классами прямо или косвенно аннотированными @Component, включая классы @Configuration; +- над методами @Bean; +- как мета-аннотация при создании наших собственных аннотаций-условий. + +Условия проверяются непосредственно перед тем, как должно быть зарегистрировано BeanDefinition компонента, и они могут помешать регистрации данного BeanDefinition. Поэтому нельзя допускать, чтобы при проверке условий мы взаимодействовали с бинами (которых еще не существует), с их BeanDefinition-ами можно. + +Условия мы определяем в специально создаваемых нами классах, которые должны имплементировать функциональный интерфейс Condition с одним единственным методом, возвращающим true или false: +```java +boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) +``` +Создав свой класс и переопределив в нем метод matches() с нашей логикой, мы должны передать этот класс в аннотацию @Conditional в качестве параметра: +```java +@Configuration +@Conditional(OurConditionClass.class) +class MySQLAutoconfiguration { +//... +} +//Для того, чтобы проверить несколько условий, можно передать в @Conditional несколько классов с условиями: +@Bean +@Conditional(HibernateCondition.class, OurConditionClass.class) +Properties additionalProperties() { +//... +} +``` + + +Если класс @Configuration помечен как @Conditional, то на все методы @Bean, аннотации @Import и аннотации @ComponentScan, связанные с этим классом, также будут распространяться указанные условия. + +Для более детальной настройки классов, аннотированных @Configuration, предлагается использовать интерфейс ConfigurationCondition. + +В одном классе - одно условие. Для создания более сложных условий можно использовать классы AnyNestedCondition, AllNestedConditions и NoneNestedConditions. + +В Spring Framework имеется множество готовых аннотаций (и связанных с ними склассами-условиями, имплементирующими интерфейс Condition), которые можно применять совместно над одним определением бина: + +__ConditionalOnBean__ Условие выполняется, в случае если присутствует нужный бин в BeanFactory. +__ConditionalOnClass__ Условие выполняется, если нужный класс есть в classpath. +__ConditionalOnCloudPlatform__ Условие выполняется, когда активна определенная платформа. +__ConditionalOnExpression__ Условие выполняется, когда SpEL выражение вернуло положительное значение. +__ConditionalOnJava__ Условие выполняется, когда приложение запущено с определенной версией JVM. +__ConditionalOnJndi__ Условие выполняется, только если через JNDI доступен определенный ресурс. +__ConditionalOnMissingBean__ Условие выполняется, в случае если нужный бин отсутствует в контейнере. +__ConditionalOnMissingClass__ Условие выполняется, если нужный класс отсутствует в classpath. +__ConditionalOnNotWebApplication__ Условие выполняется, если контекст приложения не является веб контекстом. +__ConditionalOnProperty__ Условие выполняется, если в файле настроек заданы нужные параметры. +__ConditionalOnResource__ Условие выполняется, если присутствует нужный ресурс в classpath. +__ConditionalOnSingleCandidate__ Условие выполняется, если bean-компонент указанного класса уже содержится в контейнере и он единственный. +__ConditionalOnWebApplication__ Условие выполняется, если контекст приложения является веб контекстом. + + ## Как управлять транзакциями в Spring Spring поддерживает два типа управления транзакциями: + Программное управление транзакциями: Вы должны управлять транзакциями с помощью программирования. Это способ достаточно гибкий, но его сложно поддерживать. Либо через использование TransactionTemplate, либо через реализацию PlatformTransactionManager напрямую. Используется, если нужно работать с небольшим количеством транзакций. @@ -542,6 +1010,65 @@ try { } ``` +------------------------------------------------------------------------------------------------------------------------- +новая инфа +------------------------------------------------------------------------------------------------------------------------- +Для включения возможности управления транзакциями первым делом нужно разместить аннотацию @EnableTransactionManagement у класса-конфигурации @Configuration. + +Аннотация @EnableTransactionManagement означает, что классы, помеченные @Transactional, должны быть обернуты аспектом транзакций. Однако, если мы используем Spring Boot и имеем зависимости spring-data-* или spring-tx, то управление транзакциями будет включено по умолчанию. + +@EnableTransactionManagement отвечает за регистрацию необходимых компонентов Spring, таких как TransactionInterceptor и советы прокси (proxy advices- набор инструкций, выполняемых на точках среза - Pointcut). Регистрируемые компоненты помещают перехватчик в стек вызовов при вызове методов @Transactional. + +Spring создает прокси для всех классов, помеченных @Transactional (либо если любой из методов класса помечен этой аннотацией). Прокси-объекты позволяют Spring Framework вводить транзакционную логику до и после вызываемого метода -главным образом для запуска и коммита/отката транзакции. + +Если мы разместим аннотацию @Transactional над классом @Service, то все его методы станут транзакционными. Так, при вызове, например, метода save() произойдет примерно следующее: + +1. Вначале мы имеем: + - класс TransactionInterceptor, у которого основной метод invoke(...), внутри которого вызывается метод класса-родителя TransactionAspectSupport:invokeWithinTransaction(...), в рамках которого происходит магия транзакций. + - TransactionManager: решает, создавать ли новый EntityManager и/или транзакцию. + - EntityManager proxy: EntityManager - это интерфейс, и то, что внедряется в бин в слое DAO на самом деле не является реализацией EntityManager. В это поле внедряется EntityManager proxy, который будет перехватывать обращение к полю EntityManager и делегировать выполнение конкретному EntityManager в рантайме. Обычно EntityManager proxy представлен классом SharedEntityManagerInvocationHandler. +2. Transaction Interceptor + +В TransactionInterceptor отработает код до работы метода save(), в котором будет определено, выполнить ли метод save() в пределах уже существующей транзакции БД или должна стартовать новая отдельная транзакция. TransactionInterceptor сам не содержит логики по принятию решения, решение начать новую транзакцию, если это нужно, делегируется TransactionManager. Грубо говоря, на данном этапе наш метод будет обёрнут в try-catch и будет добавлена логика до его вызова и после: +```java +try { + transaction.begin(); + // логика до + service.save(); + // логика после + transaction.commit(); + } catch(Exception ex) { + transaction.rollback(); + throw ex; + } +``` + +3. TransactionManager + +Менеджер транзакций должен предоставить ответ на два вопроса: + - Должен ли создаться новый EntityManager? + - Должна ли стартовать новая транзакция БД? TransactionManager принимает решение, основываясь на следующих фактах: + - выполняется ли хоть одна транзакция в текущий момент или нет; + - атрибута «propagation» у метода, аннотированного @Transactional (для примера, значение REQUIRES_NEW всегда стартует новую транзакцию). + +Если TransactionManager решил создать новую транзакцию, тогда: + - Создается новый EntityManager; + - EntityManager «привязывается» к текущему потоку (Thread); + - «Получается» соединение из пула соединений БД; + - Соединение «привязывается» к текущему потоку. + +И EntityManager и это соединение привязываются к текущему потоку, используя переменные ThreadLocal. +4. EntityManager proxy Когда метод save() слоя Service делает вызов метода save() слоя DAO, внутри которого вызывается, например, entityManager.persist(), то не происходит вызов метода persist()напрямую у EntityManager, записанного в поле класса DAO. Вместо этого метод вызывает EntityManager proxy, который достает текущий EntityManager для нашего потока, и у него вызывается метод persist(). +5. Отрабатывает DAO-метод save(). +6. TransactionInterceptor Отработает код после работы метода save(), а именно будет принято решение по коммиту/откату транзакции. + +Кроме того, если мы в рамках одного метода сервиса обращаемся не только к методу save(), а к разным методам Service и DAO, то все они буду работать в рамках одной транзакции, которая оборачивает этот метод сервиса. + +Вся работа происходит через прокси-объекты разных классов. Представим, что у нас в классе сервиса только один метод с аннотацией @Transactional, а остальные нет. Если мы вызовем метод с @Transactional, из которого вызовем метод без @Transactional, то оба будут отработаны в рамках прокси и будут обернуты в нашу транзакционную логику. Однако, если мы вызовем метод без @Transactional, из которого вызовем метод с @Transactional, то они уже не будут работать в рамках прокси и не будут обернуты в нашу транзакционную логику. + +------------------------------------------------------------------------------------------------------------------------- +старая инфа +------------------------------------------------------------------------------------------------------------------------- Аннотация сама по себе определяет область действия одной транзакции БД. Транзакция БД происходит внутри области действий persistence context. Persistence контекстом в JPA является EntityManager, который использует внутри класс Session ORM-фреймворка Hibernate (при использовании Hibernate как persistence провайдера). Persistence контекст это объект-синхронайзер, который отслеживает состояния ограниченного набора Java объектов и синхронизирует изменения состояний этих объектов с состоянием соответствующих записей в БД. @@ -763,17 +1290,82 @@ public class JpaConfig { ## Spring Security Spring Security предоставляет широкие возможности для защиты приложения. Кроме стандартных настроек для аутентификации, авторизации и распределения ролей и маппинга доступных страниц, ссылок и т.п., предоставляет защиту от различных вариантов атак -+ SecurityContextHolder, в нем содержится информация о текущем контексте безопасности приложения, который включает в себя подробную информацию о пользователе(Principal) работающем в настоящее время с приложением. По умолчанию SecurityContextHolder используетThreadLocal для хранения такой информации, что означает, что контекст безопасности всегда доступен для методов исполняющихся в том же самом потоке. Для того что бы изменить стратегию хранения этой информации можно воспользоваться статическим методом класса SecurityContextHolder.setStrategyName(String strategy). Более подробно SecurityContextHolder -+ SecurityContext, содержит объект Authentication и в случае необходимости информацию системы безопасности, связанную с запросом от пользователя. -+ Authentication представляет пользователя (Principal) с точки зрения Spring Security. -+ GrantedAuthority отражает разрешения выданные пользователю в масштабе всего приложения, такие разрешения (как правило называются «роли»), например ROLE_ANONYMOUS, ROLE_USER, ROLE_ADMIN. -+ UserDetails предоставляет необходимую информацию для построения объекта Authentication из DAO объектов приложения или других источников данных системы безопасности. Объект UserDetailsсодержит имя пользователя, пароль, флаги: isAccountNonExpired, isAccountNonLocked, isCredentialsNonExpired, isEnabled и Collection — прав (ролей) пользователя. -+ UserDetailsService, используется чтобы создать UserDetails объект путем реализации единственного метода этого интерфейса +Spring Security - это список фильтров в виде класса FilterChainProxy, интегрированного в контейнер сервлетов, и в котором есть поле List. Каждый фильтр реализует какой-то механизм безопасности. Важна последовательность фильтров в цепочке. + +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring8.png) + +Когда мы добавляем аннотацию @EnableWebSecurity добавляется DelegatingFilterProxy, его задача заключается в том, чтобы вызвать цепочку фильтров (FilterChainProxy) из Spring Security. + +В Java-based конфигурации цепочка фильтров создается неявно. + +Если мы хотим настроить свою цепочку фильтров, мы можем сделать это, создав класс, конфигурирующий наше Spring Security приложение, и имплементировав интерфейс WebSecurityConfigurerAdapter. В данном классе, мы можем переопределить метод: +```java +@Override +protected void configure(HttpSecurity http) throws Exception { + http + .csrf().disable() + .authorizeRequests(); +} +``` + +Именно этот метод конфигурирует цепочку фильтров Spring Security и логика, указанная в этом методе, настроит цепочку фильтров. + +__Основные классы и интерфейсы__ + +__SecurityContext__ - интерфейс, отражающий контекст безопасности для текущего потока. Является контейнером для объекта типа Authentication. (Аналог - ApplicationContext, в котором лежат бины). + +По умолчанию на каждый поток создается один SecurityContext. SecurityContext-ы хранятся в SecurityContextHolder. + +Имеет только два метода: getAuthentication() и setAuthentication(Authentication authentication). +__SecurityContextHolder__ - это место, где Spring Security хранит информацию о том, кто аутентифицирован. Класс, хранящий в ThreadLocal SecurityContext-ы для каждого потока, и содержащий статические методы для работы с SecurityContext-ами, а через них с текущим объектом Authentication, привязанным к нашему веб-запросу. +![Image alt](https://github.com/Shell26/Java-Developer/raw/master/img/Spring9.png) + +__Authentication__ - объект, отражающий информацию о текущем пользователе и его привилегиях. Вся работа Spring Security будет заключаться в том, что различные фильтры и обработчики будут брать и класть объект Authentication для каждого посетителя. Кстати объект Authentication можно достать в Spring MVC контроллере командой SecurityContextHolder.getContext().getAuthentication(). Authentication имеет реализацию по умолчанию - класс UsernamePasswordAuthenticationToken, предназначенный для хранения логина, пароля и коллекции Authorities. +__Principal__ - интерфейс из пакета java.security, отражающий учетную запись пользователя. В терминах логин-пароль это логин. В интерфейсе Authentication есть метод getPrincipal(), возвращающий Object. При аутентификации с использованием имени пользователя/пароля Principal реализуется объектом типа UserDetails. +__Credentials__ - любой Object; то, что подтверждает учетную запись пользователя, как правило пароль (отпечатки пальцев, пин - всё это Credentials, а владелец отпечатков и пина - Principal). +__GrantedAuthority__ - полномочия, предоставленные пользователю, например, роли или уровни доступа. +__UserDetails__ - интерфейс, представляющий учетную запись пользователя. Как правило модель нашего пользователя должна имплементировать его. Она просто хранит пользовательскую информацию в виде логина, пароля и флагов isAccountNonExpired, isAccountNonLocked, isCredentialsNonExpired, isEnabled, а также коллекции прав (ролей)пользователя. Данная информация позже инкапсулируется в объекты Authentication. +__UserDetailsService__ - интерфейс объекта, реализующего загрузку пользовательских данных из хранилища. Созданный нами объект с этим интерфейсом должен обращаться к БД и получать оттуда юзеров. используется чтобы создать UserDetails объект путем реализации единственного метода этого интерфейса ```java UserDetails loadUserByUsername(String username) throws UsernameNotFoundException; ``` -Позволяет получить из источника данных объект пользователя и сформировать из него объект UserDetails который будет использоваться контекстом Spring Security. +__AuthenticationManager__ - основной стратегический интерфейс для аутентификации. Имеет только один метод, который срабатывает, когда пользователь пытается аутентифицироваться в системе: +```java +public interface AuthenticationManager { +Authentication authenticate(Authentication authentication) +throws AuthenticationException; +} +``` +AuthenticationManager может сделать одну из 3 вещей в своем методе authenticate(): + +1. вернуть Authentication (с authenticated=true), если предполагается, что вход осуществляет корректный пользователь. +2. бросить AuthenticationException, если предполагается, что вход осуществляет некорректный пользователь. +3. вернуть null, если принять решение не представляется возможным. + +Наиболее часто используемая реализация AuthenticationManager - родной класс ProviderManager, который содержит поле private Listproviders со списком AuthenticationProvider-ов и итерирует запрос аутентификации по этому списку AuthenticationProvider-ов. Идея такого разделения - поддержка различных механизмов аутентификации на сайтах. + +AuthenticationProvider - интерфейс объекта, выполняющего аутентификацию. Имеет массу готовых реализаций. Также можем задать свой тип аутентификации. Как правило в небольших проектах одна логика аутентификации - по логину и паролю. В проектах побольше логик может быть несколько: Google-аутентификация и т.д., и для каждой из них создается свой объект AuthenticationProvider. + +AuthenticationProvider немного похож на AuthenticationManager, но у него есть дополнительный метод, позволяющий вызывающей стороне спрашивать, поддерживает ли он переданный ему объект Authentication, возможно этот AuthenticationProvider может поддерживать только аутентификацию по логину и паролю, но не поддерживать Googleаутентификацию: +```java +boolean supports(java.lang.Class authentication) +``` +PasswordEncoder - интерфейс для шифрования/расшифровывания паролей. Одна из популярных реализаций - BCryptPasswordEncoder. + +В случае, если нам необходимо добавить логику при успешной/неудачной аутентификации, мы можем создать класс и имплементировать интерфейсы AuthenticationSuccessHandler и AuthenticationFailureHandler соответственно, переопределив их методы. + +Как это работает с формой логина и UserDetailsService: + - Пользователь вводит в форму и отправляет логин и пароль. + - UsernamePasswordAuthenticationFilter создает объект Authentication - UsernamePasswordAuthenticationToken, где в качестве Principal - логин, а в качестве Credentials - пароль. + - Затем UsernamePasswordAuthenticationToken передаёт объект Authentication с логином и паролем AuthenticationManager-у. + - AuthenticationManager в виде конкретного класса ProviderManager внутри своего списка объектов AuthenticationProvider, имеющих разные логики аутентификации, пытается аутентифицировать посетителя, вызывая его метод authenticate(). У каждого AuthenticationProvider-а: + 1 Метод authenticate() принимает в качестве аргумента незаполненный объект Authentication, например только с логином и паролем, полученными в форме логина на сайте. Затем с помощью UserDetailsService метод идёт в БД и ищет такого пользователя. + 2 Если такой пользователь есть в БД, AuthenticationProvider получает его из базы в виде объекта UserDetails. Объект Authentication заполняется данными из UserDetails - в него включаются Authorities, а в Principal записывается сам объект UserDetails, содержащий пользователя. + 3 Затем этот метод возвращает заполненный объект Authentication (прошли аутентификацию). Вызывается AuthenticationSuccessHandler. + 4 Если логин либо пароль неверные, то выбрасывается исключение. Вызывается AuthenticationFailureHandler. + - Затем этот объект Authentication передается в AccessDecisionManager и получаем решение на получение доступа к запрашиваемой странице (проходим авторизацию). + Аннотации: + @EnableGlobalMethodSecurity - включает глобальный метод безопасности. + @EnableWebMvcSecurity - "включает" Spring Security. Не будет работать, если наш класс не наследует WebSecurityConfigurerAdapter @@ -814,6 +1406,23 @@ Starter-пакеты представляют собой набор удобны Можно отказаться от использования механизма автоконфигурации, вместо этого указывая необходимые автоконфигурации вручную. Для этого надо избавиться от аннотаций @SpringBootApplication и @EnableAutoConfiguration в коде вашего проекта, а для указания нужных конфигурационных классов использовать аннотации @SpringBootConfiguration и @ImportAutoConfiguration. Однако стоит помнить, что используемые автоконфигурации всё ещё могут содержать неиспользуемые компоненты. +__Как происходит автоконфигурация в Spring Boot:__ + +1. Отмечаем main класс аннотацией @SpringBootApplication (аннотация инкапсулирует в себе:@SpringBootConfiguration, @ComponentScan, @EnableAutoConfiguration), таким образом наличие @SpringBootApplication включает сканирование компонентов, автоконфигурацию и показывает разным компонентам Spring (например, интеграционным тестам), что это Spring Boot приложение. +```java +@SpringBootApplication + public class DemoApplication { + public static void main(String[] args) { + SpringApplication.run(DemoApplication.class, args); + } + } +``` +2. @EnableAutoConfiguration импортирует класс EnableAutoConfigurationImportSelector. Этот класс не объявляет бины сам, а использует так называемые фабрики. +3. Класс EnableAutoConfigurationImportSelector смотрит в файл META-INF/spring.factories и загружает оттуда список значений, которые являются именами классов (авто)конфигураций, которые Spring Boot импортирует. Т.е. аннотация @EnableAutoConfiguration просто импортирует ВСЕ (более 150) перечисленные в spring.factories конфигурации, чтобы предоставить нужные бины в контекст приложения. +4. Каждая из этих конфигураций пытается сконфигурировать различные аспекты приложения(web, JPA, AMQP и т.д.), регистрируя нужные бины. Логика при регистрации бинов управляется набором @ConditionalOn* аннотаций. Можно указать, чтобы бин создавался при наличии класса в classpath (@ConditionalOnClass), наличии существующего бина (@ConditionalOnBean), отсуствии бина (@ConditionalOnMissingBean) и т.п. Таким образом наличие конфигурации не значит, что бин будет создан, и в большинстве случаев конфигурация ничего делать и создавать не будет. +5. Созданный в итоге AnnotationConfigEmbeddedWebApplicationContext ищет в том же DI контейнере фабрику для запуска embedded servlet container. +6. Servlet container запускается, приложение готово к работе! + [к оглавлению](#spring) ## Starter packs diff --git a/toResolve.md b/toResolve.md new file mode 100644 index 0000000..dafdaa4 --- /dev/null +++ b/toResolve.md @@ -0,0 +1,118 @@ +[Вопросы для собеседования](README.md) + +# Вопросы для разбора + +1. Java Core: С какой проблемой можно столкнуться при увеличении размера heap памяти. Почему программисты стараются излишне не расширять ее +2. Java Core: Ускоряет ли вычисление программы использование parallelStream() в Stream ? В каких случаях да, а в каких нет +3. Java Core: какой размер у String Pool? +4. Java Core: какой GC используется по дефолту в Java 8/11 +5. Java Core: что такое Stop The World +6. Java Core: чем лямбда отличается от анонимного класса +7. Java Core: какие методы можно вызвать у Throwable +8. Java Core: назови классы, которые наследуются не от Object +9. Maven: как передавать стартовые параметры через мавен +10. Java Core: назовите Immutable коллекции +11. Java Core: как сделать иммутабельным класс, у которого в полях находятся ссылочные неиммутабельные типы (final не поможет, потому что финализируется ссылка, а не объект, и сам объект можно будет изменить) +12. Java Core: Максимальное кол-во элементов в массиве? Максимальный размер ArrayList? Максимальный размер LinkedList? Почему в LinkedList лучше не использовать size() при итеррировании, и как лучше итеррироваться? +13. SQL: что такое Explain и чем Explain отличается от Explain Analyse +14. Spring: @Value отрабатывает до вызова конструктора или после? +15. Spring: На каком этапе происходит внедрение зависимостей при использовании @Autowired над конструктором? +16. Spring: как выбрать профиль +17. Spring: Какой из трех способов автовайринга рекомендуется использовать разработчиками спринга и по каким причинам. +18. Spring: можно ли создать два бина со скоупом сингтон одного класса +19. Spring: мы создали контроллер и инжектим бин со скоупом Session. Как спринг будет подставлять каждой сессии новый бин, если контекс инициализируется со стартом приложения +20. Spring: какие исключения может обрабатывать @Transactional по дефолту. Может ли он обработать пробрасываемое исключение + +# Микросервисная архитектура. Основные принципы. Отличия от Монолита и SOA +1. 12-ти факторная модель создания облачных приложений +2. IaaS, PaaS, SaaS https://gigacloud.ua/ru/blog/navchannja/hmarna-piramida-iaas-paas-i-saas +3. CAP теорема +4. Паттерны микросервисной архитектуры. https://mcs.mail.ru/blog/26-osnovnyh-patternov-mikroservisnoj-razrabotki +5. Паттерны интеграции микросервисов https://www.enterpriseintegrationpatterns.com/patterns/messaging/ + +# Apache Kafka +https://www.youtube.com/watch?v=-AZOi3kP9Js Основы кафки + +https://www.youtube.com/watch?v=c_mkpVg5rlg Обзор брокеров сообщений + +https://www.youtube.com/watch?v=Y1eSeEJDses вебинар по обзору некоторых фишек в кафке + +1. Что такое очередь сообщений. +2. Основные концепции очередей +3. ? Kafka vs Rabbit MQ +4. Основные сущности Kafka + +# Kafka Cluster. Zookeaper +5. Zookeper. Хранение метаданных кластера +6. Kafka кластер. Устройство +7. Партиционирование. Leader партиция. +8. Репликация +9. Настройка Kafka кластера для корректной работы партиционирования и репликации +10. Устройство файлового хранилища Kafka +11. TTL + +# Producer +13. Producer. Из каких шагов состоит инцициализация +14. Стратегии коммитинга. Гарантия доставки +15. Сериализация, Десериализация +16. Стратегии выбора партиции продюссером +17. Можно ли из топика (распределен по 3 партициям) прочитать сообщения в том же порядке, в котором они были записаны? Почему? +18. Как сделать так, чтобы все сообщения по одному клиенту попали в одну партицию? +19. Timestamp +20. Headers +21. Batch size. Linger time +22. Retry +# Consumer + +# Docker, Kubernetes, OpenShift. +1. Контейнеризация + +# Реактивщина +https://projectreactor.io/docs/core/release/api/ - список классов и методов по Project Reator + +https://habr.com/ru/post/565000/ - основной источник материалов + +1. Реактивное программирование. Основные принципы. Преимущество реактивного программирование над блокирующим. +2. Перечислите основные виды потерь на блокирующем типе программирования в web +3. Объясните понятие backpressure. Что оно дает. https://habr.com/ru/post/512724/ +4. При каком количестве запросов и нагрузке имеет реактивно построенное приложение начинает выигровать у приложения с блокироющим типом запросов. Приведите оценки +5. Основные библиотеки + +# Rector Core +1. Cтандарт спецификации Reactive Streams. +2. Reactive Streams: Основные интефейсы +3. Reactive Streams: Publisher +4. Reactive Streams: Subscriber +5. Основные модули Project Reactor +6. Reactor Core: основные интерфейсы +7. Flux Api. Основные методы https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Flux.html +8. Mono Api. Основные методы +9. Обработка ошибок +10. Тестирование +11. Параллельное выполнение. Scheduler +12. Backpressure: оператор request +13. Горячий и холодный паблишер. Что это. Как создать +14. Контекст: локальные переменные для контекста +15. Sinks. События +16. Отладка/Debug реактивной программы + +# Spring WebFlux (Расширить) +22. Реактивные Application Servers: Netty, Jetty, Tomcat, Servlet 3.1, HTTP 2.0 +23. Spring WebFlux. Для чего используется +24. Обработка запроса. Аннотационная модель: контроллеры на базе WebFlux +25. Обработка запроса. Функциональная модель: HandlerFunctions, RouterFunctions, +26. Spring Security WebFlux. +27. Отправка запросов. WebClient. Основные методы +28. WebSocket, RSocket +29. Тестирование WebFlux + +# R2DBC (Расширить) +30. Что такое R2DBC, Программы, реализующие драверы для R2DBC +31. SPRING DATA R2DBC +32. ReactiveCrudRepository +33. DatabaseClient. Оптравка SQL запросов напрямую в БД +34. Транзакции + +[к оглавлению](#Вопросы-для-разбора) + +[Вопросы для собеседования](README.md)