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?。