在某些处理规则复杂且变化性大的业务中,如果将这些规则硬性编码到代码中将降低系统灵活性,扩展性,增加维护成本。
脚本有不需要编译的有点,因此如果能够将这些规则使用脚本实现,则可以避免系统的升级和重新部署。
应用实例:
该项目是一个c/s结构,在客户机上安装系统客户端,访问服务器使用所提供的各种服务。其中一项需求是由服务端控制客户端的界面显示元素以及其他某些功能。当客户端成功登陆服务器后,服务端返回客户端一个动态的配置信息,客户端根据这些配置信息展现其界面。需求要求不同的用户或者用户群可以采用不同的配置,如某个地区中使用某种操作系统的用户使用哪个配置,某个客户端版本号的又使用哪种配置,并且配置的界面内容可能根据不同的用户个性化显示,如需要显示
"XX先生你好,你的余额是X元",
这些配置信息的分发以及信息的内容都是随时可能发生变化的,如果将这些规则都硬性编码的话,将导致系统的不可维护。
实现:
如下发的内容为:
<message><%=customer.getName()%>您好,现在时间是${currentDateTime},您剩余的余额为${userBalance}</message>
首先使用脚本判断该配置能否下发给某个客户,然后在根据信息内容以及参数个性化配置信息
在java中调用jython脚本范例代码如下:
import org.python.core.PySystemState;
import org.python.util.PythonInterpreter;
public class Test {
public static void main(String[] args) {
//初始化客户对象
Customer customer = new Customer();
customer.setAddress("西安");
customer.setAge(23);
customer.setName("shangqiao");
customer.setSex(SexType.Male);
customer.setTerminalVersion("1.2.4");
customer.setOs("Win XP");
//脚本代码
StringBuffer scriptSource = new StringBuffer();
//result存储执行结果
appendLine(scriptSource, "result=0");
//判断语句
appendLine(scriptSource,
"if customer.getOs()=='Win XP' and customer.getTerminalVersion()=='1.2.4':");
//jython和python类似,格式必须良好,因此必须加上'/t'字符
appendLine(scriptSource, "/tresult=1");
appendLine(scriptSource, "else:");
appendLine(scriptSource, "/tresult=0");
PySystemState.initialize();
PythonInterpreter interp = new PythonInterpreter();
//将java对象传入jython
interp.set("customer", customer);
//执行脚本
interp.exec(scriptSource.toString());
//得到脚本执行后result的值
System.out.println(interp.get("result"));
//修改客户信息
customer.setTerminalVersion("1");
interp.set("customer", customer);
interp.exec(scriptSource.toString());
System.out.println(interp.get("result"));
}
public static void appendLine(StringBuffer source, String line) {
source.append(line).append("/r/n");
}
//
public enum SexType {
Male, Female
}
//
public static class Customer {
private String name;
private int age;
private SexType sex;
private String address;
private String terminalVersion;
private String os;
public String getOs() {
return os;
}
public void setOs(String os) {
this.os = os;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public SexType getSex() {
return sex;
}
public void setSex(SexType sex) {
this.sex = sex;
}
public String getTerminalVersion() {
return terminalVersion;
}
public void setTerminalVersion(String terminalVersion) {
this.terminalVersion = terminalVersion;
}
}
}
在实现中,一组配置信息可以和多个jython判断脚本绑定,如果某一个脚本返回的result==1则说明该用户可以使用该组配置,将该组配置的所有配置元素加在用户的配置信息列表中,继续测试其他配置组,最终得到该用户可以使用的完整的配置,然后根据配置信息中的内容,得到内容中的所有jython变量(多行jython语句"<%=%>"包围)和jython语句(单行jython语句,"${}"包围),调用这些变量所对应的jython脚本。
如得到jython语句"customer.getName()",调用jython代码并用返回值替代<%=customer.getName()%>
执行变量currentDateTime对应的jython脚本,使用代码执行后的result值替换${currentDateTime}。
最终将结果返回给客户端,客户端根据这些信息显示用户界面
注:
因为在jdk1.6中已经加入了脚本支持,并且jdk发布包还包含了javascript脚本的引擎实现,所以上面的代码也可以利用规范的写法:
public static void main(String[] args) throws ScriptException,
NoSuchMethodException {
Customer customer = new Customer();
customer.setAddress("西安");
customer.setAge(23);
customer.setName("shangqiao");
customer.setSex(SexType.Male);
customer.setTerminalVersion("1.2.4");
customer.setOs("Win XP");
StringBuffer scriptSource = new StringBuffer();
/*
var result=0;
if (customer.getOs()=='Win XP' && customer.getTerminalVersion()=='1.2.4')
{
result=1;
}
else
{
result=0;
}
*/
appendLine(scriptSource, "var result=0;");
appendLine(scriptSource,
"if (customer.getOs()=='Win XP' && customer.getTerminalVersion()=='1.2.4')");
appendLine(scriptSource, "{");
appendLine(scriptSource, "/tresult=1;");
appendLine(scriptSource, "}");
appendLine(scriptSource, "else");
appendLine(scriptSource, "{");
appendLine(scriptSource, "/tresult=0;");
appendLine(scriptSource, "}");
System.out.println(scriptSource);
//支持的所有脚本引擎
ScriptEngineManager factory = new ScriptEngineManager();
for (ScriptEngineFactory available : factory.getEngineFactories()) {
System.out.println(available.getEngineName());
}
//得到javascript引擎
ScriptEngine engine = factory.getEngineByName("JavaScript");
engine.put("customer", customer);
engine.eval(scriptSource.toString());
System.out.println(engine.get("result"));
customer.setTerminalVersion("1");
engine.put("customer", customer);
engine.eval(scriptSource.toString());
System.out.println(engine.get("result"));
scriptSource = new StringBuffer();
//支持方法调用,可以将脚本写为一个方法
/*
function canUseTheConfig(customer){
var result=false;
if (customer.getOs()=='Win XP' && customer.getTerminalVersion()=='1.2.4')
{
result=true;
}
else
{
result=false;
}
return result;
}
*/
appendLine(scriptSource, "function canUseTheConfig(customer){");
appendLine(scriptSource, "/tvar result=false;");
appendLine(scriptSource,
"/tif (customer.getOs()=='Win XP' && customer.getTerminalVersion()=='1.2.4')");
appendLine(scriptSource, "/t{");
appendLine(scriptSource, "/t/tresult=true;");
appendLine(scriptSource, "/t}");
appendLine(scriptSource, "/telse");
appendLine(scriptSource, "/t{");
appendLine(scriptSource, "/t/tresult=false;");
appendLine(scriptSource, "/t}");
appendLine(scriptSource, "/treturn result;");
appendLine(scriptSource, "}");
System.out.println(scriptSource);
engine.eval(scriptSource.toString());
Invocable invocable = (Invocable) engine;
//调用方法,传入方法名字和调用参数
System.out.println(invocable.invoke("canUseTheConfig",
new Object[] { customer }));
customer.setTerminalVersion("1.2.4");
System.out.println(invocable.invoke("canUseTheConfig",
new Object[] { customer }));
}