知識庫 / Spring RSS 訂閱

Spring @Service 註解應放置在何處?

Spring
HongKong
6
12:43 PM · Dec 06 ,2025

1. 引言

作為軟件開發人員,我們一直在尋找使用特定技術或庫的最佳實踐。當然,有時也會有爭論。

其中一個爭論是關於 Spring 框架中 @Service 註解的放置位置。由於 Spring 提供了替代定義 Bean 的方法,因此值得關注這些類型註解的位置。

在本教程中,我們將研究 @Service 註解,並探討 它最好放在接口、抽象類還是具體類上

2. 在接口上使用 <em @Service>

一些開發者可能會決定在接口上使用 <em @Service>,因為他們希望:

  • 明確表明接口僅用於服務級別目的
  • 定義新的服務實現,並在啓動時自動將其識別為 Spring Bean

讓我們看看在註解接口後的效果:

@Service
public interface AuthenticationService {

    boolean authenticate(String username, String password);
}

如我們所見,AuthenticationService現在更加具有描述性。@Service標記建議開發者僅將其用於業務層服務,而不是用於數據訪問層或其他層。

通常情況下,這沒問題,但存在一些弊端。通過在 Spring 上使用 @Service 標記接口,我們創建了額外的依賴關係並使我們的接口與外部庫耦合。

接下來,為了測試我們新服務豆的自動檢測,讓我們創建一個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. 在抽象類上使用 <em @Service>

使用 <em @Service> 註解在抽象類上並不常見。

讓我們測試一下,看看它是否能實現我們的目標,即讓 Spring 自動檢測我們的實現類。

我們將從頭開始定義一個抽象類,並在該類上添加 <em @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 這種抽象類型的具體實例,僅依賴於抽象類型。這是一種良好的實踐,也符合利斯刻維替換原則。

因此,我們再次運行 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. 在具體類上使用 <em @Service>

與之前看到的不同,在具體類上標註比在抽象類或接口上標註更為常見。

通過這種方式,我們的目標主要是告訴 Spring 這個類將作為 <em @Component 並用特殊的類型別名 <em @Service> 標記。

因此,Spring 將自動檢測 classpath 中的這些類,並自動將它們定義為管理 Bean。

所以,我們現在將 <em @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. 結果

最終,我們發現唯一可行的方法是將 <em @Service</em> 放在我們的實現類上,使其能夠自動檢測到。<strong>Spring 的組件掃描除非單獨標註,否則不會拾取即使它們是從另一個<em @Service` 註解的接口或抽象類派生出來的類。

此外,Spring 的文檔 也指出,在實現類上使用 `` 可以使它們能夠通過組件掃描自動檢測到。

6. 結論

在本文中,我們探討了使用 Spring 框架中 <em @Service</em> 註解的不同使用場景,並學習瞭如何將 <em @Service</em> 保持在服務級別 Spring Bean 中,以便它們在組件掃描期間自動檢測。

具體來説,我們發現將 <em @Service</em> 註解應用於接口或抽象類沒有任何效果,只有標註了 <em @Service</em> 的具體類才會通過組件掃描被選中。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.