在Clojure中为对象的特定实例创建代理 [英] Create a proxy for an specific instance of an object in clojure
本文介绍了在Clojure中为对象的特定实例创建代理的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!
问题描述
我正在尝试创建一个代理对象,它使用闭包(let/Proxy)向对象的一些方法添加一些功能。我可以做到这一点,但不幸的是,我必须重写原始对象中的所有方法,否则我会得到一个UnsupportedOpreationException。下面是一个示例: ;;实物
(def realcon (java.sql.DriverManager/getConnection "jdbc:h2:tcp://localhost:9092/test"))
(def con
(let [msg "FG>"
xcon rcon]
(proxy [java.sql.Connection] []
(createStatement []
(println msg) ;; access to closure context !
(.createStatement xcon)))))
(def stmt (.createStatement con))
;;output FG>
(def rs (.executeQuery stmt "select count(*) from serie_sat"))
如果我从java.sql.Connection调用任何其他方法,我会得到UnsupportedOperationException异常,我可以手动代理所有方法,但可能有更好的方法!
谢谢
推荐答案
这里是使用reify
而不是proxy
的替代方法,因为根据docs的说法,它"在其约束不是禁止的所有情况下都是首选的。"
(defmacro override-delegate
[type delegate & body]
(let [d (gensym)
overrides (group-by first body)
methods (for [m (.getMethods (resolve type))
:let [f (-> (.getName m)
symbol
(with-meta {:tag (-> m .getReturnType .getName)}))]
:when (not (overrides f))
:let [args (for [t (.getParameterTypes m)]
(with-meta (gensym) {:tag (.getName t)}))]]
(list f (vec (conj args 'this))
`(. ~d ~f ~@(map #(with-meta % nil) args))))]
`(let [~d ~delegate]
(reify ~type ~@body ~@methods))))
;; Modifying your example slightly...
(def realcon (java.sql.DriverManager/getConnection "jdbc:h2:tcp://localhost:9092/test"))
(def con
(let [msg "FG>"]
(override-delegate java.sql.Connection realcon
(createStatement [this]
(println msg)
(.createStatement realcon)))))
override-delegate
宏期望主体包含您希望覆盖的方法的reify
规范。您没有覆盖的任何内容都将在代理上调用。宏生成的所有reify
规范都将包括每个方法的参数和返回值的类型提示。
我的实现有一个警告:它只检查body
中的方法名称,而忽略重载方法的参数大小性/类型。因此,在上面的示例中,java.sql.Connection
接口提供了多个createStatement
重载,不会为con
定义任何接受参数的重载。扩展宏以解决重载并不是太困难,但当我需要此行为时,我通常无论如何都必须覆盖所有这些行为。
这篇关于在Clojure中为对象的特定实例创建代理的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!
查看全文