我正在使用Spring开发一个应用程序。我需要使用@Service
注释。我有ServiceI
和ServiceImpl
,ServiceImpl implements ServiceI
。我在这里很困惑,我应该在哪里保留@Service
注释。
我应该用@Service
注释接口或实现吗?这两种方法有什么不同?
答案 0 :(得分:105)
我从未在界面上放置@Component
(或@Service
,...),因为这会使界面变得无用。让我解释一下原因。
声明1:如果您有一个接口,那么您希望将该接口用于注入点类型。
声明2:接口的目的是定义一个可以由多个实现实现的合同。另一方面,你有注射点(@Autowired
)。只有一个界面,只有一个实现它的类,(恕我直言)没用,并且违反了YAGNI。
事实:当您放置:
@Component
(或@Service
,...)在界面然后你会得到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
注释。我们只在界面上留下了 @Service
,AuthenticationService
。
那么,让我们借助基本的 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时,在任何其他情况下使用它