应该在哪里保留@Service注释?接口还是实现?

The*_*ect 112 service spring

我正在使用Spring开发一个应用程序.我需要使用@Service注释.我ServiceIServiceImpl这样ServiceImpl implements ServiceI.我在这里很困惑,我应该在哪里保留@Service注释.

我应该注释接口或实现@Service吗?这两种方法有什么不同?

Ral*_*lph 117

我从不把@Component(或@Service......)放在界面上,因为这会使界面变得无用.让我解释一下原因.

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

权利要求2:接口的目的是定义一个可以由多个实现实现的契约.另一方面,你有注射点(@Autowired).只有一个接口,只有一个实现它的类,(恕我直言)没用,并且违反了YAGNI.

事实:当你把:

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

然后你会得到NoUniqueBeanDefinitionException (或者你有一个非常特殊的配置设置,环境,配置文件或限定符......)

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


Spring-Data-JPA Repository接口完全不同

  • 你写的是什么......这是正确的方法吗?是不是根本没有注释接口并为实现提供服务注释?Spring仍然能够使用接口类型进行自动装配吗?你有什么答案> http://stackoverflow.com/questions/12899372/spring-why-do-we-autowire-the-interface-and-not-the-implemented-class/12899432#12899432 (3认同)
  • @Yubaraj:公平点,但是您的问题是关于另一个主题的(对于我的回答,我假设:存在一个接口,问题不在于拥有接口,而在于在何处放置注释)。对于您的问题:几乎没有理由为永远不会有两个实现的业务服务类提供接口。(顺便说一句:只要您不为其他人构建api,就可以随时重构代码并在以后需要时介绍接口) (3认同)
  • 我有一个问题,为什么只有一个类实现时,为什么需要为服务层创建接口?我见过很多项目,它们有**控制器层,**服务层**(** servicInterface **,** serviceInterfaceImpl **)和**存储库层**。 (2认同)
  • @Yubaraj,接口允​​许在需要时为bean制作轻量级的基于接口的jdk代理。当没有接口时,spring必须使用cglib进行子类化或修改bean来制作代理。`@Transactional` 是使用 bean 代理的示例之一。AOP 是另一种。 (2认同)

Pau*_*nis 31

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

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

根据我的经验,我总是@Service在接口或抽象类和注释上使用注释,@Component以及@Repository它们的实现.@Component我在那些用于基本目的的类上使用的注释,简单的Spring bean,仅此而已.@Repository我在DAO图层中使用的注释,例如,如果我必须与数据库通信,有一些交易等.

因此,我建议@Service根据功能使用其他层注释您的界面.

  • 从[Spring docs](http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/stereotype/Service.html),*"这个注释作为@Component的特化,允许通过类路径扫描自动检测实现类,"*表明它打算用于实现类. (26认同)
  • 你能说出注释接口和注释实现之间有什么区别吗? (10认同)
  • 接口上的@service注释没有任何效果,就像其他构造型注释一样.所有构造型注释都应该放在抽象或具体的类上. (3认同)

Mur*_*dız 19

1. 接口上的@Service

@Service
public interface AuthenticationService {

    boolean authenticate(String username, String password);
}
Run Code Online (Sandbox Code Playgroud)

通常来说,这没问题,但有一个缺点。通过将 Spring 放在@Service接口上,我们创建了额外的依赖项并将我们的接口与外部库耦合。

接下来,为了测试新服务 bean 的自动检测,让我们创建一个实现AuthenticationService

public class InMemoryAuthenticationService implements AuthenticationService {

    @Override
    public boolean authenticate(String username, String password) {
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

我们应该注意,我们的新实现,InMemoryAuthenticationService,没有注释@Service。我们@Service只留下界面,AuthenticationService

因此,让我们在基本 Spring Boot 设置的帮助下运行 Spring 上下文:

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AuthenticationService authService;

    public static void main(String[] args) {
        SpringApplication.run(AuthApplication.class, args);
    }
}
Run Code Online (Sandbox Code Playgroud)

当我们运行我们的应用程序时,我们可能会遇到臭名昭著的NoSuchBeanDefinitionException,并且 Spring 上下文无法启动。

因此,放置@Service在接口上不足以自动检测 Spring 组件。


2.抽象类上的@Service

在抽象类上使用@Service注释并不常见。

我们将从头开始定义一个抽象类并@Service在其上添加注释:

@Service
public abstract class AbstractAuthenticationService {

    public boolean authenticate(String username, String password) {
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,我们扩展AbstractAuthenticationService以创建一个不带注释的具体实现:

public class LdapAuthenticationService extends AbstractAuthenticationService {

    @Override
    public boolean authenticate(String username, String password) { 
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

因此,我们还更新了AuthApplication, 以注入新的服务类:

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AbstractAuthenticationService authService;

    public static void main(String[] args) {
        SpringApplication.run(AuthApplication.class, args);
    }
}
Run Code Online (Sandbox Code Playgroud)

运行后AuthApplication,Spring 上下文不会启动。它最终再次出现相同的NoSuchBeanDefinitionException异常。

所以,@Service在抽象类上使用注解在 Spring 中没有任何效果。


3.具体类上的@Service

与我们上面看到的相反,注释实现类而不是抽象类或接口是一种很常见的做法。

通过这种方式,我们的目标主要是告诉 Spring 这个类将是 a@Component并用特殊的构造型来标记它,这就是@Service我们的例子。

因此,Spring 将从类路径中自动检测这些类,并自动将它们定义为托管 bean。

@Service那么,这次我们来介绍一下具体的服务类。我们将有一个类实现我们的接口,第二个类扩展我们之前定义的抽象类:

@Service
public class InMemoryAuthenticationService implements AuthenticationService {

    @Override
    public boolean authenticate(String username, String password) {
        //...
    }
}

@Service
public class LdapAuthenticationService extends AbstractAuthenticationService {

    @Override
    public boolean authenticate(String username, String password) {
        //...
    }
}
Run Code Online (Sandbox Code Playgroud)

我们应该注意这里我们AbstractAuthenticationService没有实现AuthenticationService这里。因此,我们可以独立测试它们。

最后,我们将两个服务类添加到AuthApplication并尝试一下:

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AuthenticationService inMemoryAuthService;

    @Autowired
    private AbstractAuthenticationService ldapAuthService;

    public static void main(String[] args) {
        SpringApplication.run(AuthApplication.class, args);
    }
}
Run Code Online (Sandbox Code Playgroud)

我们的最终测试给了我们一个成功的结果,并且 Spring 上下文启动,没有任何异常。这两个服务都会自动注册为 Bean。

您可以查看页面以获取其他说明。

  • 感谢您对该主题的精彩演示,以及此处公开的其他有趣帖子中描述的理论方法:它有助于获得必要的“点击”。 (3认同)

yal*_*ris 13

我只在实现类上使用@ Component,@ Service,@ Controller和@Repository注释,而不是在接口上.但@Autowired注释与接口仍然适用于我.


小智 7

在@Service上添加注释的优点是它提示它是一个服务.我不知道默认情况下是否会有任何实现类继承此annoation.

Con方面是您通过使用特定于弹簧的注释将您的界面与特定框架(即Spring)耦合.由于接口应该与实现分离,我不建议使用任何特定于框架的Annotations或对象的接口部分.


小智 5

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

interface ServiceOne {}

@Service("ServiceOne")
class ServiceOneImpl implements ServiceOne{}
Run Code Online (Sandbox Code Playgroud)

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

@Autowired 
private ServiceOne serviceOne;
Run Code Online (Sandbox Code Playgroud)

因此,您的界面与 spring 框架无关,您可以随时更改类,而不必更新所有注入点。

因此,如果我想更改实现类,我可以注释新类并从第一个类中删除,但这就是需要更改的全部内容。如果您注入该类,那么当您想要更改 impl 类时,您可能需要做很多工作。