GWT 入门介绍(续)

GWT 入门介绍(续)

功能介绍(集成JUnit)

@TODO


功能介绍(国际化)

      项目开发过程中经常需要一些可配置的常量,例如查询最大条数,目录位置等。在传统的Java应用程序中这些内容通常会放在

属性文件中(Properties文件),但是使用属性文件有些弊端,第一,不支持类型,所有的内容都是String,第二是,只有在具体使用

的时候才能发现有些属性没有定义,而不能在编译的时候发现。

      那么GWT如何处理这个问题呢?GWT中有一个特殊的接口com.google.gwt.i18n.client.Constants可以使用这个接口达到

定义常量的效果,并且这些常量在编译的时候被绑定,而且可以支持类型。

     使用GWT主要有以下几步:

第一步,建立一个集成于Constants的接口,例如:

public interface NumberFormatConstants extends Constants {
/**
   * @return the localized decimal separator
   */
String decimalSeparator();

/**
   * @return the localized thousands separator
   */
String thousandsSeparator();
}

第二步,根据接口中定义的方法定义一个跟接口同名的属性文件,例如:

#NumberFormatConstants.properties

decimalSeparator = ,
thousandsSeparator = .

 

第三步,获取文件中定义的内容,例如:

public void useNumberFormatConstants() {
NumberFormatConstants constants = (NumberFormatConstants) GWT.create(NumberFormatConstants.class);
String decimalSep = constants.decimalSeparator();
String thousandsSep = constants.thousandsSeparator();
String msg = "Decimals are separated using '" + decimalSep + "'";
msg += ", and thousands are separated using '" + thousandsSep + "'";
showMessage(msg);
}

上述三步中在第二步和第三步中间隐含了伊特特殊的步骤,就是GWT编译器结合接口文件和属性文件编译出了一个

类,这个类实现了这个接口,每一个方法返回属性文件中的值。

其中GWT.create()方法可以获得生成的中间类的引用。

 

通常情况下,接口方法明和属性文件中的名字相同,例如:

String decimalSeparator(); 和 thousandsSeparator = .

但是也可以自定义接口方法和属性文件中内容的映射,例如:

 

public interface NumberFormatConstantsWithAltKey extends Constants {
/**
   * @gwt.key fmt.sep.decimal
   * @return the localized decimal separator
   */
String decimalSeparator();

/**
   * @gwt.key fmt.sep.decimal
   * @return the localized thousands separator
   */
String thousandsSeparator();
}

@gwt.key fmt.sep.decimal 定义了属性文件中key的内容,所以属性文件应该为:

#NumberFormatConstants.properties

fmt.sep.decimal = .
fmt.sep.thousands = ,

Constants子接口中定义的方法必须满足如下形式:

 

T methodName()

 

这里T是一个返回值,T可以使用如下表中的所有类型:

T类型                        属性文件定义
String                        简单的字符串
String[]                     使用逗号分割的字符串,如果某个字符串中包含逗号需要使用\\作为转移字符,例如:'\\,'
int                            int值,在编译的时候做类型检查
float                         float值,在编译的时候做类型检查
double                       double值,在编译的时候做类型检查
boolean                     boolean值"true" 或者 "false"), 在编译的时候做类型检查
Map                          使用逗号分隔的字符产,每一个字符产在属性文件中有一条定义,定义了一个Key-Value值

 

Map示例:

a = X
b = Y
c = Z
someMap = a, b, c

 

Map someMap();方法得到的内容为:{a:X, b:Y, c:Z}

 

ConstantsWithLookup

ConstantsWithLookup是Constants的子接口,用法一样,只不过ConstantsWithLookup有一组通过属性名字获取属性值的方法:

getBoolean(String)        通过名字找到boolean型内容
getDouble(String)         通过名字找到double型内容
getFloat(String)            通过名字找到float型内容
getInt(String)              通过名字找到int型内容
getMap(String)            通过名字找到Map型内容
getString(String)          通过名字找到String型内容
getStringArray(String)   通过名字找到String[]型内容

 

效率问题:Constants效率比ConstantsWithLookup高,为什么呢?Constants在编译的时候会生成对应的JavaScript代码,

GWT Compiler会根据程序中是否使用了某些属性来决定这些内容是否会被编译为JavaScript,所以及时在Constants中声明

了某些方法,如果在代码中不使用的话,不会被编译为JavaScript代码的。

但是ConstantsWithLookup有根据属性名字查找属性内容的方法,所以,GWT Compiler不能根据上述方法确定属性是否被使用,

所以所有的属性内容都回被编译为JavaScript代码。

这是ConstantsWithLookup的优点,也是缺点!

 

Message类

在使用Constants(或者ConstantsWithLookup)的时候,我们只能使用预定义的消息,有些时候我们需要可变的消息。

例如:

    我们需要一个通用的消息再加上一个功能名字的参数怎么实现呢?

 

Message类相当于Java中的Properties,ResourceBundle和MessageFormat的联合体,例如:

 

消息文件类:

public interface GameStatusMessages extends Messages {
/**
   * @param username the name of a player
   * @param numTurns the number of turns remaining
   * @return a message specifying the remaining turns for a player
   */
String turnsLeft(String username, int numTurns);

/**
   * @param numPoints the number of points
   * @return a message describing the current score for the current player
   */
String currentScore(int numPoints);
}


属性文件定义:
turnsLeft = Turns left for player ''{0}'': {1}
currentScore = Current score: {0}

使用:
public void beginNewGameRound(String username) {
GameStatusMessages messages = (GameStatusMessages) GWT.create(GameStatusMessages.class);

// Tell the new player how many turns he or she has left.
int turnsLeft = computeTurnsLeftForPlayer(username);
showMessage(messages.turnsLeft(username, turnsLeft));

// Tell the current player his or her score.
int currentScore = computeScore(username);
setCurrentPlayer(username);
showMessage(messages.currentScore(currentScore));
}

 

我们可以看到在使用的时候基本一致,但是,可以使用参数配置原有的消息。

另外Message的方法的格式为:

    String methodName(optional-params)

从中我们也可以看出区别,Message只能使用String类型的参数。

 

Constants(或者ConstantsWithLookup)和Message的区别是:
Constants用来定义系统的常量,支持多种类型。
Message用来定义系统的消息,可以支持参数化消息,但是只支持String类型的内容。

 

在使用Constants和Message的时候,可以将属性文件的编码设置为UTF-8这样,就不用
使用Native2ascii将正常的文件转移为utf-8的替换文件了。
当然如果你觉得不麻烦也可以使用传统的Java属性文件(使用native2ascii处理过得文件)。

 

 

功能介绍(JavaScript Native Interface)
JavaScript Native Interface = JSNI
JSNI定义了在GWT环境下,Java与JavaScript交互的一种方法。

虽然GWT的一些核心的方法是用JavaScript编写的,但是这里还是不推荐使用JNI,应为这样做与GWT的初衷相悖,

并且,有一定的难度,开发调试也相对困难。


Java调用JavaScript方法:

JSNI方法定义需要使用native关键字,并且需要在参数列表之后,结尾的分号之前定义。JSNI方法的开始使用/*-{

结尾使用}-*/,例如:

 

public static native void alert(String msg) /*-{
$wnd.alert(msg);
}-*/;

 

当上述方法在Java中调用的时候,实际上将会调用Window的alert方法,将传入的内容打印出来。

在Hosted Mode下,断点可以设置在上述方法中,可以方便的查看传入的参数。

 

JavaScript调用Java方法:

方法调用方式:

    [instance-expr.]@class-name::method-name(param-signature)(arguments)

属性访问方式:

    [instance-expr.]@class-name::field-name

 

[instance-expr.]

    用来区分实例方法调用还是静态方法调用。在调用实例方法的时候必须出现,在调用静态方法的时候不能出现。

class-name

    类的名字。

method-name

    方法的名字

param-signature

    方法的参数列表,这里使用的是内部形式(参考Java虚拟机Class格式),但是不需要写返回值类型。
arguments
    调用方法的实际参数。

例如:

public class JSNIExample {

String myInstanceField;
static int myStaticField;

void instanceFoo(String s) {
    // use s
}

static void staticFoo(String s) {
    // use s
}

 

// 该方法被调用的时候将在JavaScript中执行,并且

// 可以使用JavaScript中的内容。

public native void bar(JSNIExample x, String s) /*-{
    // 调用这个实例本身的instanceFoo方法
    this.@com.google.gwt.examples.JSNIExample::instanceFoo(Ljava/lang/String;)(s);

    // 调用x实例(输入参数)上的instanceFoo实例方法
    x.@com.google.gwt.examples.JSNIExample::instanceFoo(Ljava/lang/String;)(s);

    // 调用静态方法 staticFoo()
    @com.google.gwt.examples.JSNIExample::staticFoo(Ljava/lang/String;)(s);

    // 读取这个实例的变量
    var val = this.@com.google.gwt.examples.JSNIExample::myInstanceField;

    // 设置x上的实例变量
    x.@com.google.gwt.examples.JSNIExample::myInstanceField = val + " and stuff";

    // Read static field (no qualifier)
    @com.google.gwt.examples.JSNIExample::myStaticField = val + " and stuff";
}-*/;

}

 

Java和JavaScript之间参数的传递:

Java -> JavaScript

Java type                              JavaScript Type
numeric primitive                     a JavaScript numeric value, as in var x = 42;
String                                   a JavaScript string, as in var s = "my string";
boolean                                 a JavaScript boolean value, as in var b = true;
JavaScriptObject (see notes)    a JavaScriptObject that must have originated from JavaScript code, typically as the return value of some other JSNI method
Java array            an opaque value that can only be passed back into Java code
any other Java Object        an opaque value accessible through special syntax

 

异常
调用JSNI方法的时候会抛出一个JavaScriptException的异常,但是由于JavaScript不是一个强类型的语言,所以
无法想Java一样处理JavaScript异常。一个好的方式是在Java中处理Java异常,在JavaScript中处理JavaScript异常。

另外在JSNI方法,Java普通方法混掉的过程中,异常可以从最底层移植抛到最想的调用层,例如:

1. Java method foo() calls JSNI method bar()
2. JavaScript method bar() calls Java method baz()
3. Java method baz() throws an exception
baz()中抛出的异常可以蔓延到bar方法,可以在foo方法中捕获。

 

 

从Host Model到 Web Model

在Host Model方式下,GWT并不将Java代码编译为JavaScript,而是在GWT环境中直接运行Java bytecode,

但是项目正式部署之后使用的是Web Model,那么如何从Host Model迁移到Web Model呢?

 

首先需要将Java代码编译为JavaScript代码。

使用如下命令可以将Java代码编译为JavaScript代码:

java -cp "%~dp0\src;%~dp0\bin;%~dp0\../../gwt-user.jar;%~dp0\../../gwt-dev-windows.jar" com.google.gwt.dev.GWTCompiler -out "%~dp0\www" %* com.google.gwt.sample.hello.Hello

 

-cp 指定源代码目录,Class目录,和GWT的jar文件的路径

-out 指定JavaScript代码的输出路径

com.google.gwt.sample.hello.Hello 指定编译的Module,一般是gwt.xml文件中entry-point类去掉client之后的内容。

 

当代码量比较大的时候,需要指定Java使用内存的大小,否则会内存溢出。

java -Xmx512m -Xms128m -cp "%~dp0\src;%~dp0\bin;%~dp0\../../gwt-user.jar;%~dp0\../../gwt-dev-windows.jar" com.google.gwt.dev.GWTCompiler -out "%~dp0\www" %* com.google.gwt.sample.hello.Hello

 

之后将编译成的JavaScript代码拷贝到Web项目的根目录中,与WEB-INF相同层次的目录。

 

最后需要将gwt.xml文件中定义的service编程对应的Servlet。

 

 

=>

 

 

     Calendar

     com.google.gwt.sample.dynatable.server.SchoolCalendarServiceImpl

 

 

 

     Calendar

     /calendar

 

 

使用数据源

Hosted Mode 虽然开发起来很方便,但是也有缺点,例如,数据源的配置就有问题。

在GWT Hosted Mode下无法配置数据源,一种可选的方式是使用一个假的数据库链接

管理类,这个类的接口返回Connection,内部以DriverManager的方式实现,等待

后续部署之后再切换到数据源模式。

 

日志处理(Log4J)

回想GWT应用程序,client包内部的代码将会被编译为客户端JavaScript代码,所以这里
不需要记录日志,也不可能使用Log4j。
但是Server包内的内容在服务器上运行,需要合理的使用日志。

 

 

一个简单的Login示例

代码结构如下:

 

└─src
    └─com
        └─jpleasure
            └─gwt
                └─logon
                    │ LogonDemo.gwt.xml                       GWT配置模块文件
                    │
                    ├─client                                          客户端代码包
                    │ │ LogonDemo.java                        GWT代码的入口点
                    │ │ LogonDemoController.java           画面迁移控制类
                    │ │
                    │ ├─exception                                 异常定义包
                    │ │      ApplicationException.java        应用程序异常
                    │ │     
                    │ ├─panel                                       页面Panel包

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值