簡介
Component用于管理擁有運行期狀態(tài)的組件的生命周期和依賴關(guān)系。
創(chuàng)建組件
一個實現(xiàn)了Lifecycle協(xié)議的record就是一個組件。
(defrecord Database [host port connection]
;; 實現(xiàn)Lifecycle協(xié)議
component/Lifecycle
(start [component]
(println ";; Starting database")
;; 在start方法初始化組件并啟動。例如,連接到數(shù)據(jù)庫,創(chuàng)建線程池等。
(let [conn (connect-to-database host port)]
;; 返回更新后的組件,附帶了運行時狀態(tài)。
(assoc component :connection conn)))
(stop [component]
(println ";; Stopping database")
;; 在stop方法停止組件,并釋放組件所獲取的外部資源。
(.close connection)
;; 返回組件,修改組件的狀態(tài)是可選的。
(assoc component :connection nil)))
可以定義一個創(chuàng)建函數(shù)初始化基本配置,保留運行期配置為空。
(defn new-database [host port]
(map->Database {:host host :port port}))
使用組件
需要使用組件的函數(shù),傳入一個組件實例作為參數(shù)。
(defn get-user [database username]
(execute-query (:connection database)
"SELECT * FROM users WHERE username = ?"
username))
組件依賴
如果a組件依賴于b組件,在a組件中定義一個屬性用于存放所依賴的b組件。Component庫自動注入所依賴的組件。
不要在構(gòu)造函數(shù)中傳入依賴的組件。
(defrecord ExampleComponent [options cache database scheduler]
component/Lifecycle
(start [this]
(println ";; Starting ExampleComponent")
;; 在start方法中,該組件的依賴組件都已經(jīng)啟動完成,并且注入進來了,可以直接使用。
(assoc this :admin (get-user database "admin")))
(stop [this]
(println ";; Stopping ExampleComponent")
;; 在stop方法中,該組件的依賴組件仍在啟動狀態(tài),直到當(dāng)前組件停止后,依賴組件才會停止。
this))
System
System用于啟動和停止其他組件,以及負(fù)責(zé)注入組件依賴。創(chuàng)建system最簡單的方式是使用system-map函數(shù),傳入一組key和組件實例作為參數(shù)。使用using函數(shù)指定依賴關(guān)系。
(component/system-map
:db (new-database host port)
:scheduler (new-scheduler)
:app (component/using
(example-component config-options)
{:database :db
:scheduler :scheduler}))
如果鍵名相同,可以使用vector指定依賴關(guān)系。
(component/system-map
:database (new-database host port)
:scheduler (new-scheduler)
:app (component/using
(example-component config-options)
[:database :scheduler]))
system本身也是組件,system-map輸出的system實例同樣實現(xiàn)了Lifecycle協(xié)議,并負(fù)責(zé)其他組件的啟動和停止。
可以使用system-using一次性指定整個system的依賴關(guān)系。
(-> (component/system-map
:config-options config-options
:db (new-database host port)
:sched (new-scheduler)
:app (example-component config-options))
(component/system-using
{:app {:database :db
:scheduler :sched}}))
Reload
參考這個工作流。在開發(fā)環(huán)境中:
(ns user
(:require [com.stuartsierra.component :as component]
[clojure.tools.namespace.repl :refer (refresh)]
[examples :as app]))
(def system nil)
(defn init []
(alter-var-root #'system
(constantly (app/example-system {:host "dbhost.com" :port 123}))))
(defn start []
(alter-var-root #'system component/start))
(defn stop []
(alter-var-root #'system
(fn [s] (when s (component/stop s)))))
(defn go []
(init)
(start))
(defn reset []
(stop)
(refresh :after 'user/go))
生產(chǎn)環(huán)境
(ns com.example.application.main
(:gen-class)
(:require [com.stuartsierra.component :as component]
[examples :as app]))
(defn -main [& args]
(let [[host port] args]
(component/start
(app/example-system {:host host :port port}))))