我正在用 Spring 开发一个应用程序。我需要使用 @Service注释。我有 ServiceI和 ServiceImpl这样的 ServiceImpl implements ServiceI。在这里,我对于应该将 @Service注释放在哪里感到困惑。
@Service
ServiceI
ServiceImpl
ServiceImpl implements ServiceI
我应该用 @Service注释接口还是实现? 这两种方法有什么区别?
基本上,像 @ 服务、 @ Repository、 @ 组件等这样的注释都有同样的用途:
使用基于注释的配置和类路径时自动检测 扫描中。
根据我的经验,我总是在接口上使用 @Service注释,或者像 @Component和 @Repository这样的抽象类和注释来实现它们。我正在使用的 @Component注释用于那些基本用途的类,简单的 Spring bean,仅此而已。我在 DAO层使用的 @Repository注释,例如,如果我必须与数据库通信,有一些事务,等等。
@Component
@Repository
DAO
所以我建议根据功能使用 @Service和其他层来注释你的界面。
将注释放在@Service 上的优点是,它提示它是一个服务。我不知道是否有任何实现类会默认继承这个注释。
缺点是,通过使用特定于 Spring 的注释,可以将接口与一个特定的框架(例如 Spring)耦合。 由于接口应该与实现解耦,因此我不建议使用任何特定于框架的注释或接口的对象部分。
我只在实现类上使用 @Component、 @Service、 @Controller和 @Repository注释,而不在接口上使用。但是使用接口的 @Autowired注释仍然适合我。如果接口 Spring 组件扫描只有一个实现,那么只需使用 @Autowired注释就可以自动找到它。如果有多个实现,则需要使用 @Qualifier注释和 @Autowired来在注入点注入正确的实现。
@Controller
@Autowired
@Qualifier
我从来没有把 @Component(或者 @Service,...)放在接口上,因为这会使接口无用。
声明1: 如果您有一个接口,那么您希望将该接口用于注入点类型。
权利要求2: 接口的目的是定义一个契约,这个契约可以由多个实现实现。另一边是注射点(@Autowired)。只有一个接口和一个实现它的类(IMHO)是无用的,并且违反了 YAGNI。
事实: 当你输入:
然后你会得到和 NoUniqueBeanDefinitionException (或者您有一个非常特殊的配置设置,包括 Environment、 Profiles 或 Qualifier...)
NoUniqueBeanDefinitionException
结论: 如果在接口上使用 @Component(或 @Service,...) ,那么必须违反两个类中的至少一个。因此,我认为将 @Component放在接口级别是没有用的(除了一些罕见的情况)。
Spring-Data-JPA Repository 接口是完全不同的
有5个注释可以用来制作春豆。请在下面的答案中列出。
你真的需要一个接口吗?如果要为每个服务接口实现一个实现,只需避免它,只使用 class。当然,如果你没有 RMI 或当接口代理是必需的。
@ Repository-用于注入你的道层类。
@ Service-用于注入服务层类。在服务层,您可能还需要使用@Transactional 注释来进行 db 事务管理。
@ Controller-使用前端层控制器,例如将 JSF 托管 bean 注入为 spring bean。
@ RestController-用于春季休息控制器,这将帮助您避免每次在休息方法中放置@ResponseBody 和@RequestBody 注释。
@ Component-在任何其他情况下,当您需要注入不是控制器、服务或者道类的 spring bean 时,使用它
简单地说:
@ Service 是 服务层的原型注释。
@ Repos itory 是 坚持不懈层的原型注释。
@ Component 是一个 一般原型注释,用于告诉 Spring 在 Application Context 中创建对象的实例。有可能 为实例定义任何名称,默认为驼峰大小写的类名。
Spring 的一个好处是可以轻松地切换 Service (或其他)实现。 为此,您需要在接口上进行注释,并像下面这样声明变量:
@Autowired private MyInterface myVariable;
而不是:
@Autowired private MyClassImplementationWhichImplementsMyInterface myVariable;
与第一种情况类似,您可以从注入的实现唯一的那一刻起激活它(只有一个类实现了接口)。 在第二种情况下,需要重构所有代码(新的类实现有另一个名称)。 因此,注释需要尽可能地出现在接口上。此外,JDK 代理非常适合这一点: 它们是在应用程序启动时创建和实例化的,因为与 CGlib 代理相反,运行时类型是预先知道的。
我会将 @Service放在您的类上,但是将接口的名称作为注释的参数,例如。
interface ServiceOne {} @Service("ServiceOne") class ServiceOneImpl implements ServiceOne{}
通过这样做,您可以获得所有的好处,并且仍然可以注入接口,但是可以获得类
@Autowired private ServiceOne serviceOne;
所以你的接口不需要绑定到 Spring 框架,你可以随时更改类,而不需要更新所有的注入点。
因此,如果我想更改实现类,我可以只注释新的类并从第一个类中删除,但这就是需要更改的全部内容。如果您注入这个类,那么当您想要更改 impl 类时,您可能有很多工作要做。
1.@接口服务
@Service public interface AuthenticationService { boolean authenticate(String username, String password); }
通常情况下,这没问题,但有个缺点。通过将 Spring 的 @Service放在接口上,我们创建了一个额外的依赖项,并将接口与外部库耦合。
接下来,为了测试新服务 bean 的自动检测,让我们创建一个 AuthenticationService的实现:
AuthenticationService
public class InMemoryAuthenticationService implements AuthenticationService { @Override public boolean authenticate(String username, String password) { //... } }
我们应该注意,我们的新实现 InMemoryAuthenticationService上没有 @Service注释。我们只在界面上留下了 @Service,AuthenticationService。
InMemoryAuthenticationService
因此,让我们借助一个基本的 Spring Boot 设置来运行 Spring 上下文:
@SpringBootApplication public class AuthApplication { @Autowired private AuthenticationService authService; public static void main(String[] args) { SpringApplication.run(AuthApplication.class, args); } }
当我们运行我们的应用程序时,我们可能会得到臭名昭著的 异常,而 Spring 上下文无法启动。
因此,在接口上放置 @Service不足以自动检测 Spring 组件。
2.@抽象课程服务
在抽象类上使用 @Service注释并不常见。
我们将从头开始定义一个抽象类,并在其上添加 @Service注释:
@Service public abstract class AbstractAuthenticationService { public boolean authenticate(String username, String password) { return false; } }
接下来,我们扩展 AbstractAuthenticationService来创建一个具体的实现,而不对其进行注释:
AbstractAuthenticationService
public class LdapAuthenticationService extends AbstractAuthenticationService { @Override public boolean authenticate(String username, String password) { //... } }
因此,我们也更新我们的 AuthApplication,注入新的服务类:
AuthApplication
@SpringBootApplication public class AuthApplication { @Autowired private AbstractAuthenticationService authService; public static void main(String[] args) { SpringApplication.run(AuthApplication.class, args); } }
在我们运行了 AuthApplication之后,Spring 上下文没有启动,它再次以相同的 异常异常结束。
因此,在抽象类上使用 @Service注释在 Spring 中没有任何作用。
3.@混凝土课服务
与我们上面看到的相反,注释实现类而不是抽象类或接口是很常见的做法。
通过这种方式,我们的目标主要是告诉 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。
您可以查看 这个页了解其他解释。