mon*_*962 25 components functional-programming dependency-injection clojure
我正在努力探讨如何在Clojure应用程序中使用Stuart Sierra的组件库.通过观看他的Youtube视频,我想我已经掌握了导致他创建图书馆的问题; 但是我正在努力研究如何在一个新的,相当复杂的项目上实际使用它.
我意识到这听起来很模糊,但感觉就像我缺少一些关键概念,一旦我明白了,我就会很好地掌握如何使用组件.换句话说,斯图亚特的文档和视频详细介绍了组件的内容和原因,但我错过了如何.
是否存在任何类型的详细教程/演练:
提前致谢
Jef*_*eff 39
简而言之,Component是一个专门的DI框架.它可以设置一个给定两个映射的注入系统:系统映射和依赖映射.
让我们看看一个虚构的网络应用程序(免责声明,我在没有实际运行的情况下以表格形式输入):
(ns myapp.system
(:require [com.stuartsierra.component :as component]
;; we'll talk about myapp.components later
[myapp.components :as app-components]))
(defn system-map [config] ;; it's conventional to have a config map, but it's optional
(component/system-map
;; construct all components + static config
{:db (app-components/map->Db (:db config))
:handler (app-components/map->AppHandler (:handler config))
:server (app-components/map->Server (:web-server config))}))
(defn dependency-map
;; list inter-dependencies in either:
;; {:key [:dependency1 :dependency2]} form or
;; {:key {:name-arg1 :dependency1
;; :name-arg2 :dependency2}} form
{:handler [:db]
:server {:app :handler})
;; calling this creates our system
(def create-system [& [config]]
(component/system-using
(system-map (or config {})
(dependency-map)))
Run Code Online (Sandbox Code Playgroud)
这允许我们在需要时调用(create-system)以创建整个应用程序的新实例.
使用(component/start created-system),我们可以运行它提供的系统服务.在这种情况下,它是正在侦听端口和开放数据库连接的Web服务器.
最后,我们可以阻止(component/stop created-system)系统停止运行(例如 - 停止Web服务器,断开与db的连接).
现在让我们components.clj来看看我们的应用程序:
(ns myapp.components
(:require [com.stuartsierra.component :as component]
;; lots of app requires would go here
;; I'm generalizing app-specific code to
;; this namespace
[myapp.stuff :as app]))
(defrecord Db [host port]
component/Lifecycle
(start [c]
(let [conn (app/db-connect host port)]
(app/db-migrate conn)
(assoc c :connection conn)))
(stop [c]
(when-let [conn (:connection c)]
(app/db-disconnect conn))
(dissoc c :connection)))
(defrecord AppHandler [db cookie-config]
component/Lifecycle
(start [c]
(assoc c :handler (app/create-handler cookie-config db)))
(stop [c] c))
;; you should probably use the jetty-component instead
;; https://github.com/weavejester/ring-jetty-component
(defrecord Server [app host port]
component/Lifecycle
(start [c]
(assoc c :server (app/create-and-start-jetty-server
{:app (:handler app)
:host host
:port port})))
(stop [c]
(when-let [server (:server c)]
(app/stop-jetty-server server)
(dissoc c :server)))
Run Code Online (Sandbox Code Playgroud)
那我们刚刚做了什么?我们得到了一个可重新加载的系统.我认为一些使用figwheel的 clojurescript开发人员开始看到相似之处.
这意味着我们可以在重新加载代码后轻松重启系统.到了user.clj!
(ns user
(:require [myapp.system :as system]
[com.stuartsierra.component :as component]
[clojure.tools.namespace.repl :refer (refresh refresh-all)]
;; dev-system.clj only contains: (def the-system)
[dev-system :refer [the-system]])
(def system-config
{:web-server {:port 3000
:host "localhost"}
:db {:host 3456
:host "localhost"}
:handler {cookie-config {}}}
(def the-system nil)
(defn init []
(alter-var-root #'the-system
(constantly system/create-system system-config)))
(defn start []
(alter-var-root #'the-system component/start))
(defn stop []
(alter-var-root #'the-system
#(when % (component/stop %))))
(defn go []
(init)
(start))
(defn reset []
(stop)
(refresh :after 'user/go))
Run Code Online (Sandbox Code Playgroud)
要运行系统,我们可以在我们的repl中输入:
(user)> (reset)
Run Code Online (Sandbox Code Playgroud)
这将重新加载我们的代码,并重新启动整个系统.如果它正在运行,它将关闭正在运行的现有系统.
我们获得其他好处:
值得注意的是,由于所有内容都在进程中,因此Component不会处理与故障转移,分布式系统或错误代码相关的任何内容;)
组件可以帮助您在服务器中管理大量"资源"(也称为有状态对象):
如果你只有一个web服务器+ db,那么组件看起来就像是矫枉过正.但是现在很少有网络应用程序.
注意:移动the-system到另一个命名空间会降低the-system在开发时刷新var 的可能性(例如 - 调用refresh而不是reset).