Где следует хранить аннотацию @Service? Интерфейс или реализация?

133

Я разрабатываю приложение с использованием Spring. Я должен использовать @Serviceаннотацию. У меня есть ServiceIи ServiceImplтакой то ServiceImpl implements ServiceI. Я не понимаю, где мне хранить @Serviceаннотацию.

Должен ли я аннотировать интерфейс или реализацию @Service? В чем разница между этими двумя подходами?

TheKojuEffect
источник
Это мой ответ на похожий пост: stackoverflow.com/questions/38618995/…
Agustí Sánchez

Ответы:

142

Я никогда не помещаю @Component(или @Service...) в интерфейс, потому что это делает интерфейс бесполезным. Позвольте мне объяснить почему.

Утверждение 1: Если у вас есть интерфейс, вы хотите использовать этот интерфейс для типа точки впрыска.

Утверждение 2: Назначение интерфейса состоит в том, что он определяет контракт, который может быть реализован несколькими реализациями. С другой стороны у вас есть точка инъекции ( @Autowired). Имея только один интерфейс и только один класс, реализующий его, (IMHO) бесполезно и нарушает YAGNI .

факт: Когда вы ставите:

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

тогда вы получите и NoUniqueBeanDefinitionException (или у вас есть особые настройки конфигурации со средой, профилями или квалификаторами ...)

Вывод: если вы используете @Component(или @Service, ...) в интерфейсе, вы должны нарушить хотя бы одно из двух правил. Поэтому я думаю, что это бесполезно (за исключением некоторых редких сценариев) устанавливать @Componentна уровне интерфейса.


Интерфейсы Spring-Data-JPA Repository - это нечто совершенно иное

Ральф
источник
3
Очень интересно то, что ты пишешь ... Так какой же правильный путь? Разве это не аннотирование интерфейса вообще и не предоставление аннотации Service к реализациям? Может ли Spring по-прежнему автоматически подключаться с использованием типа интерфейса? Как вы на это ответите> stackoverflow.com/questions/12899372/…
corlaez
3
У меня вопрос, зачем нам создавать интерфейс для уровня сервиса, когда его реализует только один класс? Я видел много проектов, у них есть уровень контроллера, ** уровень сервиса ( servicInterface , serviceInterfaceImpl ) и уровень репозитория .
Юбарадж 08
4
@Yubaraj: справедливо, но ваш вопрос касается совсем другой темы (в своем ответе я исходил из предположения: существует интерфейс, и вопрос не в том, чтобы иметь интерфейс, а в том, где разместить аннотацию). На ваш вопрос: почти нет причин иметь интерфейс для класса бизнес-обслуживания, который никогда не будет иметь двух реализаций. (Кстати: пока вы не создаете api для кого-то еще, вы всегда можете провести рефакторинг кода и представить интерфейс позже, когда он вам понадобится)
Ральф
1
@Yubaraj, интерфейсы позволяют делать легкие прокси jdk на основе интерфейса для bean-компонентов, когда это необходимо. Когда интерфейса нет, Spring должен создавать подклассы или изменять bean-компоненты с помощью cglib для создания прокси. @Transactionalэто один из примеров использования прокси для bean-компонента. АОП - еще один.
Yoory N.
1
Хотя иногда у вас не будет более одного реализующего класса, я все же предпочитаю объявлять свои методы в интерфейсе. Другому разработчику будет легче проверить, какие службы доступны, только просмотрев декларации и документацию, не беспокоясь о реализации.
HFSDev
32

В основном аннотации, такие как @Service , @Repository , @Component и т.д., все служат одной цели:

автоматическое определение при использовании конфигурации на основе аннотаций и сканирования пути к классам.

Из моего опыта я всегда использовать @Serviceаннотацию на интерфейсы или абстрактные классы и аннотации , как @Componentи @Repositoryдля их реализации. @Componentаннотацию, которую я использую для тех классов, которые служат базовым целям, простые компоненты Spring, не более того. @Repositoryаннотацию, которую я использую в DAOслое, например, если мне нужно связаться с базой данных, выполнить некоторые транзакции и т. д.

Поэтому я предлагаю аннотировать ваш интерфейс этим @Serviceи другими слоями в зависимости от функциональности.

Паулюс Матулионис
источник
10
Можете ли вы сказать, в чем разница между аннотирующими интерфейсами и аннотирующими реализациями?
TheKojuEffect
28
Из документации Spring : «Эта аннотация служит специализацией @Component, позволяя автоматически определять классы реализации посредством сканирования пути к классам», предполагая, что она предназначена для использования в классе реализации.
nbrooks 02
1
@TheKojuEffect, этот пост подробно объясняет разницу между аннотирующими интерфейсами и реализациями - stackoverflow.com/questions/3120143/…
Mahesh
@ user3257644 Просто обратите внимание, что предложения, содержащиеся в ответах в этом сообщении, относятся конкретно к аннотации «@Transactional», а не ко всем аннотациям в целом.
Джонатан
3
Аннотации @service на интерфейсах не действуют, как и другие аннотации стереотипов. Все стереотипные аннотации следует размещать либо на абстрактных, либо на конкретных классах.
йети
13

Я @Component, @Service, @Controllerи @Repositoryаннотации только на классы реализации , а не на интерфейсе. Но @Autowiredаннотация с интерфейсами у меня все еще работала.

yalkris
источник
7

Плюсы размещения аннотации в @Service в том, что она дает намек на то, что это служба. Я не знаю, унаследует ли какой-либо реализующий класс по умолчанию эту аннотацию.

Минус в том, что вы связываете свой интерфейс с определенной структурой, например Spring, с помощью специальной аннотации Spring. Поскольку интерфейсы должны быть отделены от реализации, я бы не предлагал использовать какие-либо аннотации для конкретной платформы или объектную часть вашего интерфейса.

Кулдип Тивари
источник
1
Я думаю, мы все слышали аргумент о сильной связи несколько раз, но помните, что аннотации могут присутствовать без jar, так что в основном, пока ваше соединение находится в аннотациях, оно все еще может быть разъединено.
Niels Bech Nielsen
1

Одним из преимуществ Spring является простота переключения реализации службы (или другой). Для этого вам нужно аннотировать интерфейс и объявить переменную следующим образом:

@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;

Таким образом, ваш интерфейс не привязан к Spring framework, и вы можете изменить класс в любое время, и вам не придется обновлять все свои точки внедрения.

Поэтому, если бы я хотел изменить класс реализации, я мог бы просто аннотировать новый класс и удалить его из первого, но это все, что требуется изменить. Если вы внедрите класс, у вас будет много работы, когда вы захотите изменить класс impl.

user1239403
источник
-1

Проще говоря:

@Service - это стереотипная аннотация для уровня сервиса .

@Repository является стереотипными аннотациями для сохранения состояния слоя.

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

HughParker
источник
3
Ищется не смысл этих аннотаций, а ГДЕ их разместить в интерфейсе или его реализации.
nanosoft
-3

Есть 5 аннотаций, которые можно использовать для создания весенних бобов. Перечислите ответы ниже.

Вам действительно нужен интерфейс? Если вы собираетесь иметь одну реализацию для каждого интерфейса службы, просто избегайте этого, используйте только класс. Конечно, если у вас нет RMI или когда требуется интерфейсный прокси.

@Repository - используйте для внедрения классов вашего слоя dao.

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

@Controller - используйте для контроллеров уровня внешнего интерфейса, таких как управляемые компоненты JSF, внедряемые как компоненты Spring.

@RestController - используйте для контроллеров пружинного отдыха, это поможет вам избежать каждый раз добавлять аннотации @ResponseBody и @RequestBody в ваши методы отдыха.

@Component - используйте его в любом другом случае, когда вам нужно ввести Spring bean, который не является контроллером, службой или классом dao

Артур Ёлчян
источник
Да, вам нужен интерфейс на границах ваших слоев (например, уровень доступа к данным и уровень сервиса). Они обеспечивают слабое связывание модулей, содержащих реализации этих уровней. Без них клиенты упомянутых слоев должны знать конкретные типы, и вам нужно изменить их, когда вы, скажем, хотите украсить свой BasicDao с помощью CachingDao ...
Иганд