如何代理含保护构造方法类的私有内部Java接口?
解决Clojure代理私有Java内部接口的IllegalAccessError问题
这个问题的核心原因很明确:你要代理的Foo$Bar是私有内部接口,JVM的编译时访问控制机制会阻止Clojure的proxy宏生成的类去实现它——毕竟proxy是在编译阶段尝试直接引用这个私有接口,而私有成员只能在定义它的类内部访问。
下面是两种可行的解决思路,优先推荐第一种:
方法一:使用Java动态代理(反射绕过访问限制)
Java的java.lang.reflect.Proxy类是运行时生成代理类,它能绕过编译时的访问检查,只要我们能通过反射拿到私有接口的Class对象就行。具体步骤如下:
获取私有内部接口的Class对象
我们可以通过Foo类的getDeclaredClasses方法直接获取(更可靠):;; 从Foo的已声明类中过滤出Foo$Bar接口 (def bar-interface (first (filter #(= (.getName %) "Foo$Bar") (.getDeclaredClasses Foo))))或者用
Class/forName指定类加载器加载:(def foo-classloader (.getClassLoader Foo)) (def bar-interface (Class/forName "Foo$Bar" true foo-classloader))实现InvocationHandler处理方法调用
这个Handler会接管代理实例的所有方法调用,我们在这里实现someMethod的自定义逻辑:(def bar-handler (reify java.lang.reflect.InvocationHandler (invoke [_ proxy method args] (cond ;; 处理目标接口的someMethod (= (.getName method) "someMethod") (do (println "执行someMethod的自定义逻辑") ;; 这里写你的具体实现,注意返回值要匹配接口定义 nil) ;; 处理Object类的默认方法(比如toString、hashCode) :else (.invoke method proxy args)))))创建代理实例
用Proxy/newProxyInstance生成实现Foo$Bar的实例:(def bar-proxy (java.lang.reflect.Proxy/newProxyInstance (.getClassLoader Foo) (into-array Class [bar-interface]) bar-handler))
现在你就可以正常使用bar-proxy调用someMethod了,完全绕过了编译时的访问限制。
方法二:修改接口的访问权限(不推荐)
如果你一定要用Clojure的proxy宏,那可以通过反射修改Foo$Bar的访问权限,把它从private改成public。不过这种方法依赖JVM的内部实现,风险较高,不建议在生产环境使用:
;; 获取Unsafe实例(需要反射,因为Unsafe的构造方法是private的) (def unsafe (let [field (.getDeclaredField sun.misc.Unsafe "theUnsafe")] (.setAccessible field true) (.get field nil))) ;; 获取Class的accessFlags字段的偏移量 (def access-flags-offset (.objectFieldOffset unsafe (.getDeclaredField Class "accessFlags"))) ;; 修改Foo$Bar的访问权限:移除private标志,添加public标志 (defn make-public [cls] (let [flags (.getInt unsafe cls access-flags-offset)] ;; 清除PRIVATE标志,设置PUBLIC标志 (.putInt unsafe cls access-flags-offset (bit-or (bit-clear flags 0x0002) 0x0001)))) ;; 应用修改 (make-public bar-interface) ;; 现在可以用proxy了 (def bar-proxy (proxy [Foo$Bar] [] (someMethod [] (println "用proxy实现的someMethod"))))
这种方法虽然能让proxy正常工作,但会破坏JVM的访问控制规则,可能带来安全隐患,而且不同JVM版本的accessFlags偏移量可能不同,兼容性差。
内容的提问来源于stack exchange,提问作者Aaron Iba
相关产品推荐
相关产品推荐

