Reputation: 3568
I have a lot of spring beans that enscapsulate existing business logic. Now I am looking to re-use those Spring Beans from within my new Clojure Compojure application. How do I do this ?
What I am looking for is Clojure equivalent of this
Context context = new ClassPathXmlApplicationContext(....);
MyBean mybean = context.get("mybean", MyBean.class)
Is there a Clojure way of doing this ?
Upvotes: 5
Views: 3642
Reputation: 2028
I have been using Spring and Clojure for some time now. I must admit to using it less and less as Spring isn't really necessary in a Clojure world. Where I do find it useful is testing my Spring config and exercising the beans. First how I use my code, then the code itself.
Usage
(&init "applicationContext.xml")
(with-spring [mybean myotherbean]
(.method mybean arg1 arg2 arg3)
(.method myotherbean arg))
&init
reads the Spring application context and stuffs it in a var.
with-spring
calls getBean for each of the names in the vector assigns it to a "variable" of the same name, using let
. The body is then executed using those assignments.
Here is the code to make it happen
(declare ^:dynamic *spring-context*)
(defn load-context
"Load a Spring Framwork Application context based on the locations"
([parent locations]
(doto (new ClassPathXmlApplicationContext (into-array locations) parent)
.refresh))
([locations]
(new ClassPathXmlApplicationContext (into-array locations))))
(defn &init
"Define spring funcs and return the Spring Application Context."
[locs]
(let [ctx (load-context locs)]
(def ^:dynamic *spring-context* ctx)
ctx))
(defn get-bean
[context name] (. context getBean name))
(defmacro typed-bean [ctx key]
(let [rtnval (gensym "rtnval") cls (gensym "cls") ]
`(fn []
(let [bean# (get-bean ~ctx ~key)
~cls (.getType ~ctx ~key)]
(let [~(with-meta rtnval {:tag cls}) bean#] ~rtnval)))))
(defn create-bean-map
"Create a map of bean names (as keywords) to functions. Calling the function
will return the bean with the given name.
ctx - The Spring Application Context"
([ctx]
(let [names (seq (org.springframework.beans.factory.BeanFactoryUtils/beanNamesIncludingAncestors ctx))]
(apply hash-map (mapcat (fn [f]
[(keyword f) (typed-bean ctx f)]) names)))))
(defn &beans [] (create-bean-map (&ctx)))
(defn && "Get a bean. Accepts a string, symbol or keyword"
([name]
(if-let [f (get (&beans) (keyword name))] (f))))
(defmacro with-spring
[[& beans] & body]
`(let [badones# (filter (fn [b#] (not (&& b#))) ~(reduce conj [] (map keyword beans)))]
(if (seq badones#) (throw (IllegalArgumentException. (str "Undefined beans:" (apply str badones#)))))
(let ~(reduce conj []
(mapcat
(fn [b]
(vector
b
(list (list (keyword b) '(&beans)))))
beans))
~@body)))
Upvotes: 5
Reputation: 84369
(let [context (ClassPathXmlApplicationContext. ...)
mybean (.get context "mybean" MyBean)]
...)
is the Clojure equivalent of your Java code. ...
is whatever you want to do next. Or if you want to return mybean
as the value of the whole thing (perhaps this will be wrapped in a function):
(let [context (ClassPathXmlApplicationContext. ...)]
(.get context "mybean" MyBean))
Note the dot at the end of ClassPathXmlApplicationContext.
; that's shorthand for new
, as in (new ClassPathXmlApplicationContext ...)
.
Upvotes: 6