Де слід зберігати примітку @Service? Інтерфейс чи реалізація?


133

Я розробляю додаток за допомогою Spring. Мені потрібно використовувати @Serviceанотацію. У мене є ServiceIі ServiceImplтаке, що ServiceImpl implements ServiceI. Я тут розгублений, де мені слід зберігати @Serviceанотацію.

Чи слід коментувати інтерфейс чи реалізацію @Service? Які відмінності між цими двома підходами?


Це моя відповідь на подібний допис: stackoverflow.com/questions/38618995/…
Agustí Sánchez

Відповіді:


140

Я ніколи не ставлю @Component(або @Service, ...) інтерфейс, тому що це робить інтерфейс марним. Дозвольте мені пояснити, чому.

п. 1: Якщо у вас є інтерфейс, ви хочете використовувати цей інтерфейс для типу точки введення.

п. 2: Метою інтерфейсу є те, що він визначає контракт, який може бути реалізований декількома реалізаціями. З іншого боку, у вас є точка введення ( @Autowired). Маючи лише один інтерфейс і лише один клас, який його реалізує, є (IMHO) марним і порушує YAGNI .

факт: Коли ви ставите:

  • @Component(або @Service, ...) в інтерфейсі,
  • мати кілька класів, які реалізують його,
  • принаймні два класи стають весняними бобами, і
  • мати точку введення, яка використовує інтерфейс для введення на основі типу,

тоді ви отримаєте і NoUniqueBeanDefinitionException (або у вас є спеціальна конфігурація налаштування, із середовищем, профілями або кваліфікаторами ...)

Висновок: Якщо ви використовуєте @Component(або @Service, ...) в інтерфейсі, ви повинні порушити принаймні один із двох записів. Тому я вважаю, що не корисно (за винятком деяких рідкісних сценаріїв) ставити @Componentна рівні інтерфейсу.


Інтерфейси сховища Spring-Data-JPA - це щось інше


3
Дуже цікаво, що ви пишете ... так що це вірний шлях? Чи це взагалі не анотування інтерфейсу та надання анотації Служби реалізаціям? Чи весна Spring все ще здатна до автоматичного з'єднання, використовуючи тип інтерфейсу? Що ваша відповідь на це> stackoverflow.com/questions/12899372 / ...
corlaez

3
У мене виникає питання, чому нам потрібно створити інтерфейс для сервісного рівня, коли його реалізує лише один клас? Я бачив багато проектів, у них рівень контролера, ** сервісний рівень ( servicInterface , serviceInterfaceImpl ) та рівень сховища .
Yubaraj

4
@Yubaraj: Справедливий момент, але ваше питання стосується зовсім іншої теми (для моєї відповіді я взяв припущення: що є інтерфейс, а питання не про те, щоб мати інтерфейс, а про те, де розмістити анотацію). Ваше запитання: майже немає причин мати інтерфейс для класу бізнес-послуг, який ніколи не матиме двох реалізацій. (BTW: Поки ви не будуєте api для когось іншого, ви завжди можете рефакторний код і ввести інтерфейс пізніше, коли вам це потрібно)
Ральф

1
@ Yubaraj, інтерфейси дозволяють робити легкі проксі-файли jdk на основі інтерфейсу, коли це потрібно. Коли немає інтерфейсу, навесні доводиться робити підкласифікацію або змінювати боби, використовуючи cglib для створення проксі. @Transactionalє одним із прикладів, коли використовується проксі-сервер до квасолі. AOP - ще один.
Yoory N.

1
Незважаючи на те, що іноді у вас не буде більше ніж один клас реалізації, я все ж вважаю за краще оголошувати свої методи в інтерфейсі. Співробітнику-розробнику буде легше перевірити, які послуги доступні, лише переглянувши декларації та документацію, не турбуючись про реалізацію.
HFSDev

32

В основному примітки, такі як @Service , @Repository , @Component тощо, усі вони відповідають одній цілі:

автоматичне виявлення при використанні конфігурації на основі анотацій та сканування на класовому шляху.

Зі свого досвіду я завжди використовую @Serviceанотацію на інтерфейсах або абстрактних класах та анотаціях, як @Componentі @Repositoryдля їх реалізації.@Componentанотація, яку я використовую на тих заняттях, які служать основним цілям, прості весняні боби, нічого більше. @Repositoryанотація, яку я використовую в DAOшарі, наприклад, якщо мені доведеться спілкуватися з базою даних, мати якісь транзакції тощо.

Тому я б запропонував анотувати ваш інтерфейс з @Serviceта іншими шарами залежно від функціональності.


10
Чи можете ви сказати, які відмінності між інтерфейсами конотування та реалізацією коментарів?
TheKojuEffect

27
З Spring Docs , «Це анотацію служить спеціалізації @Component, дозволяючи класи реалізації , щоб бути автоматично через сканування шляху до класів," припускаючи , що вона призначена для використання в класі реалізації.
nbrooks

1
@TheKojuEffect, Цей пост детально пояснює різницю між інтерпретуючими інтерфейсами та реалізаціями - stackoverflow.com/questions/3120143/…
Mahesh

@ user3257644 Зауважте лише, що пропозиції, надані відповідями в цій публікації, стосуються конкретно примітки "@Transactional", а не всіх анотацій загалом.
Джонатан

3
Анотація @service на інтерфейсах не впливає, як і інші стереотипні анотації. Усі стереотипні анотації слід ставити або на абстрактні, або на конкретні класи.
велика нога

13

Я @Component, @Service, @Controllerі @Repositoryанотації тільки на класи реалізації , а не на інтерфейсі. Але @Autowiredанотація з інтерфейсами все ж працювала для мене.


7

Плюси розміщення анотації на @Service полягають у тому, що це дає підказку, що це служба. Я не знаю, чи якийсь реалізаційний клас успадкує цю примітку за замовчуванням.

З іншого боку, ви з'єднуєте свій інтерфейс із конкретною рамкою, тобто Spring, використовуючи специфічну анотацію для весни. Оскільки інтерфейси повинні бути відірвані від реалізації, я б не пропонував використовувати будь-які рамкові конкретні анотації або об’єктну частину вашого інтерфейсу.


1
Я думаю, що всі ми чули сильний аргумент сполучення неодноразово, але пам’ятайте, що анотації можуть бути присутніми без баночки, так що в основному, якщо ваша з’єднання знаходиться в анотаціях, її все одно можна роз’єднати.
Niels Bech Nielsen

1

Однією з переваг весни є легке переключення сервісної (чи іншої) реалізації. Для цього вам потрібно зробити анотацію на інтерфейсі та оголосити змінну, як це:

@Autowired
private MyInterface myVariable;

і ні :

@Autowired
private MyClassImplementationWhichImplementsMyInterface myVariable;

Як і в першому випадку, ви можете активувати, яку реалізацію слід вводити з моменту, коли вона унікальна (лише один клас реалізує інтерфейс). У другому випадку потрібно перефактурувати весь код (нова реалізація класу має іншу назву). Як наслідок, анотація повинна бути якомога більше в інтерфейсі. Крім того, для цього добре підходять проксі-сервери JDK: вони створюються та інстанціюються при запуску програми, оскільки тип виконання заздалегідь відомий, всупереч проксі-серверам CGlib.


4
"MyClassImplementationWhichImplementsMyInterface" LOL
inafalcao

Вам не потрібно коментувати інтерфейс, щоб перший приклад працював. Ви можете коментувати @Serviceреалізацію та автопроводити інтерфейс. Spring перевірить наявність будь-якого об'єкта, що реалізує цей інтерфейс.
Марко

1

Я б поставив @Serviceу ваш клас, але ставлю назву інтерфейсу як параметр до анотації, наприклад

interface ServiceOne {}

@Service("ServiceOne")
class ServiceOneImpl implements ServiceOne{}

Тим самим ви отримуєте всі переваги і все ще можете вводити інтерфейс, але отримуєте клас

@Autowired 
private ServiceOne serviceOne;

Таким чином, ваш інтерфейс не прив’язаний до весняної основи, і ви можете змінити клас у будь-який час і не потрібно оновлювати всі точки введення.

Отже, якщо я хотів змінити клас реалізації, я міг би просто анотувати новий клас і видалити його з першого, але це все, що потрібно змінити. Якщо ви вводите клас, у вас може бути багато роботи, коли ви хочете змінити клас impl.


-1

Простіше кажучи:

@Service - це стереотипна примітка до сервісного рівня.

@Repository є стереотипними анотаціями для збереження стану шару.

@Component - це загальна анотація стереотипу, яка використовується для того, щоб повідомити Spring для створення примірника об'єкта в контексті програми. Можна визначити будь-яке ім'я для екземпляра, за замовчуванням - назва класу як регістр верблюда.


3
Значення цих анотацій не шукається, але де їх розмістити на інтерфейсі чи його реалізації.
nanosoft

-3

Є 5 анотацій, які можна використовувати для виготовлення ярої квасолі. Перелічі нижче відповіді.

Вам справді потрібен інтерфейс? Якщо у вас буде одна реалізація для кожного сервісного інтерфейсу, просто уникайте цього, використовуйте лише клас. Звичайно, якщо у вас немає RMI або коли потрібен проксі-інтерфейс.

@Repository - використовувати для введення класів шару дао.

@Service - використовувати для введення класів службового рівня. На рівні обслуговування також вам може знадобитися використання анотації @Transactional для управління транзакціями db.

@Controller - використовуйте для контролерів шару шару, такі як квасоля, що керується JSF, як ін'єкційна квасоля.

@RestController - використовуйте для контролерів весняного відпочинку, це допоможе вам уникати кожного разу, щоб у ваші способи відпочинку вносити анотації @ResponseBody та @RequestBody.

@Component - використовуйте його в будь-якому іншому випадку, коли вам потрібно вводити пружинний боб, який не є контролером, службою чи класом дао


Так, вам потрібен інтерфейс на межах ваших шарів (наприклад, рівень доступу до даних та рівень обслуговування). Вони дозволяють вільно з'єднати модулі, які містять реалізацію цих шарів. Без них клієнти згаданих шарів повинні знати конкретні типи, і вам потрібно їх змінити, коли ви, скажімо, хочете прикрасити ваш BasicDao CachingDao ...
Igand
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.