- 浏览: 163984 次
- 来自: ...
文章分类
最新评论
-
hellostory:
不知所云………………
如何使用反射访问类的private域和方法 -
xusong_zidingyi:
如果在linux下面的话就没有办法运行了,因为linux没有办 ...
使用tesseract破解checkCode -
javajava22:
Errors accessing files. There m ...
使用tesseract破解checkCode -
suncathay:
qljobs 写道cmd.add("E:/Works ...
使用tesseract破解checkCode -
zuoxu128:
TIFFImageWriteParam ,这个类是哪个包的啊
使用tesseract破解checkCode
1 背景
在我们的项目中,有时候在需要运行时获取一段脚本并执行其逻辑以灵活地实现业务需求。有人的第一想法就是脚本语言,我们当然可以在程序中内嵌一个Python解释器,然后在需要灵活变动的地方使用Python脚本实现我们的逻辑。但是这样做太“重量级”了,况且身为一个Java程序员,你不一定懂得Python的语法,又或者你忠于Java根本不愿把项目交托给其他语言。如果能够使用符合Java语法的脚本,而且不必嵌入任何解释器,那么这是不是一个完美的选择?
2 原理
Java本身具有足够的灵活性能让我们做到这一点,JVM可以在运行时动态编译Java源文件然后加载类,这是Java脚本动态获得生命力的基础。不过,动态编译的输入是一个完整的Java类的源文件,和javac工具一样,而我们要执行的仅仅是一段脚本。另外,这段Java脚本还需要有上下文环境,例如输入和输出,不然凭空写一段脚本没有任何意义,这就要费一番心思去设计了。要满足这些要求也不难,这里提供一个简单的实现方法,基本思路是生成一个临时类,将上下文变量声明为该类的成员,将脚本放在该类的excute()方法里,然后将这个临时类动态编译并执行excute()方法,即可使脚本生效。对于程序来说,动态编译的过程是透明的,其结果是准确获得了脚本的输出。
3 具体示例
3.1 需求描述
程序从数据库中取出了别名和姓名,而在界面上显示怎样的名字却不确定,因为这点会经常变动,有些场合显示别名和姓名的组合,有些场合仅显示姓名等等,为了适应这种灵活变化,需要将如何显示名称的逻辑作为一段脚本写在外部文件里,程序在运行时执行这段脚本得到名称然后显示。
3.2 解决过程
假设程序从数据库中取出了别名和姓名,分别存入变量
String alias = “万里独行”; String name = “田伯光”;
并从外部文件读取了脚本内容
String script = “displayName = aliasName + ':' + originalName”;
从脚本里可以看到3个变量,分别是displayName、aliasName和originalName,这是我们默认提供给脚本的上下文变量环境,就像在Jsp页面里默认有request对象一样。
3.2.1 首先需要构造一个Java类的完整源代码
利用StringBuilder构造如下内容的字符串 String javaSource:
public class Temp { public String aliasName; public String originalName; public String displayName; public void excute() { displayName = aliasName + ‘:’+ originalName; } }
其中,excute()方法体的内容就是脚本内容,其余部分是固定的,将aliasName等3个变量声明为Temp类的成员变量,这样就为脚本代码构建了上下文环境(不然连编译都没法通过)。源代码内容构造完成之后,就要将它写入文件,文件名当然就是Temp.java了。
OutputStream os = new FileOutputStream("Temp.java"); os.write(javaSource.getBytes()); os.close();
这样,就在当前目录产生了一个我们需要的Java源文件。
3.2.2 然后编译这个Java源文件和加载编译后的class
编译源文件:
String[] compileArgs = new String[] {"Temp.java"}; com.sun.tools.javac.Main.compile(compileArgs);
注意,使用com.sun.tools.javac.Main类必须为项目添加tools.jar包的引用,这个jar包在JDK根目录下的lib文件夹里,自Java1.4开始包含。如果你的项目用手工编译,则必须添加类路径参数 “-cp tools.jar”。
加载编译后的class:
URLClassLoader loader = new URLClassLoader(new URL[]{new File(".").toURI().toURL()}); Class<?> scriptClass = loader.loadClass("Temp");
3.2.3 然后向临时变量注入上下文变量环境
虽然刚才构造的Java源代码里面已经有了aliasName和originalName两个成员变量提供给脚本代码使用,但是这两个变量并没有实际意义的值,实际的值应该是从数据库里取出来的值,我们要想办法将这个值传递给脚本。
Object obj = scriptClass.newInstance(); c.getDeclaredField("aliasName").set(obj, alias); c.getDeclaredField("originalName").set(obj, name);
用类反射的方法将我们之前从数据库里取出来的别名和姓名赋值给Temp的实例obj,这样当执行脚本的时候,脚本里面所引用的aliasName和originalName就是实际中应用的值了。
3.2.4 最后是执行脚本代码并获得显示名称
c.getDeclaredMethod("excute").invoke(obj); String displayName = (String)c.getDeclaredField("displayName").get(obj);
用类反射执行excute()方法实际上就是执行了外部脚本的逻辑,而外部脚本已将显示名称构造完成,所以取出临时变量的displayName属性即是我们需要的显示名称。这样,我们就实现了在运行时获取并执行外部Java脚本的需求,在一些场合下大大提高了应用程序的灵活性。当然,为了提高性能,不可能让程序每次都进行动态编译,可以将临时对象缓存起来,仅当脚本变化后才重新编译,不过这不属于本文的范畴,留待以后讨论。
4 改进
4.1 避免源文件冲突
我们可以通过构造唯一的路径或文件名来保证生成的Java源文件的唯一性,但是复杂的路径会增加后续编译和加载的复杂度,所以最好还是使用唯一的文件名。文件名可以用前缀+UUID的方式构造,也可以通过Java提供的临时文件来构造,临时文件能够保证唯一性。
//在当前目录构造前缀为Temp,扩展名为.java的临时文件 File.createTempFile("Temp", ".java", new File("."));
4.2 编译参数增强
Main.compile()的参数是一个String数组,它支持的参数和javac工具支持的参数一致,只要将javac命令行的每一个单词依次作为数组的元素即可,例如
String[] compileArgs = new String[] { "-d", “.”, “-cp”, “.;xx1.jar;xx2.jar”, "Temp.java"};
4.3 简化构造的源代码
如果脚本的上下文变量环境较多,那么构造源代码的过程就变得复杂低效易错,我们可以用继承的方式来消除这种复杂性,而且还可以用”多态”来代替一些类反射的使用,例如定义以下类:
public abstract class ScriptContext { public Object context1; public Object context2; //……其他上下文变量 public void excute() {} }
有了这样一个抽象基类,我们构造的源代码只需要继承ScriptContext,然后覆盖excute()方法即可,而执行脚本的过程也可以直接调用ScriptContext.excute(),而不必通过类反射定位到excute()方法然后执行。在新的思路下我们刚才构造的源代码可能变成这样:
public class Temp { @Override public void excute() { displayName = aliasName + ‘:’ + originalName; } }
而执行脚本的过程也相应简化为:
Class<?> scriptClass = loader.loadClass("Temp"); ScriptContext obj = (ScriptContext) scriptClass.newInstance(); obj. aliasName = alias; obj. originalName = name; //上下文变量属于父类 obj.excute();//利用多态调用覆盖的excute()函数 String displayName = obj. displayName;//直接获得脚本的输出结果
本文转载自:http://blog.csdn.net/tanjiazhang/archive/2010/01/05/5139095.aspx
附完全代码:
import java.io.*; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.URLClassLoader; public class Test { public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, NoSuchFieldException, InstantiationException{ String javaSource="public class DynaScript { public String aliasName; public String originalName; public String displayName; public void excute() { displayName=aliasName+\":\"+originalName;}}"; OutputStream os = new FileOutputStream("DynaScript.java"); os.write(javaSource.getBytes()); os.close(); String[] compileArgs = new String[] {"DynaScript.java"}; com.sun.tools.javac.Main.compile(compileArgs); URLClassLoader loader = new URLClassLoader(new URL[]{new File(".").toURI().toURL()}); Class<?> scriptClass = loader.loadClass("DynaScript"); Object obj = scriptClass.newInstance(); scriptClass.getDeclaredField("aliasName").set(obj, "alias"); scriptClass.getDeclaredField("originalName").set(obj, "name"); scriptClass.getDeclaredMethod("excute").invoke(obj); String displayName = (String)scriptClass.getDeclaredField("displayName").get(obj); System.out.println(displayName); } }
发表评论
-
一些随想
2012-06-01 22:05 0需求 idea 方案 执 ... -
Redis的安装与使用
2011-10-24 20:04 1239今天看了下redis相关的东东,做个备忘。 Re ... -
Redis的安装与使用
2011-10-24 20:00 0Redis 服务端: http://cocos.iteye. ... -
GenericDAO
2011-07-31 14:49 0GenericDAO -
loadfromproperties
2011-07-31 14:48 0loadfromproperties -
resultcode
2011-07-31 14:47 0resultcode -
berkeleydb的使用
2011-10-24 20:05 1808在napoli的代码中发现这个berkeleydb,版本为3. ... -
几种简单的Load Balance方法
2011-07-31 13:44 0以下代码出自napoli (1)定义Selec ... -
参数断言工具
2011-07-31 12:58 912见代码: public final class Argume ... -
如何检测线上代码的运行情况---BTrace使用分享
2011-04-10 17:36 2045(1)你是否有时发现线上的代码运行效率不高,但却无法定位到底具 ... -
napoli和JMS,mq的一些知识
2010-11-26 21:01 01 jms和mq (1)jms activeMQ 应用 h ... -
Taglib基础知识
2010-11-26 12:41 0浅论taglib设计 : http://wenku.bai ... -
java编码问题的一些文章
2010-11-11 17:30 0http://www.iteye.com/topic/8047 ... -
URL在转码的过程中数据丢失
2010-10-25 14:52 0示例代码 import java.io.Unsuppo ... -
遍历大容量map的正确方法
2010-10-08 13:20 11080首先,遍历map有以下方法: import java.uti ... -
B2B-Code Review规范
2010-08-31 10:15 01.目标 凡事知其然还要知其所以然,我们首先需要知道什么 ... -
多线程书写及code review规范
2010-08-31 10:09 14131. 不允许将Calendar用于类的静态成员 ... -
域名白名单检查
2010-08-24 21:05 0首先准备一份白名单名为safe-domain.xml ,如下: ... -
如何使用反射访问类的private域和方法
2010-08-17 21:44 5091如何使用反射访问类的private域和方法?非常简单,由 ... -
FindBugs插件的安装与使用
2010-04-21 10:54 18701 什么是FindBugs FindBugs 是一个静态分析 ...
相关推荐
java android 执行脚本代码(java代码)
java 执行sql脚本 例子java 执行sql脚本 例子java 执行sql脚本 例子java 执行sql脚本 例子java 执行sql脚本 例子java 执行sql脚本 例子java 执行sql脚本 例子java 执行sql脚本 例子java 执行sql脚本 例子java 执行...
shell,bat脚本运行java程序, shell,bat脚本运行java程序,
运行bat文件,对IE一键设置ActiveX控件、JAVA脚本、活动脚本。win7及以上版本,最好采用右键点击,选择以管理员身份运行bat文件。
详细的linux shell脚本启动java代码类。
在Java中运行Perl脚本 JERL
Java调用Groovy,实时动态加载数据库groovy脚本,java读取mongoDB的groovy脚本,加载实时运行,热部署
在windows下,可以指定jdk路径,后台编译并运行java的脚本
NULL 博文链接:https://wangzijian777.iteye.com/blog/1141763
在装jenkins 服务器下放了所有的脚本,开发环境,测试环境,预发环境,生产环境的脚本,在jenkins中构建项目,执行本地脚本来启动远程java项目,重点:脚本都在jenkins这台服务器进行管理。
本章首先介绍如何把JavaScript解释器嵌入到一个Java6应用程序中,以及如何从该应用程序运行JavaScript脚本。然后,回过头来展示这些脚本如何直接脚本化Java对象。我们还将在第23章再次回到Java脚本化的话题,第23章...
作为一个开发期间的脚本管理工具,让开发者在开发期间享受JSI带来的种种便捷,也可以作为一个运行时的脚本管理框架,让类库编写者能够自己管理好自己编写的类库的相关依赖,让类库的最终用户从繁琐的依赖管理中解脱...
在java代码中调用执行shell脚本,sqlldr导数与使用sqlplus在shell调用执行存储过程。 linux环境中有2个dba的用户:oracle、erm 还有1个web用户:erm 在linux环境中,这三个用户都可以直接在任意目录下执行该shell...
脚本上有备注,将java项目打为jar包后,替换jar的包名为项目包名即可,log文件会自动生成,方便好用。
在集群上批量运行脚本, 入参有5个: status: |stop> filename:运行的文件名 servername:机器名前缀 startno:机器开始序号 endno:机器结束序号 请根据实际情况修改脚本内的java路径
NULL 博文链接:https://wangxc.iteye.com/blog/682229
在Java程序中调用Unix/Linux主机上的Shell命令,并返回相应执行结果。
Linux系统下的jdk解压及配置环境脚本 具体步骤: 1.使用chmod +x jdk.sh赋予可执行权限 2.确保在root环境下运行该脚本 3.将该脚本与*.tar.gz放于同一文件夹 4.输入文件名称时请不要输入.tar.gz
各位小伙伴肯定都遇到过拿到一个linux环境发现没有java...资源分为脚本和安装包两部分内容,放置在同一个目录下即可,备份好/etc/profile后为脚本赋予执行权限,直接执行即可! 感谢各位老铁!希望脚本对你们有帮助!
对javax.script包进行讲解,实现支持java动态嵌入执行groovy代码片段