【本文已迁移到“程序员文摘” http://programmerdigest.cn/category/lajp】新瓶装老酒--PHP结合Java构建Web应用的新思路

来源:互联网 发布:北京网络职业学院电话 编辑:程序博客网 时间:2024/04/28 00:41

【本文已迁移到“程序员文摘” http://programmerdigest.cn/category/lajp】

PHP和Java各自有着鲜明的特点:

 

PHP简单、易学,开发动态网页效率高,草根文化气息浓重,是语言世界的平民英雄。

 

Java强大而复杂,有众多高端功能,又有IMB、Oracle等企业大颚的追捧,是语言世界的贵族王子。

 

不管是平民英雄还是贵族王子,都不满足在各自领域称王,于是平民英雄祭出LAMP大旗,在PHP5新的对象模型帮衬下,硬要往上层阶层闯一闯,无奈金融、电信等王侯们不大欢迎;而贵族王子也非常重视WEB这个广大的基层社会,先制定Servlet/JSP法令,跟着又推出struts、JSF、Taglib、JSTL、Spring等系列政策,试图消除阶级等级概念,但实施手法贵族气太浓无法深得民心。

 

平民就是平民,贵族就是贵族,硬要将自己也视为对方阵营里的一员,只有一个结果:牵强。

 

-----------------------------------------

 

PHP结合Java是早已有的想法,PHP负责WEB层,Java负责业务和数据逻辑层,真是一对黄金组合。但PHP和Java是两个不同的语言,如何将他们结合在一起是个问题。实际上已经有了一些手段:如PHP中内置了调用Java的方法,开源的也有php-java-bridge,这里介绍的一个新的解决思路。

 

从操作系统层面观察: PHP和Java是系统中运行的不同进程,他们之间沟通属于进程间通信技术(IPC):

 

 

在传统的Unix环境中,IPC技术有:管道、消息队列、共享内存、信号量等,在当今TCP/IP流行的大背景下,IPC已经鲜有耳闻了,但在同一个系统中,IPC通信比基于TCP的socket通信在性能、资源占用方面有相当大的优势,所以今天我们重新挖掘IPC这门传统工艺,让它还发出新的光芒。

 

本文介绍的思路即是使用IPC技术来结合PHP和Java。

 

在Unix/Linux中,PHP基本提供了所有IPC的访问接口,这点不难想象,因为PHP可以看作是以C语言为核心的一个壳,而IPC是系统内核的组成部分,对外提供了一组C函数接口,因此PHP可以非常顺畅的运用IPC技术。而Java没那么幸运,为了追求夸平台性(Windows的IPC技术和Unix的不同),没有提供系统级的IPC访问,这也体现了Java的文化特色:为追求统一可牺牲效能。

 

好在Java有和C对接的JNI接口,可利用它来实现我们的设想,最后的思路如下图:

 

 

从上图中看出,PHP承担HTTP层的职责,而Java承担业务层的职责,他们通过System V Message Queue(消息队列,进程间通讯IPC中的一种)相互沟通,Java需要JNI的支持。

 

------------------------------------------------

 

通信问题解决了,下来就需要考虑通信内容的问题了。PHP和Java各有其语言内部定义的数据类型,当PHP数据传送到Java,或Java数据传送到PHP时,怎样进行转换呢? 很多人马上会想到使用xml,xml确是一种夸平台、能够很好描述对象模型的数据封装技术,但xml体积大传输速率慢,通讯两端解析也比较麻烦。

 

实际上,无论是PHP还是Java,在语言设计时都考虑了数据怎样传输和存储的问题,那就是序列化,例如EJB调用就是通过Java的序列化对象来传输数据的。

 

PHP的序列化非常巧妙,例如:

 

整形: $i = 5 序列化后是 i:5

字符串: $s = "abcd" 序列化后是 s:4:"abcd"

数组:$arr = array("a" => "value1", "b" => "value2") 序列化后是 a:2:{s:1:"a";s:6:"value1";s:1:"b";s:6:"value2";}

对象: class aaa{var $attr1 = 5; var $attr2 = "ABCD";} 序列化后是 O:3:"aaa":2:{s:5:"attr1";i:5;s:5:"attr2";s:4:"ABCD";}

 

简单而明了,不仅描述了数据结构,并带有数据原来的类型,问题得以解决。使用PHP序列化数据作为PHP和Java的通讯数据格式,不仅效能高,而且更加接近语言本意。

 

------------------------------------------------

 

开源项目LAJP就是以上述思想为核心的实现技术,使用起来也非常简单,下面是一个示例程序:

 

    * php端程序
<?php
  require_once("../lajp/php_java.php"); //LAJP提供的程序脚本

  //php类,映射到JavaBean类:cn.com.ail.test.Bean
  class cn_com_ail_test_Bean
  {
    var $a = "v1";
    var $b = "v2";
  }

  $p1 = "a";

  $p2 = array(); //数组有两个元素,没有规定数组下标(默认下标是int类型0和1),映射到java的java.util.List类型
  $p2[] = 10;
  $p2[] = 20;

  $p3 = new cn_com_ail_test_Bean;

  //调用java服务
  //lajp_call是“php_java.php”脚本提供的函数
  //参数cn.com.ail.test.Objtest::method1表示调用java的cn.com.ail.test.Objtest类中的method1方法
  //后面的$p1,$p2,$p3是向method1方法传递的参数。
  $ret = lajp_call("cn.com.ail.test.Objtest::method1", $p1, $p2, $p3);

  echo "返回信息:".$ret;    //打印"OK,收到并返回字符串应答"
?>


    * java端程序

  //对应php中$p3的JavaBean
  package cn.com.ail.test;
  public class Bean
  {
    private String a;
    private String b;
       
    public String getA()
    {
      return a;
    }
    public void setA(String a)
    {
      this.a = a;
    }
    public String getB()
    {
      return b;
    }
    public void setB(String b)
    {
      this.b = b;
    }
  }

  //java端服务
 
  package cn.com.ail.test;
  public class Objtest
  {
    //服务方法,只是简单的打印入参并返回简单字符串
    public static final String method1(String param1, java.util.List param2, Bean param3)
    {
      System.out.println("$p1=" + param1);
      for (int i = 0; i < param2.size(); i++)
      {
        System.out.printf("$p2[%i]=%i/n", i, (Integer)param2.get(i));
      }
      System.out.println("$p3->a=" + param3.getA());
      System.out.println("$p3->b=" + param3.getB());

      //返回
      return "OK,收到并返回字符串应答";
    }
  }

 

LAJP的相关资料,可查看其网站:http://code.google.com/p/lajp/

 

 

原创粉丝点击