@Service批注应保存在哪里?接口还是实现?


Answers:


140

我从不将@Component(或@Service...)放在接口上,因为这会使接口无用。让我解释一下原因。

声明1:如果您有一个接口,那么您想将该接口用于注入点类型。

权利要求2:接口的目的是它定义可以由多种实现方式实现的契约。在另一侧,您有注射点(@Autowired)。(IMHO)仅具有一个接口且只有一个实现该接口的类是无用的,并且违反了YAGNI

事实:当你放:

  • @Component(或@Service,...)在界面上,
  • 有多个实现它的类,
  • 至少有两个类成为Spring Bean,并且
  • 有一个使用该接口进行基于类型的注入的注入点,

然后您将获得和NoUniqueBeanDefinitionException (或者您具有非常特殊的配置设置,包括环境,配置文件或限定符...)

结论:如果在接口上使用@Component(或@Service,...),则必须违反两个规则中的至少一个。因此,我认为放在@Component接口级别上是没有用的(除了一些罕见的情况)。


Spring-Data-JPA存储库接口完全不同


3
您写的东西很有趣...那是正确的方法?它根本不对接口进行注释,而是对实现进行Service注释吗?Spring仍然能够使用接口类型自动装配吗?您对此的回答是什么> stackoverflow.com/questions/12899372/…–
corlaez

3
我有一个问题,当只有一个类实现时,为什么我们需要为服务层创建一个接口?我看过很多项目,它们有控制器层,**服务层servicInterfaceserviceInterfaceImpl)和存储库层
Yubaraj

4
@Yubaraj:很公平,但是您的问题是关于另一个主题的(对于我的回答,我假设:存在一个接口,问题不在于拥有接口,而在于在何处放置注释)。对于您的问题:几乎没有理由为永远不会有两个实现的业务服务类提供接口。(顺便说一句:只要您没有为其他人构建api,就可以随时重构代码并在以后需要时介绍接口)
Ralph

1
@Yubaraj,接口允​​许在需要时为bean提供基于轻量级接口的jdk代理。当没有接口时,spring必须使用cglib进行子类化或修改bean来创建代理。@Transactional是使用bean代理的示例之一。AOP是另一个。
Yoory N.

1
即使有时候您不会只有一个以上的实现类,但我仍然更喜欢在接口中声明我的方法。一位开发人员会发现,仅通过查看声明和文档即可更轻松地检查哪些服务可用,而不必担心实现。
HFSDev '19

32

基本上注解像@服务@Repository@Component等,它们都达到同样的目的:

使用基于注释的配置和类路径扫描时自动检测。

从我的经验我一直使用@Service的接口或抽象类和注释像注释@Component,并@Repository组织落实。@Component我在那些用于基本目的的类上使用的注解,简单的Spring bean,仅此而已。@Repository我在该DAO层中使用的注释,例如,如果我必须与数据库进行通信,进行一些事务等。

因此,我建议@Service根据功能在和其他层上注释您的界面。


10
您能说明注释接口和注释实现之间的区别吗?
TheKojuEffect

27
Spring文档开始“此注释用作@Component的一种特殊功能,允许通过类路径扫描自动检测实现类,”建议将其用于实现类。
nbrooks,2015年

1
@TheKojuEffect,这篇文章解释了VS实现标注接口之间差异的详细信息- stackoverflow.com/questions/3120143/...
马赫什

@ user3257644请注意,该帖子中的答案所提供的建议是针对“ @Transactional”注释的,而不是一般的所有注释。
乔纳森

3
与其他构造型注释一样,接口上的@service注释无效。所有构造型注释都应放在抽象或具体类上。
大脚怪

13

我用@Component@Service@Controller@Repository注解只在实现类,而不是接口。但是@Autowired使用Interfaces进行注释仍然对我有用。


7

将注释放在@Service上的优点是,它暗示了它是服务。我不知道默认情况下是否有任何实现类会继承此注释。

缺点是,您将使用特定于Spring的注释将接口与特定框架(例如Spring)耦合。由于应该将接口与实现分离,因此我不建议使用任何特定于框架的注释或接口的对象部分。


1
我认为我们都已经多次听到强耦合参数,但是请记住,无需jar即可存在批注,因此基本上,只要您的耦合位于批注上,它仍然可以解耦。
Niels Bech Nielsen 2014年

1

spring的一个好处是可以轻松地切换Service(或其他)实现。为此,您需要在接口上进行注释并声明变量,如下所示:

@Autowired
private MyInterface myVariable;

并不是 :

@Autowired
private MyClassImplementationWhichImplementsMyInterface myVariable;

与第一种情况一样,您可以从唯一的那一刻起就激活要注入的实现(只有一个类实现该接口)。在第二种情况下,您需要重构所有代码(新类实现具有另一个名称)。因此,注释需要尽可能多地位于界面上。此外,JDK代理非常适合于此:它们是在应用程序启动时创建和实例化的,因为与CGlib代理相反,运行时类型是预先已知的。


4
“ MyClassImplementationWhichImplementsMyInterface” LOL
inafalcao

您无需在界面上进行注释即可使第一个示例生效。您可以注释@Service实现并自动连接接口。Spring将检查实现此接口的任何对象。
马可(Marco)

1

我会把@Service你的类,但将接口的名称作为参数例如注释

interface ServiceOne {}

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

这样,您将获得所有好处,并且仍然可以注入接口但可以获取类

@Autowired 
private ServiceOne serviceOne;

因此,您的界面不依赖于Spring框架,您可以随时更改类,而不必更新所有注入点。

因此,如果我想更改实现类,则可以注释新的类并从第一个类中删除,但这就是所有需要更改的地方。如果您要注入类,那么在您想更改impl类时,可能需要做很多工作。


-1

简而言之:

@Service服务层的构造型注释。

@Repository对于一个构造型注释持久层。

@Component是一个通用构造型注释,用于告诉Spring在Application Context中创建对象的实例。可以为实例定义任何名称,默认名称是类名,例如驼峰式大小写。


3
这些注释的含义不是要寻找的,而是将它们放在Interface或其实现上的位置。
nanosoft

-3

有5个注释可用于制作春豆。在答案下面列出。

您真的需要一个界面吗?如果每个服务接口都有一个实现,请避免使用,仅使用类。当然,如果您没有RMI或需要接口代理时。

@Repository-用于注入您的dao图层类。

@Service-用于注入服务层类。在服务层中,您可能还需要使用@Transactional批注进行数据库事务管理。

@Controller-用于您的前端层控制器,例如作为弹簧bean注入的JSF托管bean。

@RestController-用于spring rest控制器,这将帮助您避免每次都将@ResponseBody和@RequestBody批注放入rest方法中。

@Component-在需要注入不是控制器,服务或dao类的spring bean的任何其他情况下使用它


是的,您需要在各层(例如数据访问层和服务层)的边界上有一个接口。它们使包含那些层实现的模块能够松散耦合。没有它们,提到的图层的客户就必须知道具体的类型,例如,当您想用CachingDao装饰BasicDao时,就需要更改它们。– Igand
15:
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.