clojure工程的构建在此不详细讲述。假设你clojure工程中创建了如下单元:
(ns holiday.holiday-client)
(defn holiday-status
[game-id type]
(let [cfg (util/load-config-memo "holiday-config.edn")]
"holiday-status is called"))
(defn holiday-rewards
[game-id type users datas]
1)
在函数中holiday-status
我们加载了一个holiday-config.edn
配置文件,返回一个字符串。其中load-config-memo
函数会将加载的配置文件内容缓存在内存中,这样以后再调用该函数时,就不要重新读取配置文件了,它是基于clojure的memoize
函数实现的;在函数holiday-rewards
中,返回了一个整数1
。上述两个函数是想暴露给java调用的,下面讲java工程中如何调用它们。
将上述构建的clojure工程打包,假设包名为holiday-client-0.1.0-standalone.jar
,以mvn构建的项目,在pom.xml中加入如下配置,引入clojure库以及上述holiday-client-0.1.0-standalone.jar
包。clojure工程的配置文件,则复制到java工程的资源目录下即可。
<dependency>
<groupId>org.clojuregroupId>
<artifactId>clojureartifactId>
<version>1.6.0version>
dependency>
<dependency>
<groupId>holiday-clientgroupId>
<artifactId>holiday-clientartifactId>
<version>0.1.0-standaloneversion>
dependency>
注意:对于lein构建的clojure工程,用lein uberjar
命令打jar包时,会打出两种包:holiday-client-0.1.0.jar
和holiday-client-0.1.0-standalone.jar
。第一种jar包是不包含clojure工程所依赖的其他包的,因此如果你在pom.xml
文件中引入的是这种包,那么连带它依赖的其他包也要一起引入。第二种*-standalone.jar
包,是将其所以来的其他包一并打入到这个独立包中,你只需在java工程中单独引入这个包就行,但这可能会出现一个问题:该独立包中依赖的其他库,可能在java工程中已经存在另外一个版本,这时就可能产生冲突(比如clojure工程找到的包是java中的那个版本,从而产生bug),这种bug在不同的系统环境中可能不同,比如在测试环境中不会暴露(因为找到了正确的包),但在发布环境中就出现T T。
以下是我在实际项目中提取出来的用于与clojure交互的工具类:
package holidaycarnival;
import clojure.java.api.Clojure;
import clojure.lang.IFn;
/**
* Java跟Clojure交互的工具方法
*
*/
public class CljUtil {
private static final IFn require = Clojure.var("clojure.core", "require");
/**
* 加载指定的Clojure空间 使用相应空间的函数前必须先调用该函数加载相应空间
*
* @param ns
*/
public static final void requireNameSpace(String ns) {
require.invoke(Clojure.read(ns));
}
/**
* 引用指定空间中的相应Clojure函数 注意:先加载相应函数
*/
public static final IFn referClojureFn(String ns, String fn) {
return Clojure.var(ns, fn);
}
}
接下来,可以定义一个与业务相关的用于调用clojure工程函数的类:
package holidaycarnival;
import clojure.lang.IFn;
import net.minidev.json.JSONObject;
import net.minidev.json.JSONValue;
public class CljHoliday {
/**
* cljNsHoliday是我们想要调用的函数所在的clojure命名空间
*/
private static final String cljNsHoliday = "holiday.holiday-client";
/**
* 业务相关的其他数据
*/
public static final String DOUBLE_EXP = "double-exp";
/**
* 加载clojure命名空间
*/
static {
CljUtil.requireNameSpace(cljNsHoliday);
}
/**
* 获取假期活动奖励(调用clojure工程中的holiday-rewards函数)
*/
public static Integer[] holidayRewards(String gameID, String type, String[] usernames, int[] datas) {
IFn cljHolidayRewards = CljUtil.referClojureFn(cljNsHoliday, "holiday-rewards");
return (Integer[])cljHolidayRewards.invoke(gameID, type, usernames, datas);
}
/**
* 查询当前假期活动是否开启
* 返回一个json对象,内容为:
* { status : String 当前活动状态close/open
remain : Long 距离活动开始/结束最少剩余1000(mills)
type : String 活动类型:双倍经验“double-exp”
data : double 活动数据:如在双倍经验活动中2.0表示经验奖励倍数
msg : String 附加消息}
*/
public static JSONObject holidayStatus(String gameID, String type){
IFn cljIsHolidayStatus = CljUtil.referClojureFn(cljNsHoliday, "holiday-status");
Object status = cljIsHolidayStatus.invoke(gameID, type);
JSONObject jsonObject = (JSONObject) JSONValue.parse(status.toString());
return jsonObject;
}
}
这样java工程中就可以通过CljHoliday的静态函数,来调用clojure函数了。
clojure作为一门函数式编程语言,有其自身的优势,比如可以用极为精简的代码写一些逻辑运算,灵活的edn文件配置,编程效率特别高。因此,在需要编写一些较为复杂的逻辑模块或者是要和第三方clojure库/服务进行无缝交互时,可以使用上述的方法。