基于 Jython 的大型应用系统动态实现

源自:http://www.ibm.com/developerworks/cn/java/j-lo-jythonconfig/


引言

伴随信息时代的的迅猛发展,互联网已经全面渗透到电子商务,金融,电信,物流,资源等各个领域并逐渐成为其不可或缺的一部分。

信息时代,时间就意味着金钱。伴随着各种大型应用系统的普及,人们对于系统的稳定性,对与零宕机硬件不断的提出更高要求,却往往会忽视应用系统自身升级所带来的问题。


什么是 Jython

Jython 是一种完整的语言,而不是一个简单的 Java 翻译器或 Python 编译器,它是 Python 在 Java 中的完整实现。由于 Jython 继承了 Java 和 Python 二者的特性从而使其显得很独特。

看到这里读者一定会问,那 Jython,Java 以及 Python 之间的关系到底是怎么样的呢?其实,我理解 Jython 说简单一些,就是用 Java 语言实现的 Python 解释器,这种关系也就意味着你可以用 Python 语言编写程序而同时使用 Java 库。

在这里我不想大篇幅的阐述 Jython 是什么,如果各位读者感兴趣,请参阅 Jython 介绍

发现问题

大型的应用系统从不同的应用层面大致可以分为 :

  • UI 层

该层应该说是比较稳定的,或者说它的变化对于整个系统的运行不会带来特别严重的影响。

  • 接口层

该层与上层的 UI 以及底层的实现,都有着紧密的联系,所谓牵一发而动全身。作为一位大型应用系统的架构师或是系统设计人员,往往会出于维护成本以及开发成本的考虑,尽可能会让这个层面的设计与定义相对灵活。

  • 实现层

该层可以说是系统最核心的层面,整个系统的核心处理逻辑都会放在这一层面上。而往往该层,在实际使用过程中也是最不稳定的,最容易从需求角度发生变化。

  • 数据层

该层其实说简单一些就是数据库层,用于用户数据的查询、组装、更新等等,提供持久的数据服务,我们可以认为它也是个比较稳定的层面。

问题焦点似乎发现了,如何让我们的实现层更加灵活成为核心问题所在。那么如何能让我们系统的核心“与时俱进”呢?

分析问题

接下来,让我们看看一个系统的实现层都可能会干哪些事情:

  1. 接收通过接口传递过来的用户数据
  2. 依据一定的业务逻辑,处理用户数据
  3. 将处理结果更新到数据库中(可选)
  4. 将处理结果响应给 UI 层并渲染出来

分析到这里。我们对于实现层的内部数据流向又有了更进一步直观的认识。而我们对于实现层定义的所谓变数所在,也就比较明显了,如何实现业务处理逻辑的分离成为我们的目标。

解决问题

动态算法核心

本段以一个简单的 Python 方法为例,介绍如何借助 Jython 的动态执行能力来实现系统核心算法,以供 Java 代码调用。

Java 层代码片段

 PythonInterpreter interpreter=new PythonInterpreter();              
 PySystemState sys = Py.getSystemState(); 

 // 将 Jython 库加入系统的 classpath 中或直接通过这种方式动态引入
 sys.path.add("D:\\jython2.5.2\\Lib");  

 // 执行算法所在的 python 文件
 interpreter.execfile("impl/demo.py");

Jython 层代码片段

def getValue(o): 
    result = 0 
    if o < 2: 
      return -1 
    else: 
      for i in range(o): 
       		result +=i 
    return result

该方法很简单,当用户数据小于 2 时返回运算结果 -1,当用户数据大于等于 2 时,返回从 0 到输入数据的和。

Java 调用

 PyFunction pyFunction = (PyFunction)interpreter.get("getValue",PyFunction.class ); 

 System.out.println("Result: "+pyFunction.__call__(new PyInteger(100)));

通过 PyFunction 对象的内置方法 __call__() 传入用户数据。因为 Python 文件本身具备动态编译执行能力,所以我们只需要更新算法核心部分的 Python 代码,就可以达到动态改变系统底层算法的目的,而不需要更新 jar 包或重新部署应用而重启应用系统。

上边这个例子其实很简单,我们假设这个简单的 Jython 方法就是我们的业务逻辑,它会返回处理结果供上层渲染,但当业务逻辑发生了变化,比如当用户输入等于 2 时也要求返回值是 -1,此时对于这样一个简单的需求,我们只需要修改 Jython 代码就好了,并不需要更新 Java 层的东西。

动态配置文件

本段着重介绍,当系统中配置文件中若该项的数值需要发生变化时,我们如何来解决动态获取最新配置的问题。

配置文件 ( demo.properties )

假设我们为每一次查询结果定义了最大的返回数目为 100

 MaxValue=100 
…

Java 层代码片段

 PythonInterpreter interpreter=new PythonInterpreter();              
 PySystemState sys = Py.getSystemState(); 
 sys.path.add("D:\\jython2.5.2\\Lib"); 

 //‘re’是 Python 自身提供的正则表达式库,在 Python 方法中我们需要用到这个库中的方法,所以需要提前导入进来
 interpreter.exec("import re"); 

 interpreter.execfile("impl/demo.py"); 
 PyFunction pyFunction = (PyFunction)interpreter.get("getconfig",PyFunction.class );

Jython 层代码片段

import re
def getconfig(flag):
    result=''
    f=open('demo.properties','r')
    for i in f:
        g=re.findall(flag+'.*=.*',i)
        if len(g)>0:
            al=g[0].split('=')            
            result=al[1].strip()
            break    
return result

这个方法很简单根据我们传入的条目名称,获取配置文件中对应条目的定义数值。

Java 调用

System.out.println("Config value: "+pyFunction.__call__(new PyString("MaxValue")));

借助 Python 强大的文本处理能力,由 Python 作为 Java 代码和配置文件之间的桥梁,从而动态的获取最新配置项数值。

相比纯 Java 内存操作的实现方式,这种实现机制从运行速度上可能没有什么优势,但是却可以给我们带来一种新的理念。也许我们可以将这两种方式融合起来找到某种平衡,既不影响系统的运行效率又保留了 Python 动态的运行机制,达到双赢的目的。

动态后台实现

前边已经提到了系统接口定义层和实现层之间的关系。那么我们是否可以借助 Jython 来帮助我们动态实现定义的接口呢?答案很明显,当然可以。

这里我就以一个简单的例子,介绍一下如何借助 Jython 实现可插拔式的后台实现。利用 Jython 来实现 Java 定义的接口。从而可以被其他的 Java 方法调用。

Java 接口定义

 package com; 
 public interface Worker { 
 public String getFirstName(); 
    public String getLastName(); 
    public String getId(); 
    public 
				 void setId(); 
 }

上边是一个简单的人员接口,接口的实现类中我们就需要实现这里定义的 4 个方法。

Jython 层代码片段 ( 实现接口 )

 from com import Worker 
 class Worker(Worker): 
    def __init__(self): 
        self.first = "FirstName"
        self.last  = "LastName"

    def getFirstName(self): 
        return self.first 

    def getLastName(self): 
        return self.last 

    def getId(self): 
        return self.Id 

    def setId(self, newId): 
        self.Id = newId

我们在 Worker 类的构造函数中为该类的两个私有成员赋值,当调用 getFirstName() 或 getLastName() 方法时我们返回这两个私有成员的当前值。

Java 层代码片段

 Object javaObject; 
 PythonInterpreter interpreter=new PythonInterpreter();              
 PySystemState sys = Py.getSystemState(); 
 sys.path.add("D:\\jython2.5.2\\Lib");  
 interpreter.execfile("impl/demo.py"); 
 interpreter.exec("worker=Worker()"); 
 PyObject pyObject = interpreter.get("worker"); 
 pyObject.invoke("setId",new PyString("8888")); 
 try { 
     Class Interface = Class.forName(interfaceName); 
     javaObject = pyObject.__tojava__(Interface); 
 } catch (ClassNotFoundException e) { 
     e.printStackTrace(); 
 }

Java 调用

 Worker worker = (Worker) javaObject; 
 System.out.println("Name:"+worker.getFirstName()+worker.getLastName()); 
 System.out.println("ID:"+worker.getId());

看到这里就不必多说了,只需要动态的替换 Python 实现部分的代码,就可以实现可插拔式后台实现啦。而对于调用这个接口的 Java 对象来说,并不知道这个实现类是否发生了变化。

	/**
	 * <br>
	 * java直接执行python脚本(任意类型脚本)</br> <br>
	 * http://bbs.csdn.net/topics/390970924</br> <br>
	 * http://www.cnblogs.com/lmyhao/p/3363385.html</br> <br>
	 * http://bbs.chinaunix.net/thread-4185335-1-1.html</br>
	 * 
	 * @Title: runpythonbyexecfile
	 * @Description: <br>java直接执行python脚本</br>
	 * @param @param pythoncmd 设定文件
	 * @return String 返回类型
	 * @throws
	 */
	public String runpythonbyruntimeexec(String pythoncmd)
	{
		String defString = "result is:";
		if (null == pythoncmd || "".equals(pythoncmd))
		{
			defString += "error,the parameters filePath is empty or null!";
			return defString;
		}
		if (pythoncmd.matches("^python "))
		{
			defString += "error,the parameters filePath is error,it should be like ^python !";
			return defString;
		}
		Process pr;
		BufferedReader in;
		try
		{
			// String[] args = new String[] {"youpath2python\\python.exe","youscriptpath\\test.py","a","b","c","d" };
			// Process process = Runtime.getRuntime().exec(args);
			pr = Runtime.getRuntime().exec(pythoncmd);
			in = new BufferedReader(new InputStreamReader(pr.getInputStream()));
			String line;
			
			while ((line = in.readLine()) != null)
			{
				defString += line+"\n";
			}
			in.close();
			pr.waitFor();
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		finally
		{
			in = null;
			pr = null;
		}
		return defString;
	}


可参考:

http://blog.csdn.net/wodestudy/article/details/8190707


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值