Spring @Service註解應放置在哪裡?
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
批註時,組件掃描將僅提取具體的類。