Stix aasearchS Www search
Stix Tag search Stix search Stix Ja Tag asearch Tag Findmortgagehomemortgages Tag Www Www A Stix Isearch Stix Findmortgagehomemortgages Www
Findmortgagehomemortgages search Findmortgagehomemortgages Www Www search Www Stix Findmortgagehomemortgages Findmortgagehomemortgages search Stix Findmortgagehomemortgages Tag Www search Www 共searchsearch Tag Www Findmortgagehomemortgages Tag
Findmortgagehomemortgages Stix
Tag search Findmortgagehomemortgages search Findmortgagehomemortgages Www Tag search Www searchJ
va Findmortgagehomemortgages searchsearch Tag Stix Stix searchsearch
Stix
Findmortgagehomemortgages Stix
Www Www searchsearch Www Www Stix Www Www Stix Findmortgagehomemortgages Stix Stix searchsearch Www
Findmortgagehomemortgages Tag Findmortgagehomemortgages Stix Findmortgagehomemortgages searchsearchsearch Tag Stix
Stix Tag Stix search执行某些任务的情况下。但是,Java 脚本编程 API 不仅仅能够以一种统一的方式在 Java 程序中添加许多种脚本语言代码,它还支持在运行时寻找、读取和执行脚本。可以利用这些动态功能在程序运行时修改脚本,从而修改应用程序的逻辑。本文演示如何使用 Java 脚本编程 API 调用外部脚本来动态地修改程序逻辑。还要讨论在将一种或多种脚本语言集成到 Java 应用程序中时可能遇到的问题。
第 1 部分 用一个 Hello World 风格的应用程序介绍了 Java 脚本编程 API。这里将展示一个更真实的示例应用程序,这个程序使用脚本编程 API 创建一个动态的规则引擎,它可以以外部 Groovy、JavaScript 和 Ruby 脚本的形式定义规则。这些规则决定申请人是否符合某些抵押产品的住宅贷款条件。如果用脚本语言定义业务规则,规则就更容易编写,也便于非程序员(比如贷款审查员)阅读。通过使用 Java 脚本编程 API 将这些规则放在程序之外,还可以支持在应用程序运行时修改规则和添加新的抵押产品。
这个示例应用程序为虚构的 Shaky Ground Financial 公司处理住宅贷款申请。住宅抵押行业不断地推出新的贷款产品,还常常修改对合格申请人的限制规则。Shaky Ground 公司不但希望能够快速地添加和删除抵押产品,还需要快速修改业务规则,从而控制哪些人符合产品的贷款条件。
Java 脚本编程 API 正好能够满足这种需求。这个应用程序由一个 ScriptMortgageQualifier 类组成,这个类负责判断打算购买某一资产的贷款人是否符合给定的抵押贷款产品的条件。清单 1 给出这个类。
// Imports and Javadoc not shown.
public class ScriptMortgageQualifier {}
// Make params accessible to scripts by adding to engine's context.
scriptEngine.put("borrower", borrower);
scriptEngine.put("property", property);
scriptEngine.put("loan", loan);
// Make return-value object available to scripts.
MortgageQualificationResult scriptResult = new MortgageQualificationResult();
scriptEngine.put("result", scriptResult);
// Add an object scripts can call to exit early from processing.
scriptEngine.put("scriptExit", new ScriptEarlyExit());
try {} catch (ScriptException se) {}
// Set script result message if early-exit exception embedded.
Throwable t = se.getCause();
while (t != null) {}
t = t.getCause();
}
}
return scriptResult;
}
/** Returns a script engine based on the extension of the given file. */
private ScriptEngine getEngineForFile(File f) {}
/** Returns the file's extension, or "" if the file has no extension */
private String getFileExtension(File file) {} else {}
}
/** Internal exception so ScriptEarlyExit.exit can exit scripts early */
private static class ScriptEarlyExitException extends Exception {}
}
/** Object passed to all scripts so they can indicate an early exit. */
private static class ScriptEarlyExit {}
public void withMessage(String msg) throws ScriptEarlyExitException {}
}
} |
这个类相当简单,因为它把所有业务决策任务都委派给了外部脚本。每个脚本表示一个抵押产品。每个脚本文件中的代码包含一系列业务规则,这些规则定义了符合这种抵押产品要求的贷款人类型、资产类型和贷款类型。由于采用了这种方式,只需在脚本目录中添加新的脚本文件,就可以添加新的抵押产品。如果某一抵押产品的业务逻辑改变了,那么只需更新脚本来反映规则的变化。
通过用脚本语言编写抵押产品业务规则,可以展示 Java 脚本编程 API 的功能。这个程序还说明有时候脚本语言代码更容易阅读、修改和理解,即使是非程序员也可以掌握脚本代码。
ScriptMortgageQualifier 类的工作方式
ScriptMortgageQualifier 中的主要方法是 qualifyMortgage()。这个方法通过参数接受以下信息:
File 对象,其中包含要执行的脚本这个方法的任务是用业务实体参数运行脚本文件并返回一个结果对象,这个对象指出贷款人是否符合抵押产品的要求。这里没有给出 Borrower、Property 和 Loan 的代码。它们只是简单的实体类,可以在本文的源代码中找到它们的代码(见 下载)。
为了找到一个 ScriptEngine 来运行脚本文件,qualifyMortgage() 方法使用了 getEngineForFile() 内部 helper 方法。getEngineForFile() 方法使用 scriptEngineManager 实例变量(这个变量在类实例化时被设置为一个 ScriptEngineManager)寻找能够处理具有给定文件扩展名的脚本的脚本引擎。getEngineForFile() 方法使用 ScriptEngineManager.getEngineByExtension() 方法(见 清单 1 中的粗体代码)搜索并返回 ScriptEngine。
找到脚本引擎之后,qualifyMortgage() 将它接收的实体参数绑定到引擎的上下文,从而让脚本能够使用这些参数。前三个 scriptEngine.put() 调用(也是粗体代码)执行这些绑定。第四个 scriptEngine.put() 调用创建一个新的 MortgageQualificationResult Java 对象并通过脚本引擎共享它。脚本可以通过设置这个对象的属性将它的运行结果返回给 Java 应用程序,qualifyMortgage() 将返回这个共享对象。脚本使用 result 全局变量访问这个 Java 对象。每个脚本负责使用这个共享对象将自己的结果返回给 Java 应用程序。
最后一个 scriptEngine.put() 调用让脚本可以通过 scriptExit 变量使用一个内部 helper 类(ScriptEarlyExit,见 清单 1)的实例。ScriptEarlyExit 定义了两个简单的方法 —— withMessage() 和 noMessage(),它们惟一的作用是抛出一个异常。如果脚本调用 scriptExit.withMessage() 或 scriptExit.noMessage(),那么方法抛出一个 ScriptEarlyExitException 异常。脚本引擎会捕捉这个异常、终止脚本处理并向调用脚本的 eval() 方法抛出一个 ScriptException 异常。
通过以这种迂回的方式提前退出脚本,就可以以一致的方式从函数或方法外的脚本处理过程返回。并非所有脚本语言都提供了这种方式所需的语句。例如,在 JavaScript 中,在执行高层代码时(这个示例应用程序中的抵押处理脚本正是采用这种构造方式),无法使用 return 语句。共享对象 scriptExit 解决了这个问题,一旦脚本判断出贷款人不符合抵押产品的要求,用任何语言编写的脚本都可以通过这个对象退出。
在 qualifyMortgage 中,对脚本引擎的 eval 方法的调用(见粗体代码)使用一个 try/catch 块捕捉 ScriptException 异常。catch 块中的代码检查 ScriptException 错误消息,从而判断这个脚本异常是由 ScriptEarlyExitException 造成的,还是由真正的脚本错误造成的。如果错误消息包含名称 ScriptEarlyExitException,那么代码就认为一切正常并忽略这个脚本异常。
这种在 Java 脚本编程 API 的脚本异常错误消息中搜索字符串的技术有点儿笨拙,但这对于本示例中使用的 Groovy、JavaScript 和 Ruby 语言解释器是有效的。如果所有脚本语言实现将从调用的 Java 代码抛出的 Java 异常添加到异常堆栈中,那么会更方便,这样就可以使用 Throwable.getCause() 方法获取这些异常。JRuby 和 Groovy 等解释器会这样做,但是内置的 Rhino JavaScript 解释器并不这样做。
运行代码:ScriptMortgageQualifierRunner
为了测试 ScriptMortgageQualifier 类,将使用测试数据表示四个贷款人、贷款人打算购买的一项资产和一笔抵押贷款。我们将用一个贷款人、资产和贷款运行所有三个脚本,检查贷款人是否满足脚本所代表的抵押产品的业务规则。
清单 2 给出 ScriptMortgageQualifierRunner 程序的部分代码,我们将用这个程序创建测试对象、在一个目录中寻找脚本文件并通过 清单 1 中的 ScriptMortgageQualifier 类运行它们。为了节省篇幅,这里没有给出这个程序的 createGoodBorrower()、createAverageBorrower()、createInvestorBorrower()、createRiskyBorrower()、createProperty() 和 createLoan() helper 方法。这些方法的作用仅仅是创建实体对象并设置测试所需的值。在 下载 一节中可以获得所有方法的完整源代码。
// Imports and some helper methods not shown.
public class ScriptMortgageQualifierRunner {}
String dirName;
if (args.length == 0) {} else {}
scriptDirectory = new File(dirName);
if (!scriptDirectory.exists() || !scriptDirectory.isDirectory()) {}
run();
}
/**
* Determines mortgage loan-qualification status for four test borrowers by
* processing all script files in the given directory. Each script will determine
* whether the given borrower is qualified for a particular mortgage type
*/
public static void run() {}
}
/**
* Reads all script files in the scriptDirectory and runs them with this borrower's
* information to see if he/she qualifies for each mortgage product.
*/
private static void runQualifications(
ScriptMortgageQualifier mortgageQualifier,
Borrower borrower,
Loan loan,
Property property
) {} catch (FileNotFoundException fnfe) {} catch (IllegalArgumentException e) {} catch (ScriptException e) {}
if (result == null) continue; // Must have hit exception.
// Print results.
System.out.println(
"* Mortgage product: " + result.getProductName() +
", Qualified? " + result.isQualified() +
"\n* Interest rate: " + result.getInterestRate() +
"\n* Message: " + result.getMessage()
);
System.out.println();
}
}
/** Returns files with a '.' other than as the first or last character. */
private static File[] getScriptFiles(File directory) {} else {}
}
});
}
private static void waitOneMinute() {} catch (InterruptedException e) {}
}
}
|
ScriptMortgageQualifierRunner 中的 main() 方法搜索命令行上提供的脚本文件目录,如果这个目录存在,就用目录的 File 对象设置一个静态变量,并调用 run() 方法执行进一步的处理。
run() 方法对 清单 1 中的 ScriptMortgageQualifier 类进行实例化,然后用一个无限循环调用内部方法 runQualifications(),测试四个贷款人/贷款场景。这个无限循环模拟连续的抵押申请处理。这个循环让我们可以在脚本目录中添加或修改脚本文件(抵押贷款产品),这些修改会动态地生效,不需要停止应用程序。因为这个应用程序的业务逻辑放在外部脚本中,所以可以在运行时动态地修改业务逻辑。
对于脚本目录中的每个脚本文件,runQualifications() helper 方法分别调用 ScriptMortgageQualifer.qualifyMortgage 一次。每个调用前面有一系列打印语句,它们输出脚本文件和贷款人的相关信息;调用之后,用打印语句显示结果,即贷款人是否符合抵押产品的要求。脚本代码使用共享的
gWww Findmortgagehomemortgages Tag Stix Find Mortgage Home Mortgages 动态调用动态语言,第 2 部分: 在运行时寻找、执行和修改脚本n u Fast Find
gWww Findmortgagehomemortgages Tag Stix Find Mortgage Home Mortgages 动态调用动态语言,第 2 部分: 在运行时寻找、执行和修改脚本g y Find Mortgage Home Mortgages b Find Mortgage Home Mortgages j Find Mortgage Home Mortgages Find Mortgage Home Mortgages