admin管理员组

文章数量:1794759

LAJP(Linux Apache Java Php)

LAJP(Linux Apache Java Php)

分享一个金融、电信领域的web环境:

   LAJP名称含义 LAJP名称来源于著名的LAMP(Linux,Apache,Mysql,Php),LAMP是轻量级的开发Web程序的环境,在Internet上有广泛的应用,但对于企业开发,如金融、电信领域,LAMP略显能力不足,而这些领域通常是Java(J2EE)的势力范围。LAJP是将LAMP的简便性和Java高端能力结合起来的一项技术,LAJP中的J指的是Java,由于数据库厂商对Java的广泛支持,数据库也不再特别限制为Mysql。 LAJP可以理解为PHP和Java相结合的技术,也可称为PHP和Java混合编程技术,或者PHP调用Java服务的技术,也有人习惯称之为前台PHP后台Java的技术框架。 特点
  • 优势互补: PHP是非常流行的WEB编程脚本语言,有易学、易用、开发部署效率高的特点,非常适合网页编程;JAVA适合编写具有复杂的业务功能和数据的程序,二者结合可发挥各自优势。
  • 高效稳定:Apache+PHP组合可带来优异的WEB服务稳定性,而JAVA可补充如连接池、事物管理、分布式、对象模型等高端特性。
  • 创新的通信机制 PHP和Java间的通讯方式采用系统消队列和Socket两种机制,兼顾通讯效率和平台兼容性。
  • 数据类型自动转换机制 PHP数据和Java数据可准确地自动匹配和转换,无须程序员编写解析代码。
  • 易用:LAJP安装配置简单,PHP端和JAVA端编程符合各自的编程习惯。
  • 轻量级:LAJP架构非常轻量级,除了最基本的PHP和Java环境,不需要任何扩充的、第三方的组件、容器。

LAMP和LAJP的简要对比 LAMP从传统技术架构上看属于2层结构,虽然在php5以后增强了面向对象的能力,有了形成业务逻辑层的语言基础,但对于复杂的企业级WEB应用,php语言能力仍显不足。LAJP继承了LAMP在WEB领域编程的活力,并用java构建业务逻辑层,通过“PHP调用Java的方法”来实现二者间的互通。

php和java的互通

php和java是两种不同的语言,在LAJP架构中二者之间的互通有两种机制。

  • 一、消队列

以操作系统的消队列为沟通媒介,在通讯过程中php作为客户端调用java端服务。消队列属于IPC技术(进程间通讯),php语言中内置了一组函数(msg_send、msg_receive等)可以和System V消队列通讯,而java中没有相似的方法,因此通过调用底层JNI接口使用C函数来实现。 使用消队列有以下好处:

  • 使php和java保持独立性
  • 有极高的传输速度,大于socket
  • 相对于socket方式,Java服务端只向本机提供服务(没有对外侦听端口),相对安全,易于管理。
    • 二、Socket

    消队列技术只能适用于Unix/Linux/BSD系统,因此LAJP提供基于TCP/IP的通讯机制,从而适应各种平台。

    数据类型转换

    PHP和Java各有其语言内部定义的数据类型,当PHP数据传送到Java,或Java数据传送到PHP时,LAJP在内部自动地、准确地对他们进行转换,程序员无需进行任何的解码工作。

    详细参看《lajp数据转换示例》 code.google/p/lajp/wiki/Example

    示例

    示例程序表现了一个简单的PHP调用Java的程序片段,PHP在调用过程中向Java传递了3个参数,参数类型分别是字符串、数组、对象,Java服务方法返回字符串应答。

    • php端程序
      require_once("php_java.php"); //LAJP提供的程序脚本   //php类,映射到JavaBean类:cn.ail.test.Bean   class cn_com_ail_test_Bean   {     var $a = "v1";     var $b = "v2";   }   $p1 = "a";     //字符串,传给Java方法的第一个参数   $p2 = array(); //数组,传给Java方法的第二个参数   $p2[] = 10;   $p2[] = 20;   $p3 = new cn_com_ail_test_Bean; //php对象,传给Java方法的第三个参数   //"lajp_call"是LAJP提供的函数,用来调用java端服务   //"cn.ail.test.Objtest::method1"表示调用java的cn.ail.test.Objtest类中的method1方法   //"$p1,$p2,$p3"是向method1方法传递的3个参数。   $ret = lajp_call("cn.ail.test.Objtest::method1", $p1, $p2, $p3);   echo "返回信:".$ret;    //打印"OK,收到并返回字符串应答"
    • java端程序
      //对应php中$p3的JavaBean(普通的JavaBean)   package cn.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.ail.test;   public class Objtest   {     //PHP调用的Java方法(普通的Java方法,LAJP仅要求声明为public static final)     //php传来的三个参数自动转换为相应的Java数据类型     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());       //返回给PHP的应答字符串       return "OK,收到并返回字符串应答";     }   } LAJP帮助文档

    LAJP是用来解决PHP和Java通讯的一项技术,在PHP中可以通过"正常"的PHP函数来调用Java的一个方法,如同下面的一个例子:

    java(service):

    package aaa.bbb.ccc; public class MyClass {   public static final int addMethod(int a, int b)   {     return a + b;   } }

    php(client):

    $ret = lajp_call("aaa.bbbc.MyClass::addMethod", 10, 20); echo $ret;  //30

    LAJP有两个核心能力:

  • PHP优雅、高效地调用Java方法的能力
  • PHP数据和Java数据合理、自动地转换的能力
  • 在LAJP的当前版本中,使用两种技术进行PHP和Java间的通信,我对它们分别命名为: 消队列模式 和 socket模式 。它们各自有优缺点,在使用中应根据程序所在环境特点加以选择:

    • 消队列 以System V的消队列作为PHP和Java间的通信媒介,优点是理论速度快,占用资源较小;缺点是只能使用在支持System V的系统中,可运用于大多数的Unix/Linux/BSD系统,但不能用于windows。
    • socket 以TCP/IP作为PHP和Java间的通信媒介,优点是基本无系统限制;缺点是理论速度慢,占用资源较大。

    一、LAJP运行环境要求

    "消队列模式"和"socket模式"对运行环境的要求是不同的,下面分别加以阐述:

    消队列模式

    环境需要满足System V消队列的运行:

    • 系统 目前常见的Unix/Linux系统都可满足php(Apache)、java的运行,其中大部分默认支持System V消队列。
    • php php需要通过消队列和java进程通信,按php的说明,php在4.3.0版本以后支持System V消队列。
    • apache 无特殊要求,满足php要求即可。
    • java java版本在1.5以后。

    • 在Unix/Linux环境中,推荐使用消队列模式。

    socket模式
    • 系统 没有限制,很难找到不支持TCP/IP的系统。
    • php 按php的说明,php版本>=4.1.0支持socket
    • apache 无特殊要求,满足php要求即可。
    • java java版本在1.5以后。
    • Windows系统只能使用socket模式

    在开发过程中可以同时使用这两种模式,比如一般开发者使用Windows环境,而程序部署在Linux系统中,LAJP在模式的配置上和编码无关。

    二、LAJP安装与运行 Windows下的LAJP安装配置

    请阅读 《图解LAJP在Windows系统上的安装配置》

    Unix/Linux下的LAJP安装配置

    • 下载 下载Lajp的安装文件,解压后目录结构如下:

      lajp安装包   |   |--jin                      //消队列模式必要的JNI源程序   |   |   |   |--lajp_MsgQ.h   |   |--lajp_MsgQ.c   |   |--make.sh   |     |--php                       //PHP端脚本   |   |   |   |--php_java.php.msgq   |   |--php_java.php.socket   |     |--test_service/             //Hello World 示例服务程序   |     |--lajp-10.05.jar            //LAJP主程序   |--run_msgq.sh               //Unix/Linux使用消队列模式启动脚本   |--run-socket.bat            //windows使用启动脚本   |--run-socket.sh             //Unix/Linux使用socket模式启动脚本
    Unix/Linux中运行LAJP依赖以下前提设置
    • Apache+php环境 部分发行版本的php默认安装不支持消队列(System V messages)、信号量(System V semaphore)、共享内存(System V shared memory), 如使用消队列模式需在编译php时附带编译选项 --enable-sysvsem,--enable-sysvshm和--enable-sysvmsg;如使用socket模式则要检查sockets是否激活,这些可以通过phpinfo()函数来观察。
    • java环境 要求Java5.0以上。
    Unix/Linux中socket模式的配置运行

    Socket模式使用run-socket.sh脚本,运行前确保run-socket.sh有执行权限,在脚本内部可以配置Java服务端口(默认21230),PHP和Java传输字符集(默认UTF-8),classpath等。

    Unix/Linux中消队列模式的配置运行
    • 首先配置好c语言编译环境
    • 编译JNI 将下载的lajp安装包中的3个源代码文件:lajp_MsgQ.c,lajp_MsgQ.h,make.sh复制到某个目录,确保make.sh有执行权限,按注释要求编辑make.sh #!/bin/sh # ----------------------------------------------------------- #  LAJP-JNI 编译脚本 (2009-09 code.google/p/lajp/) #   #  编译环境: Unix/Linux #   #  源文件: lajp_MsgQ.c lajp_MsgQ.h #  目标文件: liblajpmsgq.so #  编译参数: #    --share  : 编译为动态库 #    -I       : 搜索编译JNI需要的.h文件, 注意"/usr/lib/jvm/java-6-sun/"要换成编译环境中 #               的JAVA_HOME路径 # #  liblajpmsgq.so发布 : #    复制到<java.library.path>中,可通过java程序 #    System.out.println(System.getProperties().getProperty("java.library.path")); #    获得本机的<java.library.path> # ----------------------------------------------------------- gcc lajp_MsgQ.c --share -I. -I/usr/lib/jvm/java-6-sun/include -I/usr/lib/jvm/java-6-sun/include/linux -o liblajpmsgq.so
    • 编译 : 执行 ./make.sh
    • 部署编译好的liblajpmsgq.so库文件: 如果编译成功,会生成liblajpmsgq.so文件,将它复制到任一个"java.library.path"路径中,"java.library.path"路径可以通过java程序侦测: System.out.println(System.getProperties().getProperty("java.library.path"))

    消队列模式使用run_msgq.sh脚本,运行前确保run_msgq.sh有执行权限,在脚本内部可以配置PHP和Java传输字符集(默认UTF-8),classpath等。

    三、LAJP使用注意事项
    • Java Java方法如果要做为LAJP的服务方法,必须声明为 public static final
    • 数据类型 PHP和Java通过LAJP传输的数据,包括PHP调用时向Java传递的参数,和Java方法的返回值,都需要遵循LAJP数据类型要求。

    php语言规范定义了8中数据类型:boolean、int、float、string、array、object、resource、NULL;java语言的数据类型分为2类:基本数据类型和对象类型,基本数据类型有byte、short、int、long、 char、boolean、float、double, 对象类型包括数组、集合、javaBean等。在LAJP架构中,php数据以参数形式传递给Java方法,Java方法的返回值再回传给php调用程序,在调用过程中,php数据“自动”转换为Java数据,反之亦然。

    并不是所有数据类型都可以转换,在LAJP中建立了以下转换规则:

    表1

     phpjava说明
    布尔booleanboolean 
    整形intint 
    浮点floatdouble在php中float和double含义相同
    字符串stringjava.lang.String 
    顺序集合array(key:int)java.util.Listphp中array的每个元素的key类型必须是int
    key-value集合array(key:string)java.util.Mapphp中array的每个元素的key类型必须是string
    对象objectJavaBean 
    NULLnull 

    详细的数据转换规则请查阅 《LAJP数据转换示例》

    四、LAJP基本配置

    LAJP只是单纯的PHP和Java传输的中间机制,像Web系统中常见的数据库连接池、JNDI、缓存等需要开发者自己管理。

    • 消队列配置

    对于消队列,有三个系统配置影响其性能:

  • MSGMNI 指定系统中消队列最大数目
  • MSGMAX 指定一个消的最大长度
  • MSGMNB 指定在一个消队列中最大的字节数
  • 一般性的,Linux系统的默认消队列配置非常可怜,通过查看下面三个文件获得系统配置信:

    • /proc/sys/kernel/msgmni 缺省设置:16
    • /proc/sys/kernel/msgmax 缺省设置:8192
    • /proc/sys/kernel/msgmnb 缺省设置:16384
    为了更好的性能,可编辑 /etc/sysctl.conf 文件,修改缺省配置:

    # /etc/sysctl.conf # set message queue 20M kernel.msgmnb = 20971520 kernel.msgmni = 20480
    • socket侦听端口配置

    在socket模式中,Java端默认的侦听端口是21230,如要修改,有两处:

    • php_java.php : 在下载包中命名为php_java.php.socket define("LAJP_IP", "127.0.0.1");                 //Java端IP define("LAJP_PORT", 新的端口);                      //Java端侦听端口
    • run-socket启动脚本 : windows中使用run-socket.bat,Unix/Linux中使用run-socket.sh

    windows中在run-socket.bat中添加下面两行:

    rem 设置服务侦听端口 set SERVICE_PORT=新的端口

    Unix/linux中在run-socket.sh中修改:

    # 设置服务侦听端口 export SERVICE_PORT=新的端口
    • 传输字符集

    LAJP中默认的PHP和Java交互字符集是UTF-8,可通过修改启动脚本环境变量变更。

    run-socket.bat

    rem 字符集设置  GBK | UTF-8 set CHARSET=字符集

    run-socket.sh或run-msgq.sh

    # 字符集设置  GBK|UTF-8 export CHARSET=字符集 配置示例

    一般性的使用Java链接数据库需要配置数据源,这里提供一个简单的配置示例,以供参考。

    项目:

  • 一个简单的Web应用
  • 数据库:Mysql
  • 数据源:DBCP
  • run-msgq.sh

    #!/bin/sh # ----------------------------------------------------------- #  LAJP-Java Service 启动脚本 #               #               (2009-10 code.google/p/lajp/) #   # ----------------------------------------------------------- #----------------------- #DBCP配置 export DBCP_url="jdbc:mysql://127.0.0.1:3306/paper?characterEncoding=utf8&zeroDateTimeBehavior=round" export DBCP_username=root export DBCP_password=root export DBCP_maxActive=30 export DBCP_maxIdle=10 export DBCP_maxWait=1000 export DBCP_removeAbandoned=false export DBCP_removeAbandonedTimeout=120 export DBCP_testOnBorrow=true export DBCP_validationQuery="select 1" export DBCP_logAbandoned=true #----------------------- # java服务中需要的jar文件或classpath路径,如业务程序、第三方jar文件(如log4j等) export classpath=lib/commons-beanutils-1.8.2.jar:lib/commons-logging-1.1.1.jar:lib/log4j-1.2.8.jar:lib/commons-dbcp-1.2.2.jar:lib/commons-collections-3.2.1.jar:lib/commons-pool-1.5.4.jar:lib/mysql-connector-java-5.1.7-bin.jar:lib/lajp_10.04.jar:bin/ # 自动启动类和方法,LAJP服务启动时会自动加载并执行 export AUTORUN_CLASS=cn.programmerdigest.Init export AUTORUN_METHOD=init # 字符集设置  GBK|UTF-8 # export CHARSET=GBK # LAJP服务启动指令(前台) java -classpath .:$classpath lajp.PhpJava # LAJP服务启动指令(后台) # nohup java -classpath .:$classpath lajp.PhpJava &

    Java自动启动方法cn.programmerdigest.Init类中的init方法:

            /**          * 初始化DBCP数据源          */         public static void init()         {                 try                 {                         //从环境变量中获取DBCP配置信                         Properties p = new Properties();                         p.setProperty("driverClassName", "com.mysql.jdbc.Driver");                         p.setProperty("url", System.getenv("DBCP_url"));                         p.setProperty("username", System.getenv("DBCP_username"));                         p.setProperty("password", System.getenv("DBCP_password"));                         p.setProperty("maxActive", System.getenv("DBCP_maxActive"));                         p.setProperty("maxIdle", System.getenv("DBCP_maxIdle"));                         p.setProperty("maxWait", System.getenv("DBCP_maxWait"));                         p.setProperty("removeAbandoned", System.getenv("DBCP_removeAbandoned"));                         p.setProperty("removeAbandonedTimeout", System.getenv("DBCP_removeAbandonedTimeout"));                         p.setProperty("testOnBorrow", System.getenv("DBCP_testOnBorrow"));                         p.setProperty("validationQuery", System.getenv("DBCP_validationQuery"));                         p.setProperty("logAbandoned", System.getenv("DBCP_logAbandoned"));                         //创建数据源                         dataSource = (BasicDataSource) BasicDataSourceFactory                                         .createDataSource(p);                 }                 catch (Exception e)                 {                         //--                         throw new RuntimeException(e.getMessage());                 }         } 五、其他的文档

    LAJP的blog programmerdigest/category/lajp

    LAJP数据转换示例 文章简介

    本文通过程序样例来介绍LAJP中PHP和Java之间的的数据转换。

    PHP序列化数据简介

    在PHP语言中,数据类型是隐匿的,并且是根据上下文而自动变化的,比如:

    $a = 10; $a = "a is " . $a;

    在第一行中,$a是int类型,在第二行中$a变化为string类型。通常“弱”类型语言,像Javascript,VB,PHP等都是这样。PHP中提供了一些函数(is_array()、is_bool()、is_float()、is_integer()等)来获得变量的类型,更直接的方式是观察变量序列化后的排列规则:


    $a = 10; echo serialize($a);

    输出:

    i:10;

    i表示int类型,10是其值。


    $a = "abcd"; echo serialize($a);

    输出:

    s:4:"abcd";

    s表示string类型,4表示长度,"abcd"是其值。


    $a = TRUE; echo serialize($a);

    输出:

    b:1;

    b表示boolean类型,1表示TRUE,0表示FALSE。


    $a = 10.24; echo serialize($a);

    输出:

    d:10.2400000000000002131628207280300557613372802734375;

    d表示double类型,10.2400000000000002131628207280300557613372802734375是其值。


    数组、对象等复杂类型也可以序列化:

    $a = array(); $a[] = 20; $a[] = "abcde"; $a[] = TRUE; echo serialize($a);

    输出:

    a:3:{i:0;i:20;i:1;s:5:"abcde";i:2;b:1;}

    开始的a表示array,紧跟着的3表示数组长度,{}内部是数组元素:

    • i:0;i:20;是第一个元素,i:0;是KEY(表示下标是int类型的0),i:20;是VALUE。
    • i:1;s:5:"abcde";是第二个元素,i:1;是KEY(表示下标是int类型的1),s:5:"abcde";是VALUE。
    • i:2;b:1;是第三个元素,i:2;是KEY(表示下标是int类型的2),b:1;是VALUE。

    $a = array(); $a["a"] = 20; $a["b"] = "abcde"; $a["c"] = TRUE; echo serialize($a);

    输出:

    a:3:{s:1:"a";i:20;s:1:"b";s:5:"abcde";s:1:"c";b:1;}

    这里数组下标是字符串,数据结构可以看作是其他语言的Hashtable类型。


    在LAJP中,PHP和Java之间传输的数据封装形式,即是上面这种PHP序列化数据形式。

    Example 1 基本

    Java:

    package aaa.bbb.ccc; public class MyClass1 {         public static final int myMethod1(int i)         {                 return ++i;         } }

    PHP:

    $a = 10; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $b = lajp_call("aaa.bbbc.MyClass1::myMethod1", $a); echo "b---&gt;" . serialize($b) . "&lt;---<br/>"; echo "b---&gt;" . $b . "&lt;---<br/>";

    输出:

    a--->i:10;<--- b--->i:11;<--- b--->11<---

    在LAJP中,当PHP将整形10传给Java服务时,传送的数据即是字符串i:10;,Java服务返回整形11也包装为字符串i:11;。


    Java:

    package aaa.bbb.ccc; public class MyClass1 {         public static final int myMethod2(long i)         {                 return (int)++i;         } }

    PHP:

    $a = 10; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $b = lajp_call("aaa.bbbc.MyClass1::myMethod2", $a); echo "b---&gt;" . serialize($b) . "&lt;---<br/>"; echo "b---&gt;" . $b . "&lt;---<br/>";

    输出:

    a--->i:10;<--- Fatal error: Uncaught exception 'Exception' with message '[LAJP Error] Response receive Java exception: MethodNotFoundException: Can't match method: aaa.bbb.ccc.MyClass1.myMethod2(long)' in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php:215 Stack trace: #0 /media/sda3/prog/eclipse_php/workspace/LAJP_test/test1_02.php(8): lajp_call('aaa.bbb.ccc.MyC...', 10) #1 {main} thrown in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php on line 215

    myMethod2方法参数声明为long,在PHP中没有与之对应的数据类型,因此抛出异常。

    也有朋友认为应该允许这种情况,因为在Java中int可以自动转换为long;我的意见还是不允许,因为有带来二义性的可能。


    Java:

    package aaa.bbb.ccc; public class MyClass1 {         public static final int myMethod3(Integer i)         {                 return (int)++i;         } }

    PHP:

    $a = 10; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $b = lajp_call("aaa.bbbc.MyClass1::myMethod3", $a); echo "b---&gt;" . serialize($b) . "&lt;---<br/>"; echo "b---&gt;" . $b . "&lt;---<br/>";

    输出:

    a--->i:10;<--- Fatal error: Uncaught exception 'Exception' with message '[LAJP Error] Response receive Java exception: MethodNotFoundException: Can't match method: aaa.bbb.ccc.MyClass1.myMethod3(java.lang.Integer)' in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php:215 Stack trace: #0 /media/sda3/prog/eclipse_php/workspace/LAJP_test/test1_03.php(8): lajp_call('aaa.bbb.ccc.MyC...', 10) #1 {main} thrown in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php on line 215

    myMethod3方法参数声明为Integer,在Java语言中有装箱、拆箱,int和Integer通常可互换,但在LAJP中此种情况不被允许。所以,如果PHP传送int,Java方法必声明为基本类型int;传double,必声明为基本类型double;传boolean,必声明为基本类型boolean。


    Java:

    package aaa.bbb.ccc; public class MyClass1 {         public static final long myMethod4(int i)         {                 return ++i;         } }

    PHP:

    $a = 10; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $b = lajp_call("aaa.bbbc.MyClass1::myMethod4", $a); echo "b---&gt;" . serialize($b) . "&lt;---<br/>"; echo "b---&gt;" . $b . "&lt;---<br/>";

    输出:

    a--->i:10;<--- b--->O:14:"java_lang_Long":0:{}<--- Catchable fatal error: Object of class __PHP_Incomplete_Class could not be converted to string in /media/sda3/prog/eclipse_php/workspace/LAJP_test/test1_04.php on line 11

    这里myMethod4方法返回类型为long,在PHP中并没有与之对应的数据类型,但PHP端仍然接收到了:

    O:14:"java_lang_Long":0:{}

    其中起始的"O"表示这是一个对象。

    在Java端,LAJP是这样来转换返回数据的:

    • 如果方法返回类型是int或包装类Integer,封装为PHP的int序列化数据;
    • 如果返回是double或包装类Double,封装为PHP的float序列化数据;
    • 如果返回是boolean或包装类Boolean,封装为PHP的boolean序列化数据;
    • 如果返回是java.lang.String,封装为PHP的String序列化数据;
    • 如果返回是java.util.List或其子类,封装为PHP的array序列化数据,array下标为递增整数;
    • 如果返回是java.util.Map或其子类,封装为PHP的array序列化数据,array下标为字符串;
    • 如果以上都不是,视为JavaBean,封装为PHP4的对象序列化数据。

    本例中,返回类型long被视为最后一种情况。


    Java:

    package aaa.bbb.ccc; public class MyClass1 {         public static final Integer myMethod5(int i)         {                 return ++i;         } }

    PHP:

    $a = 10; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $b = lajp_call("aaa.bbbc.MyClass1::myMethod5", $a); echo "b---&gt;" . serialize($b) . "&lt;---<br/>"; echo "b---&gt;" . $b . "&lt;---<br/>";

    输出:

    a--->i:10;<--- b--->i:11;<--- b--->11<---

    在LAJP中,Java方法的返回类型,当声明为int,boolean,double或它们的包装类型是等价的。我的建议是不要声明为包装类型,将来的版本很可能不再支持。


    Example 2 参数

    Java:

    package aaa.bbb.ccc; public class MyClass2 {         public static final int myMethod1(int a, int b)         {                 return a + b;         } }

    PHP:

    $a = 10; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $b = lajp_call("aaa.bbbc.MyClass2::myMethod1", $a); echo "b---&gt;" . serialize($b) . "&lt;---<br/>"; echo "b---&gt;" . $b . "&lt;---<br/>";

    输出:

    a--->i:10;<--- Fatal error: Uncaught exception 'Exception' with message '[LAJP Error] Response receive Java exception: MethodNotFoundException: Can't match method: aaa.bbb.ccc.MyClass2.myMethod1(1 parameters)' in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php:215 Stack trace: #0 /media/sda3/prog/eclipse_php/workspace/LAJP_test/test2_01.php(8): lajp_call('aaa.bbb.ccc.MyC...', 10) #1 {main} thrown in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php on line 215

    在PHP语言中,方法参数是可变长的,但在LAJP中,不允许。


    Java:

    package aaa.bbb.ccc; public class MyClass2 {         public static final String myMethod3(boolean a)         {                 if (a)                 {                         return "input TRUE";                 }                 else                 {                         return "input FALSE";                 }         }         //同名方法         public static final String myMethod3(boolean a, int b)         {                 if (a)                 {                         return "input TRUE, " + b;                 }                 else                 {                         return "input FALSE, " + b;                 }         } }

    PHP:

    $a = TRUE; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $b = lajp_call("aaa.bbbc.MyClass2::myMethod3", $a); echo "b---&gt;" . serialize($b) . "&lt;---<br/>"; echo "b---&gt;" . $b . "&lt;---<br/>";

    输出:

    a--->b:1;<--- b--->s:10:"input TRUE";<--- b--->input TRUE<---

    Java的方法重载(overload),允许。下面是另一个例子(同名,参数数量也相同):

    Java:

    package aaa.bbb.ccc; public class MyClass2 {         public static final String myMethod4(boolean a)         {                 if (a)                 {                         return "input TRUE";                 }                 else                 {                         return "input FALSE";                 }         }         public static final String myMethod4(int a)         {                         return "input " + a;         } }

    PHP:

    $a = TRUE; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $b = lajp_call("aaa.bbbc.MyClass2::myMethod4", 10); echo "b---&gt;" . serialize($b) . "&lt;---<br/>"; echo "b---&gt;" . $b . "&lt;---<br/>";

    输出:

    a--->b:1;<--- b--->s:8:"input 10";<--- b--->input 10<---
    Example 3 空和异常

    Java:

    package aaa.bbb.ccc; public class MyClass3 {         public static final int myMethod1(int a, int b)         {                 return a / b;         } }

    PHP:

    $a = 10; $b = 0; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $ret = lajp_call("aaa.bbbc.MyClass3::myMethod1", $a, $b); echo "ret---&gt;" . serialize($ret) . "&lt;---<br/>"; echo "ret---&gt;" . $ret . "&lt;---<br/>";

    输出:

    a--->i:10;<--- Fatal error: Uncaught exception 'Exception' with message '[LAJP Error] Response receive Java exception: InvocationTargetException for call method aaa.bbbc.MyClass3.myMethod1' in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php:215 Stack trace: #0 /media/sda3/prog/eclipse_php/workspace/LAJP_test/test3_01.php(9): lajp_call('aaa.bbbc.MyC...', 10, 0) #1 {main} thrown in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php on line 215

    这是Java端除0异常的例子。Java方法抛出的异常,如果是Exception或其子类,都可以“抛”给PHP端。


    Java:

    package aaa.bbb.ccc; public class MyClass3 {         public static final String myMethod2()         {                 return null;         } }

    PHP:

    $ret = lajp_call("aaa.bbbc.MyClass3::myMethod2"); echo "ret---&gt;" . serialize($ret) . "&lt;---<br/>"; echo "ret---&gt;" . ($ret == NULL) . "&lt;---<br/>";

    输出:

    ret--->N;<--- ret--->1<---

    这是Java端返回null的例子。

    Java:

    package aaa.bbb.ccc; public class MyClass3 {         public static final void myMethod3()         {                         } }

    PHP:

    $ret = lajp_call("aaa.bbbc.MyClass3::myMethod3"); echo "ret---&gt;" . serialize($ret) . "&lt;---<br/>"; echo "ret---&gt;" . ($ret == NULL ? "NULL" : $ret) . "&lt;---<br/>";

    输出:

    ret--->N;<--- ret--->NULL<---

    这是Java端返回void的例子。

    在LAJP的9.10版本中,null和void返回类型在PHP端被解释为FALSE,10.04版本中已改正。


    Example 4 集合

    Java:

    package aaa.bbb.ccc; public class MyClass4 {         //方法参数声明为List         public static final void myMethod1(java.util.List list)         {                 if (list == null)                 {                         System.out.println("list集合为空");                         return;                 }                                 System.out.println("list集合长度: " + list.size());                 for (int i = 0; i < list.size(); i++)                 {                         System.out.printf("----list[%d]:%s\\n", i, (String)list.get(i));                 }         }         //方法参数声明为Map         public static final void myMethod1(java.util.Map<String, String> map)         {                 if (map == null)                 {                         System.out.println("map集合为空");                         return;                 }                                 System.out.println("map集合长度: " + map.size());                                 java.util.Set<String> keySet = map.keySet();                 for (String key : keySet)                 {                         System.out.printf("----map[%s=>%s]\\n", key, (String)map.get(key));                 }         } }

    PHP:

    $a = array(); //定义一个数组 $a[0] = "aaa";//第一个元素"aaa" $a[1] = "bbb";//第二个元素"bbb" $a[2] = "ccc";//第三个元素"ccc" echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; lajp_call("aaa.bbbc.MyClass4::myMethod1", $a);

    PHP输出:

    a--->a:3:{i:0;s:3:"aaa";i:1;s:3:"bbb";i:2;s:3:"ccc";}<---

    Java输出:

    list集合长度: 3 ----list[0]:aaa ----list[1]:bbb ----list[2]:ccc

    在PHP中,array可以模拟多种数据类型,如队列、哈西、栈等,对应到Java,有以下规定:

    • 如果array的第一个元素的KEY是int类型,对应为Java的java.util.List
    • 如果array的第一个元素的KEY是string类型,对应到Java的java.util.Map
    上面的例子中, $a[0] = "aaa";  第一个元素KEY是整形,因此调用的是myMethod1(java.util.List list)方法。下面将PHP代码稍加改动:

    $a["a"] = "aaa";//第一个元素"aaa" $a["b"] = "bbb";//第二个元素"bbb" $a["c"] = "ccc";//第三个元素"ccc"

    array的KEY数据类型被改动为字符串,再来看Java端的输出:

    map集合长度: 3 ----map[b=>bbb] ----map[c=>ccc] ----map[a=>aaa]

    myMethod1(java.util.Map<String, String> map)方法使用了泛型,在Java中运行态是去泛型化的,因此对于LAJP,泛型无作用。但我建议仍然多用泛型,至少在代码review中清晰很多。


    Java:

    package aaa.bbb.ccc; public class MyClass4 {         public static final void myMethod2(java.util.ArrayList list)         {                 if (list == null)                 {                         System.out.println("list集合为空");                         return;                 }                                 System.out.println("list集合长度: " + list.size());                 for (int i = 0; i < list.size(); i++)                 {                         System.out.printf("----list[%d]:%s\\n", i, (String)list.get(i));                 }         } }

    PHP:

    $a = array(); $a[] = 10; $a[] = 20; $a[] = 30; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; lajp_call("aaa.bbbc.MyClass4::myMethod2", $a);

    PHP输出:

    a--->a:3:{i:0;i:10;i:1;i:20;i:2;i:30;}<--- Fatal error: Uncaught exception 'Exception' with message '[LAJP Error] Response receive Java exception: MethodNotFoundException: Can't match method: aaa.bbb.ccc.MyClass4.myMethod2(java.util.ArrayList)' in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php:215 Stack trace: #0 /media/sda3/prog/eclipse_php/workspace/LAJP_test/test4_03.php(11): lajp_call('aaa.bbb.ccc.MyC...', Array) #1 {main} thrown in /media/sda3/prog/eclipse_php/workspace/LAJP_test/php_java.php on line 215

    集合入参类型声明必须是java.util.List或java.util.Map。这里不能声明为ArrayList是因为PHP传入的参数被视为List,而从面向对象角度理解ArrayList继承自List,可以说ArrayList是List,而List不是ArrayList,因此这里类型不匹配。


    Java:

    package aaa.bbb.ccc; public class MyClass4 {         public static final java.util.HashMap<String, Integer> myMethod4()         {                 java.util.HashMap map = new java.util.HashMap();                 map.put("aaa", 10);                 map.put("bbb", 20);                 map.put("ccc", 30);                                 return map;         } }

    PHP:

    $ret = lajp_call("aaa.bbbc.MyClass4::myMethod4"); echo "ret---&gt;" . serialize($ret) . "&lt;---<br/>";

    PHP输出:

    ret--->a:3:{s:3:"aaa";i:10;s:3:"ccc";i:30;s:3:"bbb";i:20;}<---

    这里返回类型声明为java.util.Map的子类形是可以的。从面向对象角度理解:HashMap即是Map。


    Example 5 对象

    Java:

    package aaa.bbb.ccc; //JavaBean public class MyBean {         private int i;         private boolean b;         private String c;         private java.util.List<Double> list;                 public int getI()         {                 return i;         }         public void setI(int i)         {                 this.i = i;         }         public boolean isB()         {                 return b;         }         public void setB(boolean b)         {                 this.b = b;         }         public String getC()         {                 return c;         }         public void setC(String c)         {                 this.c = c;         }         public java.util.List<Double> getList()         {                 return list;         }         public void setList(java.util.List<Double> list)         {                 this.list = list;         } } package aaa.bbb.ccc; public class MyClass5 {         public static final MyBean myMethod1(MyBean bean)         {                 if (bean == null)                 {                         return null;                 }                                 bean.setI(bean.getI() + 1);                 bean.setB(!bean.isB());                 bean.setC(bean.getC() + " OK!");                                 java.util.ArrayList<Double> retList = new java.util.ArrayList<Double>();                 for (double d : bean.getList())                 {                         retList.add(d + 1);                 }                 bean.setList(retList);                                 return bean;         } }

    PHP:

    class aaa_bbb_ccc_MyBean {         var $i;         var $b;         var $c;         var $list; } $a = new aaa_bbb_ccc_MyBean; //实例化对象 $a->i = 10; $a->b = TRUE; $a->c = "zhangsan"; $a->list = array(); $a->list[] = 10.2; $a->list[] = 20.4; echo "a---&gt;" . serialize($a) . "&lt;---<br/>"; $ret = lajp_call("aaa.bbbc.MyClass5::myMethod1", $a); echo "ret---&gt;" . serialize($ret) . "&lt;---<br/>";

    PHP输出:

    a--->O:18:"aaa_bbb_ccc_MyBean":4:{s:1:"i";i:10;s:1:"b";b:1;s:1:"c";s:8:"zhangsan";s:4:"list";a:2:{i:0;d:10.199999999999999289457264239899814128875732421875;i:1;d:20.39999999999999857891452847979962825775146484375;}}<--- ret--->O:18:"aaa_bbb_ccc_MyBean":4:{s:1:"i";i:11;s:1:"b";b:0;s:1:"c";s:12:"zhangsan OK!";s:4:"list";a:2:{i:0;d:11.199999999999999289457264239899814128875732421875;i:1;d:21.39999999999999857891452847979962825775146484375;}}<---

    在LAJP中,对象传输有下面的规则:

    • Java端,对象必须符合JavaBean规则
    • PHP端,必须是PHP4对象(PHP5对象序列化数据不完备,目前不支持)
    • Java和PHP对象映射,属性名称一致(有过Struts编程经历的人很容易理解这点)

    LAJP中提供了一个自动生成PHP4对象类的工具,比如可以通过Java的aaa.bbbc.MyBean类,来生成与之对应的PHP4对象类代码:

    php:

    $ret = lajp_call("lajp.ReflectUtil::javaBean2Php", "aaa.bbbc.MyBean"); //$ret = lajp_call("lajpsocket.ReflectUtil::javaBean2Php", "aaa.bbbc.MyBean"); //LAJP的socket版本用这一行 echo $ret;

    PHP输出:

    class aaa_bbb_ccc_MyBean {     var $b;     var $c;     var $i;     var $list; }

    对象、数组(Java映射为Map和List)属于容器型数据类型,可以嵌套其他类型数据包括对象和数组,LAJP对嵌套的层级没有限制,但嵌套的子类型(对象内部的属性、数组内部的元素)必须是以下几种:

    • int
    • boolean
    • php(float或double) | Java(double)
    • php(string) | Java(java.lang.String)
    • php4对象 | Java(JavaBean)
    • array | java.util.Map或java.util.List
    以上6种数据,也就是LAJP所能支持的可以传输的所有数据类型。

    本文标签: LinuxLAJPApachePHPjava