Component

简介

Component用于管理拥有运行期状态的组件的生命周期和依赖关系。

创建组件

一个实现了Lifecycle协议的record就是一个组件。

(defrecord Database [host port connection]
  ;; 实现Lifecycle协议
  component/Lifecycle

  (start [component]
    (println ";; Starting database")
    ;; 在start方法初始化组件并启动。例如,连接到数据库,创建线程池等。
    (let [conn (connect-to-database host port)]
      ;; 返回更新后的组件,附带了运行时状态。
      (assoc component :connection conn)))

  (stop [component]
    (println ";; Stopping database")
    ;; 在stop方法停止组件,并释放组件所获取的外部资源。
    (.close connection)
    ;; 返回组件,修改组件的状态是可选的。
    (assoc component :connection nil)))

可以定义一个创建函数初始化基本配置,保留运行期配置为空。

(defn new-database [host port]
  (map->Database {:host host :port port}))

使用组件

需要使用组件的函数,传入一个组件实例作为参数。

(defn get-user [database username]
  (execute-query (:connection database)
    "SELECT * FROM users WHERE username = ?"
    username))

组件依赖

如果a组件依赖于b组件,在a组件中定义一个属性用于存放所依赖的b组件。Component库自动注入所依赖的组件。

不要在构造函数中传入依赖的组件。

(defrecord ExampleComponent [options cache database scheduler]
  component/Lifecycle

  (start [this]
    (println ";; Starting ExampleComponent")
    ;; 在start方法中,该组件的依赖组件都已经启动完成,并且注入进来了,可以直接使用。
    (assoc this :admin (get-user database "admin")))

  (stop [this]
    (println ";; Stopping ExampleComponent")
    ;; 在stop方法中,该组件的依赖组件仍在启动状态,直到当前组件停止后,依赖组件才会停止。
    this))

System

System用于启动和停止其他组件,以及负责注入组件依赖。创建system最简单的方式是使用system-map函数,传入一组key和组件实例作为参数。使用using函数指定依赖关系。

(component/system-map
  :db (new-database host port)
  :scheduler (new-scheduler)
  :app (component/using
         (example-component config-options)
         {:database  :db
          :scheduler :scheduler}))

如果键名相同,可以使用vector指定依赖关系。

(component/system-map
  :database (new-database host port)
  :scheduler (new-scheduler)
  :app (component/using
         (example-component config-options)
         [:database :scheduler]))

system本身也是组件,system-map输出的system实例同样实现了Lifecycle协议,并负责其他组件的启动和停止。

可以使用system-using一次性指定整个system的依赖关系。

(-> (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

参考这个工作流。在开发环境中:

(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))

生产环境

(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}))))
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容