1.简介
作为软件开发人员,我们一直在寻找使用给定技术或库的最佳实践。自然,有时会有辩论。
这样的争论之一就是关于Spring的@Service
注释的放置。由于Spring提供了定义bean的替代方法,因此值得注意构造型注释的位置。
在本教程中,我们将研究@Service
批注,并检查将其放在接口,抽像类或具体类上是否最有效。
2.接口上的@Service
一些开发人员可能决定将@Service
放在接口上,因为他们想要:
- 明确表明接口只能用于服务级别的目的
- 定义新的服务实现,并在启动过程中将它们自动检测为Spring Bean
让我们看一下我们对接口进行注解的样子:
@Service
public interface AuthenticationService {
boolean authenticate(String username, String password);
}
我们注意到, AuthenticationService
现在变得更具自我描述性。 @Service
标记建议开发人员仅将其用于业务层服务,而不用于数据访问层或任何其他层。
通常,这很好,但是有一个缺点。通过将Spring的@Service
放在接口上,我们创建了一个额外的依赖项,并将我们的接口与外部库耦合。
接下来,为了测试新服务bean的自动检测,让我们创建AuthenticationService
的实现:
public class InMemoryAuthenticationService implements AuthenticationService {
@Override
public boolean authenticate(String username, String password) {
//...
}
}
我们应该注意,我们的新实现InMemoryAuthenticationService
上没有@Service
批注。我们仅在AuthenticationService
接口上保留@Service
。
因此,让我们在基本的Spring Boot设置的帮助下运行Spring上下文:
@SpringBootApplication
public class AuthApplication {
@Autowired
private AuthenticationService authService;
public static void main(String[] args) {
SpringApplication.run(AuthApplication.class, args);
}
}
当运行我们的应用程序时,**我们得到臭名昭著的NoSuchBeanDefinitionException,
**并且Spring上下文无法启动:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.baeldung.annotations.service.interfaces.AuthenticationService' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
...
因此,在接口上放置@Service
不足以自动检测Spring组件。
3. @Service
类上的@Service
在抽像类上使用@Service
注解并不常见。
让我们对其进行测试,看看它是否达到了使Spring自动检测实现类的目的。
我们将从头定义一个抽像类开始,并在其上添加@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);
}
}
我们应该注意,我们不尝试在此处直接注入抽像类,这是不可能的。相反,我们打算仅根据抽像类型获取具体类LdapAuthenticationService
的实例。正如Liskov替代原则所建议的那样,这是一个好习惯。
因此,我们再次运行AuthApplication
:
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.baeldung.annotations.service.abstracts.AbstractAuthenticationService' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations:
...
如我们所见,Spring上下文没有启动。它**以相同的NoSuchBeanDefinitionException
**异常结束。
当然,在抽像类上使用@Service
注释在Spring中没有任何作用。
4. @Service
具体类的服务
与上面看到的相反,注释实现类而不是抽像类或接口是一种很常见的做法。
这样,我们的目标主要是告诉Spring该类将是@Component
并用特殊的@Service
型对其进行标记,在本例中为@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。
5.结果
最终,我们看到了唯一的工作方式是将@Service
放入实现类中,以使其能够自动检测。除非单独注解这些类,否则Spring的组件扫描不会选择这些类,即使它们是从另一个@Service
注释的接口或抽像类派生的也是如此。
另外, Spring的文档还指出,在实现类上使用@Service
可以使组件扫描自动检测到它们。
六,结论
在本文中,我们研究了使用Spring的@Service
批注的不同位置,并了解了保留@Service
以定义服务级别的Spring Bean,以便在组件扫描期间自动检测到它们。
具体来说,我们看到将@Service
批注放置在接口或抽像类上没有任何效果,并且当使用@Service
批注时,组件扫描将仅提取具体的类。
0 评论