JavaRush /Java блог /Random UA /Spring для лінивих. Основи, базові концепції та приклади ...
Стас Пасинков
26 рівень
Киев

Spring для лінивих. Основи, базові концепції та приклади з кодом. Частина 1

Стаття з групи Random UA
Багато людей після прочитання моїх статей про створення заготовки для веб-проекту і про створення простенького веб-сервісу на сервлетах - цікавабося коли я напишу і про Spring. Я не хотів, пропонував почитати книгу (і досі говорю, що книга — це краще ніж 10, а то й 100 статей в інтернеті). Але зараз ось вирішив, що пояснюючи одне й те саме різним людям, я витрачаю більше часу, ніж якби один раз сів і написав статтю, а потім просто кидав би посилання на неї. Так що пишу заради посилання)). Spring для лінивих.  Основи, базові концепції та приклади з кодом.  Частина 1 - 1У цій статті я не писатиму як за 5 хвабон зробити працюючий проект на спрингу на мій приклад. Я напишу лише про базові речі, без знання яких "запиляти" проект звичайно можна, але що там відбувається, і, що важливіше, чому — буде не зрозуміло.

Що таке Spring Framework?

Spring Framework , або просто Spring - один із найпопулярніших фреймворків для створення веб-додатків на Java. Фреймворк— це щось схоже на бібліотеку (можливо цей термін вам знайоміший), але є один момент. Грубо кажучи, використовуючи бібліотеку, ви просто створюєте об'єкти класів, які в ній є, викликаєте потрібні вам методи і таким чином отримуєте потрібний вам результат. Тобто, тут більш імперативний підхід: ви чітко вказуєте у своїй програмі, у який конкретний момент треба створити якийсь об'єкт, у який момент викликати конкретний метод, ітд. З фреймворками справи трохи інакше. Ви просто пишете якісь свої класи, прописуєте там якусь частину логіки, а створює об'єкти ваших класів та викликає методи за вас уже сам фреймворк. Найчастіше ваші класи імплементують якісь інтерфейси з фреймворку або успадковують якісь класи з нього, таким чином отримуючи частину вже написаної за вас функціональності. Але не обов'язково саме так. У спрингу наприклад намагаються по максимуму відійти від такої жорсткої зв'язності (коли ваші класи безпосередньо залежать від якихось класів/інтерфейсів з цього фреймворку), і використовують для цієї мети інструкції. Далі ми ще до цього моменту повернемось. Але важливо зрозуміти, щоспринг - це просто набір якихось класів та інтерфейсів, які вже написані за вас :) Ще хочу відразу відзначити, що спринг можна використовувати не тільки для веб-додатків, але і для так знайомих всім нам звичайних консольних програмок. І сьогодні ми щось таке навіть напишемо.

Структура

Але спринг — це не один якийсь конкретний фреймворк. Це скоріше загальна назви для цілого ряду невеликих фреймворків, кожен із яких виконує якусь свою роботу.
Spring для лінивих.  Основи, базові концепції та приклади з кодом.  Частина 1 - 2
Як видно, спринг має модульну структуру. Це дозволяє підключати тільки ті модулі, які нам потрібні для нашого додатка і не підключати ті, якими ми свідомо не користуватимемося. Наскільки мені відомо, саме цей підхід і допоміг спрингу обійти свого конкурента на той час (EJB) і захопити лідерство. Тому що програми, що використовують EJB, тягнули дуже багато залежностей за собою, та й взагалі виходабо повільні та неповороткі. На зображенні видно, що спринг фреймворк складається з кількох модулів:
  • data access;
  • web;
  • core;
  • та інших.
Сьогодні ми познайомимося з деякими концепціями основного модуля, такими як: біни, контекст та інші. Як можна було здогадатися, модуль data access містить у собі засоби для роботи з даними (в основному, з базами даних), web - для роботи в мережі (у тому числі і для створення веб-додатків, про які буде пізніше). Крім того, є ще так-звана ціла спринг-інфраструктура: безліч інших проектів, які не входять у сам фреймворк офіційно, але при цьому безшовно інтегруються у ваш проект на спрингу (наприклад, той же spring security для роботи з авторизацією користувачів на сайті , який, я сподіваюся, ми теж якось помацаємо).

Чому Spring у Java?

Ну крім того, що це модно-стильно-молодіжно, можу відразу сказати, що як тільки ви хоч трохи оволодієте - ви зрозумієте скільки всякої різної роботи вам тепер не доводиться робити, і скільки всього бере на себе Spring. Можна написати пару десятків рядків конфігів, написати кілька класів — і вийде працюючий проект. Але як тільки починаєш замислюватися скільки там всього знаходиться "під капотом", скільки роботи виконується, і скільки довелося б писати коду, якщо робити такий же проект на голих сервлетах або на сокетах і чистій Java - волосся стає дибки :) Є навіть такий вираз, як "магія" Spring. Це коли ти бачиш, що все працює, але ти приблизно прикидаєш скільки там всього має відбуватися, щоб усе працювало і як воно там все працює — то здається, що відбувається це все завдяки дійсно якійсь магії)) Простіше назвати це все магією, ніж спробувати пояснити як воно там все взаємопов'язане. :) Ну і другий аргумент "за" вивчення Spring - це те, що приблизно 90% вакансій на джуна (за моїми особистими спостереженнями) - потрібно або знання, або хоча б загальне уявлення про джентльменському наборі спрингу зdata, web-mvcі security:) Але сьогодні лише про основи.

DI/IoC

Якщо ви намагалися щось читати по спрингу, то перше з чим ви стикалися - це напевно ось ці ось літери: DI/IoC . Зараз я вам дуже рекомендую відволіктися від цієї статті і почитати ось цю статтю на хабрі ! IoC (Inversion of Control) – інверсія управління. Про це я вже мимохіть згадував, коли писав, що при використанні бібліотеки ви самі прописуєте у своєму коді якийсь метод якого об'єкта викликати, а у випадку з фреймворками — найчастіше вже фреймворк викликатиме в потрібний момент той код, який ви написали. Тобто тут уже не ви керуєте процесом виконання коду/програми, а фреймворк це робить за вас. Ви передали йому керування (інверсія керування). Під DI розуміють то Dependency Inversion(інверсію залежностей, тобто спроби не робити жорстких зв'язків між вашими модулями/класами, де один клас безпосередньо зав'язаний на інший), то Dependency Injection (використання залежностей, це коли об'єкти котиків створюєте не ви в main-і і потім передаєте їх у свої методи, а за вас їх створює спринг, а ви йому просто кажете щось на кшталт "хочу сюди отримати котика" і він вам його передає у ваш метод). Ми частіше зіштовхуватимемося в подальших статтях з другим.

Біни та контекст

Одне з ключових понять у спрингу - це бін . По суті це просто об'єктякогось класу. Припустимо, для нашої програми треба використовувати 3 об'єкти: котика, собачку та папужки. І у нас є купа класів з купою методів, де іноді нам потрібен для методу котик, а для іншого методу — собачка, а іноді у нас будуть методи, де потрібен котик та папужка (наприклад метод для годівлі котика, хе-хе), а у якихось методах всі три об'єкти знадобляться. Так, ми можемо в main-і спочатку створити ці три об'єкти, а потім їх передавати до наших класів, а вже зсередини класів — у потрібні нам методи... І так по всій програмі. А якщо ще й уявити, що періодично ми захочемо змінювати список параметрів, що приймаються, для наших методів (ну вирішабо переписати щось або додати функціональності) — то нам доведеться робити досить багато правок за кодом якщо треба буде щось поміняти. А тепер якщо уявити, що таких об'єктів у нас не 3, а 300? Як варіант, це зібрати всі наші такі об'єкти в один загальний список об'єктів (List<Object> ) і в усі методи передавати його, а зсередини методів вже діставати той чи інший об'єкт, який нам потрібен. Але що якщо уявити, що по ходу програми у нас до цього списку може додатись якийсь об'єкт, або (що гірше) віддалитися? Тоді у всіх методах, де ми дістаємо об'єкти зі списку за їх індексом, все може поламатися. Тоді ми вирішуємо зберігати не список, а карту, де ключем буде ім'я потрібного нам об'єкта, а значенням - сам об'єкт, і тоді ми зможемо з нього діставати потрібні нам об'єкти просто на їхнє ім'я: get("папуга" )і у відповідь отримали об'єкт папужки. Або, наприклад, ключ - це клас об'єкта, а значення - сам об'єкт, тоді ми зможемо вказати вже не ім'я об'єкта, а просто клас потрібного нам об'єкта, теж зручно. Або навіть написати якусь обгортку над картою, де зробити методи, щоб у якихось випадках діставати об'єкти на їхнє ім'я, а в інших випадках — на клас. Ось це і вийде у нас application context зі спрингу. Контекст – це набір бінів (об'єктів). Звертаючись до контексту — ми можемо отримати потрібний нам бін (об'єкт) на його ім'я, наприклад, або на його тип, або ще якось. Крім того, ми можемо попросити спринг самого сходити пошукати у своєму контексті потрібний нам бін і передати його в наш метод. Наприклад, якщо у нас був такий метод:
public void doSomething(Cat cat) {
    ...
}
нам спринг колись викликав цей метод — передавав у нього об'єкт нашого котика зі свого контексту. Тепер ми вирішуємо, що нашому методу крім котика потрібен ще й папужка. Використовуючи спринг – для нас немає нічого простішого! Ми просто пишемо:
public void doSomething(Cat cat, Parrot parrot) {
    ...
}
і спринг, коли викликатиме цей наш метод — сам зрозуміє, що сюди треба передати котика та папужку, сходить до себе в контекст, дістане ці два об'єкти і передасть їх у наш метод. Передавши спрингу кермо правління нашою програмою — ми також переклали на нього відповідальність за створення об'єктів і передачу їх у наші методи, які він викликатиме. Виникає питання: а як спринг знатиме які об'єкти (біни) створювати?

Способи конфігурації програми

Існує три основні способи конфігурації програми (тобто, вказівки спрингу які саме об'єкти нам потрібні для роботи):
  1. за допомогою xml файлів/конфігів;
  2. за допомогою java-конфігів;
  3. автоматична конфігурація.
Розробники спрингу вибудовують їх у такому порядку пріоритетності:
  • найбільш пріоритетний спосіб, якому варто віддавати перевагу – це автоматична конфігурація;
  • якщо за допомогою автоматичної конфігурації немає можливості правильно налаштувати всі можливі біни - використовувати джава-конфігурацію (створення об'єктів використовуючи джава-код);
  • Та й самий низькопріоритетний метод - це по-старому, використовуючи xml конфіги.
Крім того, спринг дозволяє комбінувати ці способи. Наприклад, все те, що може бути налаштоване автоматично – нехай спринг зробить сам, там де треба вказати якісь особливі параметри – зробити за допомогою джава-конфігів, і крім того, можна підключити якісь леґаси конфіги в xml форматі. Загалом досить гнучко це все можна зробити. Але все ж таки, якщо все можна зробити за допомогою автоматичного налаштування - використовуйте її. Я розглядатиму тільки автоматичне налаштування та джава-конфіги; xml конфіги і так майже в кожному прикладі по спрингу в інтернеті використовуються, та й зрозумівши як працює джава-конфігурація — не повинно виникнути проблем для того, щоб "прочитати" xml файл, який робить те ж саме. Автоматична конфігурація використовується тоді, коли потрібні для роботи об'єкти — це об'єкти написаних намикласів. Якщо для створення об'єкта нашого класу потрібна якась дуже специфічна логіка, або якщо ми не маємо можливості відзначити якийсь клас потрібною нам анотацією, яку б підхопила автоматична конфігурація — це можна зробити в джава-конфігах. У наступній частині ми створимо мавен-проект, підключимо до нього парочку центральних модулів спрингу та створимо наші перші біни.
Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ