在上一篇文章中,我們講完了Spring如何透過容器(Container)來託管Beans,也就是商業邏輯所需的依賴物件,將實例化的過程反轉(Inverse Control),抽離主流程。
這不僅使程式結構更加簡潔,還有助於依賴的抽換和測試。
講完了理論,這篇文章將以本專案的Bot Server為例,說明開發者在實際開發中需要提供哪些資訊,以及如何組合程式結構,使上述過程得以實現。我們將涵蓋以下幾個重點:
Bean
和 Component
"在物件導向中的一切都是物件,在Spring中的一切都是Beans。"
本來要拿這句看似通順有力的話來當引言,還好沒有,直接被AI打臉。
第一句話可能沒太大問題,第二句的問題就不只有語病了。
雖然在IoC的過程中,會將實依賴例化成物件的過程託付給容器,但並不是所有物件都會交由容器來建造或託管。
new
來把類別(Class)實例化成物件。new出來物件並不會被Spring容器託管。<tag>
描述,多了一個了解<tag>
定義以及轉換的過程。@
作為前綴來表示。Function的名稱 + 引數(Parameters)型態
,可以作為辨識Function的依據此外,Java 也允許開發者自定義註解。在Spring中就有很多非Java原生的Annotation,例如後續會提到的@Bean
和@Component
。
@SpringBootApplication
的Class通稱Application Class,也是Spring Boot的程式進入點。如果使用前一章介紹的Spring Initializr,會在下載的初始專案中找到DemoApplication,他有兩個特色:
所有Bean的根目錄
。根目錄
的意義在之後會說明,下面我們先說明Bean
和Componet
的意義和使用@Bean
和 @Component
@Configuration
,並且在這個Class的Method標示@Bean
@Component
@Configuration
的Class取代xml設定檔,我們暫稱Configuration Class
@Bean
的Function,宣告Bean,該Function return的物件就是要宣告的Bean
例如:
@Configuration
public class LinebotConfig {
@Bean
LineSignatureValidator lineSignatureValidator() {
return new LineSignatureValidator(channelSecret.getBytes(StandardCharsets.UTF_8));
}
Configuration Class
lineSignatureValidator
這個Function會return一個已經設定好ChannelSecret的LineSignatureValidator的物件,因為標注了@Bean
,所以會是一個將要被Spring容器託管的Bean@Configuration
+ @Bean
以外,Spring還提供一個更簡單暴力的方法: 把Class標註為@Component
(以下暫稱為Component Class
)。@Component
public class OpenAiApiService {
@Value("${openai.api.key}")
private String token;
public String sendMessage(String pre, String userInputPrompt) {
...
@SpringBootApplication
的Class的所在目錄,會是所有Bean的根目錄,這是為什麼呢? 原理如下:@ComponentScan
、*@Configuration
, @EnableAutoConfiguration
。@ComponentScan
意旨告訴Spring,"掃描這個Class所在的目錄(Package),以及所有的子目錄(sub Packages),只要有找到被標示為@Component
的Class,就把它當成要給容器託管的Bean"。任何Component Class,只要放在被@ComponentScan標注的Class的目錄或子目錄,就會被當成要給容器託管的Bean
@Configuration
也是@Component
的一種特化,它同樣需要處於@ComponentScan的掃描範圍才會生效。@Configuration
+ @Bean
,還是2.@Component
,這兩種方式宣告的Bean,都必須處在@SpringBootApplication
,或者 @ComponentScan
Class所在的目錄或子目錄,才會被容器託管。圖: 由於Application Class(或者@ComponentScan宣告的Class)的位置在根目錄,
所有位於根目錄,或者子目錄的:
@Autowired
這個Annotation,來取用已經被Spring容器託管的Bean:class KafkaConsumer {
@Autowired
LineSignatureValidator lineSignatureValidator;
@Autowired
,就可以直接拿到設定好channelSecret的LineSignatureValidator物件。@Autowired
就是前面提到的Dependency Injection(DI)的實作。@Autowired
,@Inject
這個Annotation也有一樣的作用,差別僅在於後者是來自JSR-330規範,@Autowired
是Spring自己特有的Annotation。@SpringBootApplication
的Class本身也可以宣告Bean。spring-boot-starter-data-jpa
依賴時,Spring會自動配置 JPA 所需的 Bean,如 DataSource、EntityManager 等。spring-boot-starter-web
依賴時,Spring甚至會自動配置一個內嵌的 Tomcat伺服器。@Controller
會自動將標註的Function映射為處理指定URL的Web request的對應方法。總的來說:
讓Spring容器託管Bean
1.需要有Class被宣告成Bean的根目錄,該Class稱為Application Class
@ComponentScan
,或者 @SpringBootApplication
2.所有託管的Bean都要在Application Class所在目錄,或者子目錄中
3.要託管的Bean可以用兩種方式宣告:
@Configuration
+ @Bean
@Component
取用Spring容器託管的Bean
@Autowired
或 @Inject
來做Dependency Injection(DI)。本專案用到的Stereotype Annotation及其作用,將在之後的章節介紹。