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

时间:2013-05-03 04:51:03

标签: spring service

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

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

9 个答案:

答案 0 :(得分:105)

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

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

声明2:接口的目的是定义一个可以由多个实现实现的合同。另一方面,你有注射点(@Autowired)。只有一个界面,只有一个实现它的类,(恕我直言)没用,并且违反了YAGNI

事实:当您放置:

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

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

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

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

答案 1 :(得分:33)

基本上注释如@Service@Repository@Component等,它们都有相同的用途:

  

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

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

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

答案 2 :(得分:12)

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

答案 3 :(得分:7)

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

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

答案 4 :(得分:1)

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

@Autowired
private MyInterface myVariable;

而不是:

@Autowired
private MyClassImplementationWhichImplementsMyInterface myVariable;

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

答案 5 :(得分:0)

简单地说:

@Service 服务图层的刻板印象注释。

@Repository 持久性图层的刻板印象注释。

@Component 是一个泛型构造型注释,用于告诉Spring在Application Context中创建对象的实例。有可能  定义实例的任何名称,默认是类名为camel case。

答案 6 :(得分:0)

我会将@Service放在您的类上,但是将接口名称作为参数添加到注释中,例如

ServiceOne接口{}

@Service(“ ServiceOne”) ServiceOneImpl类实现ServiceOne {}

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

@自动连线 私有ServiceOne serviceOne;

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

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

答案 7 :(得分:0)

1. @Service 接口

@Service
public interface AuthenticationService {

    boolean authenticate(String username, String password);
}

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

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

public class InMemoryAuthenticationService implements AuthenticationService {

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

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

那么,让我们借助基本的 Spring Boot 设置来运行我们的 Spring 上下文:

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AuthenticationService authService;

    public static void main(String[] args) {
        SpringApplication.run(AuthApplication.class, args);
    }
}

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

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


2.抽象类上的@Service

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

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

@Service
public abstract class AbstractAuthenticationService {

    public boolean authenticate(String username, String password) {
        return false;
    }
}

接下来,我们扩展 AbstractAuthenticationService 以创建一个具体的实现,而无需对其进行注释:

public class LdapAuthenticationService extends AbstractAuthenticationService {

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

相应地,我们也更新了我们的 AuthApplication,以注入新的服务类:

@SpringBootApplication
public class AuthApplication {

    @Autowired
    private AbstractAuthenticationService authService;

    public static void main(String[] args) {
        SpringApplication.run(AuthApplication.class, args);
    }
}

在我们运行 AuthApplication 之后,Spring 上下文没有启动。它再次以相同的 NoSuchBeanDefinitionException 异常结束。

因此,在抽象类上使用 @Service 注释在 Spring 中没有任何影响。


3. @Service 在具体类上

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

通过这种方式,我们的目标主要是告诉 Spring 这个类将是一个 @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) {
        //...
    }
}

我们在这里应该注意,我们的 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);
    }
}

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

有关完整说明,您可以查看 Yavuz Taş 的 Where Should the Spring @Service Annotation Be Kept?

答案 8 :(得分:-3)

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

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

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

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

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

@RestController - 用于弹簧控制器,这可以帮助你避免每次都在你的休息方法中放入@ResponseBody和@RequestBody注释。

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