Helper Method

未分类 | Posted by 子非舟
Apr 28 2011

Helper Method (4.7.1)

Problem:

You have a long or complex method to implement. You want your code to be easy to develop, test and modify.

Solution:

Look for logical steps in the solution of the method. Put the code to solve this step in a well-named helper method. For example, if the problem is for a robot to travel in a square pattern, the problem could be decomposed like this:

public void squareMove()

{ this.sideMove();

this.sideMove();

this.sideMove();

this.sideMove();

}

where sideMove is defined as follows:

private void sideMove()

{ this.move();

this.move();

this.move();

this.turnLeft();

}

Of course the problem may involve writing several different helper methods. Since helper methods are usually not services the class provides, they should generally be declared private or at least protected, depending on whether or not subclasses will need access to them.

Consequences:

Long or complex methods are easier to read, develop, test and modify.

通过Helper Method.

Http Message结构学习总结

未分类 | Posted by 子非舟
Apr 28 2011

【转】http://www.jzxue.com/jianzhanzhinan/jianzhanjingyan/200904/19-1949_2.html

最近做的东西需要更深入地了解Http协议,故死磕了一下RFC2616-HTTP/1.1协议,主要是了解Http Message结构及每部分含义,在此总结一下,并打算写一个模拟发送HTTP请求的工具,明天写完再附上来:>

(注:下面如“(14.1)”表示是在RFC2616第14章第1节有更详细的介绍)

一.Http Message结构

了解Http Message先看下图:


Http Message包含3个部分:

(1).请求行/状态行

(2).消息头(Message Header),分为4类:常规头,请求头,响应头和实体头,下面会详细介绍,一个Message里可以有多个消息头。

(3).消息体(Message Body),这个是可选的。

二.Http Message分两类:

1.请求消息(Request Message)

Request Message结构如下:


(1).请求行(Request-Line)结构(5.1):


Http方法(Http Method):主要有8类:GET,POST......下面会介绍。

Request-URI:请求操作的资源。

Http-Version:Http的版本,如:Http/1.0,Http/1.1

(2).消息头:

请求消息(Request Message)不应包含响应头。

2.响应消息(Response Message)

Response Message结构如下:


(1).状态行(Status-Line)结构:


Http-Version:Http的版本,如:Http/1.0,Http/1.1

Status-Code(状态码):状态码是一个三位数字,我对状态码的理解是:对请求(request)做出响应的类型/结果。

状态码第一位数字定义了响应类型,这里分为5种:

-1XX:Informational,请求接收到了,正在进一步的处理中(Request received, continuing process)。

-2XX:Success,表示用户请求被正确接收(The action was successfully received,understood, and accepted),这里典型的是:200 OK

-3XX:Redirection,表示请求没有成功,客户必须采取进一步的动作 (Further action must be taken in order to complete the request.)

-4XX:Client Error,表示客户端提交的请求有错误(The request contains bad syntax or cannot be fulfilled),典型的有:404 Not Found

-5XX:Server Error,表示服务器不能完成对请求的处理 (The server failed to fulfill an apparently valid request),典型的是:503 Service Unavailable

(注:详细的状态码会在文章的最后列出)

(2).消息头:

响应消息(Response Message)不应包含请求头。

三.Http消息头包括4类:

Http消息头(Message Header),主要是带上一些处理HTTP消息所需的辅助信息。

Http消息头结构如下:


1.Field-Name:下面提到的,如:Cache-Control,Date...等,这些就是Field-Name,在RFC文档中Field-Name的数量是有限的,只有43个,而你自己可以增加自定义的Field-Name,但由于浏览器是按照RFC规范实施的(当然它们也有它们自定义的消息头),所以除非是自己实现服务器和客户端,否则,自定义的Field-Name一般没用。

2.Field-Vlaue:根据Field-Name的不同,会有不同的Field-Value

1.常规头(4.5节)

常规头(generl-header):request和response都有可能会用到的,但和消息体无关的一些附加信息。

(1).Cache-Control(14.9)

(2).Connection(14.10)

(3).Date(14.18)

(4).Pragma(14.32)

(5).Trailer(14.40)

(6).Transfer-Encoding(14.41)

(7).Upgrade(14.42)

( 8) .Via(14.45)

(9).Warning(14.46)

2.请求头(5.3节)

请求头(request-header):一些关于客户端/request的附加信息。

(1).Accept(14.1)

(2).Accept-Charset(14.2)

(3).Accept-Encoding(14.3)

(4).Accept-Language(14.4)

(5).Authorization(14.8)

(6).Expect(14.20)

(7).From(14.22)

( 8) .Host(14.23)

(9).If-Match(14.24)

(10).If-Modified-Since(14.25)

(11).If-None-Match(14.26)

(12).If-Range(14.27)

(13).If-Unmodified-Since(14.28)

(14).Max-Forwards(14.31)

(15).Proxy-Authorization(14.34)

(16).Range(14.35)

(17).Referer(14.36)

(1 8) .TE(14.39)

(19).User-Agent(14.43)

3.响应头(6.2节)

响应头(response header):一些关于响应(response)的附加信息。

(1).Accept-Ranges(14.5)

(2).Age(14.6)

(3).ETag(14.19)

(4).Location(14.30)

(5).Proxy-Authenticate(14.33)

4.实体头(7.1节)

实体头(entity-header):和消息体有关的附加信息。

(1).Allow(14.7)

(2).Content-Encoding(14.11)

(3).Content-Language(14.12)

(4).Content-Length(14.13)

(5).Content-Location(14.14)

(6).Content-MD5(14.15)

(7).Content-Range(14.16)

( 8) .Content-Type(14.17)

(9).Expires(14.21)

(11).Last-Modified(14.29)

(12).extension-header

看到这里你可能会很奇怪,为什么会没有Cookie,Content-Disposition这种常见的信息头?!这里我说一下,Content-Disposition不是HTTP标准的一部分,但它在其他RFC文档中定义了(RFC1806)。而Cookie呢?首先看看Cookie是用来干嘛的:Cookie和Session是为了解决Http协议中无状态的问题,由于Http的设计者们时就没打算让Http有状态这种特性,故Cookie这种东西是肯定不可能是Http标准中的一部分。其实,它们都属于上面所说的:extension-header。其实各种浏览器都有它们自定义的扩展头,特别是IE!

四.Http/1.1方法(9节):

1.OPTIONS:简单说它的作用是查询信息,例如:查询服务器的能力(能干些什么)/查询操作该资源需要一些什么......当然,你可以不查询,直接操作资源/向服务器发送请求,如果服务器直接,它会告诉你成功/失败,如果服务器不支持,它也会告诉你不支持,但这样效率比较低,因为你操作了资源/你需要初始化资源,而OPTIONS不会执行这些操作,它仅仅是询问,当你不知道能否对资源执行此操作时,可以使用OPTIONS提高效率。

2.GET:请求资源(详细参考《浅谈HTTP中Get与Post的区别》

3.HEAD:主要是获取服务器响应的头信息,它的响应消息(response message)没有消息体(message body),只有消息头(message headers),作用是获取服务器的一些信息,如:Cache-Control等,以给客户端足够的信息决定接下来该如何去做。

4.POST:更新资料(详细参考《浅谈HTTP中Get与Post的区别》

5.PUT:如果请求的URI是已经存在的资源,则PUT请求所附的实体应被当作修改服务器中的资源,成功的话返回200或者204。如果请求的URI资源不存在,则URI可以被定义成新的资源,这时,服务器必须通过201(建立)响应通知用户。

POST和PUT不同点在反映在对request-URI的不同意义。使用POST方法时,URI资源会处理POST所提交的数据,这时的URI资源可以看作是接收并处理数据的程序。而使用PUT方法时,请求(request)中的实体数据会被认为是URI资源,而服务器不会试图用其他资源去接收这个请求。

6.DELETE:要求服务器释放请求(request)中URI所指向的资源。在服务器上,DELETE方法可能会被强行制止,所以客户端不能担保操作已经实现,即使服务器返回的状态码说明操作已经成功完成了(当然,如果服务器返回了成功,说明服务器已经打算去删除/移动需要被删除的资源了)。

7.TRACE:TRACE 方法用于引起远程的,该请求消息的应用层回射。请求的最终接收者应该反射200(OK)响应,并以该消息作为客户端回收消息的实体。最终接收者是原始服务器或第一个收到请求中的Max-Forwards值为0(0)的代理或网关。TRACE请求(注意是请求不是响应)不能包括实体。以上是RFC文档的解释,或许你没看明白上面到底想说什么,但只要你知道它的作用,你大概也能猜到了:>,TRACE作用是:允许客户端看见请求链上的另一端收到了什么,然后使用该数据作为测试或诊断信息。就是说响应请求(response)的实体里包含了服务器/网关接收到的数据,而其中消息头Via的值有特殊作用,将它作为请求链路径。使用Max-Forwards头部域允许客户端限制请求链的长度,这对于测试无限循环转发消息的代理链非常有用。

8.CONNECT:规范保留 CONNECT 方法名。该方法用于代理,使之能够动态切换隧道(例如 SSL隧道)。

9.extension-method

先写这么多,本想再写写每类消息头的中Field-Name具体含义,但罗马非一天建成,日后再继续吧:<~~本文如有错漏,请各位指出,谢谢。

转载请说明出处,谢谢![hyddd(http://www.cnblogs.com/hyddd/)]

五.参考文献

[1].RCF2616

六.附录(状态码)

"100"(10.1.1)Continue
"101"(10.1.2)Switching Protocols
"200"(10.2.1)OK
"201"(10.2.2)Created
"202"(10.2.3)Accepted
"203"(10.2.4)Non-Authoritative Information
"204"(10.2.5)No Content
"205"(10.2.6)Reset Content
"206"(10.2.7)Partial Content
"300"(10.3.1)Multiple Choices
"301"(10.3.2)Moved Permanently
"302"(10.3.3)Found
"303"(10.3.4)See Other
"304"(10.3.5)Not Modified
"305"(10.3.6)Use Proxy
"307"(10.3.8)Temporary Redirect
"400"(10.4.1)Bad Request
"401"(10.4.2)Unauthorized
"402"(10.4.3)Payment Required
"403"(10.4.4)Forbidden
"404"(10.4.5)Not Found
"405"(10.4.6)Method Not Allowed
"406"(10.4.7)Not Acceptable
"407"(10.4.8)Proxy Authentication Required
"408"(10.4.9)Request Time-out
"409"(10.4.10)Conflict
"410"(10.4.11)Gone
"411"(10.4.12)Length Required
"412"(10.4.13)Precondition Failed
"413"(10.4.14)Request Entity Too Large
"414"(10.4.15)Request-URI Too Large
"415"(10.4.16)Unsupported Media Type
"416"(10.4.17)Requested range not satisfiable
"417"(10.4.18)Expectation Failed
"500"(10.5.1)Internal Server Error
"501"(10.5.2)Not Implemented
"502"(10.5.3)Bad Gateway
"503"(10.5.4)Service Unavailable
"504"(10.5.5)Gateway Time-out
"505"(10.5.6)HTTP Version not supported

[Java 5.0] Annotation – @Deprecated @Override @SuppressWarnings

未分类 | Posted by 子非舟
Apr 25 2011
【转】http://blog.donews.com/vincentcao/archive/2006/01/18/700024.aspx
英文原文:http://java.sun.com/docs/books/tutorial/java/javaOO/annotations.html
JDK5.0 引入了一种新的元语言工具,叫“annotation”。 Annotation 提供的信息不是程序的一部分,例如,代码片断的作者,或者告诉编译器忽略特定的错误等。Annotation不会对程序的运行产生任何影响。
Annotation的形式为:@annotation。它可以被应用到类,属性,方法等的声明处。Annotation位于他们之前,并且(通常)独立一行。它还可以带有参数,例如: @Author("MyName") class myClass {} 或者 @SuppressWarnings("unchecked") void MyMethod() {}
自定义annotation属于高级用法,不属于本文讨论范畴。但是有三个内置的annotation你必须知道(如果你是Java程序员的话),他们是@Deprecated,@Override和@SuppressWarnings。以下列举他们对于方法的应用。
import java.util.List;
class Food {} class Hay extends Food {} class Animal { Food getPreferredFood() { return null; } /** * @deprecated document why the method was deprecated */ @Deprecated static void deprecatedMethod() { } } class Horse extends Animal { Horse() { return; } @Override Hay getPreferredFood() { return new Hay(); } @SuppressWarnings("deprecation") void useDeprecatedMethod() { Animal.deprecateMethod(); //deprecation warning - suppressed } }

@Deprecated

@Deprecated annotation 表示被标志的方法不应该被继续使用了,如果程序使用了deprecated的类,变量或方法,编译器将会给出警告。如果一个元素被deprecate了,那么应该如上例所示在上面的注释文档中添加deprecated的标记并给出原因。注意,文档中标记的首字母是小写d,annotation中的首字母是大写D。一般来说,你应该避免使用deprecated的方法,可以参阅相关文档了解替代方法。

@Override

@Override annotation是为了告诉编译器,该元素会override父类的这个元素。在上例中,override annotation表示Horse中的getPreferredFood方法将会override 父类Animal中的getPreferredFood方法。如果这步失败,编译器会报错。

尽管Override的时候并不是必须使用这个annotation,使用它还是可以使程序逻辑更明了化,特别是当子类方法的返回值继承自父类方法的返回值。如在上例中,子类的getPreferredFood方法返回Hey的实例,父类的getPreferredFood方法返回Food的实例,其中Hey继承Food。更多信息见Overriding and Hiding Methods

@SuppressWarnings

@SuppressWarnings告诉编译器,此处不要报错。在上例中,useDeprecatedMethod方法调用了Animal类中的deprecated方法,通常编译器会给出警告,但是此处,被suprress了。

每一个编译器警告属于一个类别,Java语言规范中列出了两类:“deprecation” 和“unchecked”。如果要suppress这两类警告,使用如下方式:
@SuppressWarnings ( {"unchecked", "deprecation"} )
参见编译器文档了解警告的类别。

对于annotation更高级的用法是写一段程序不仅可以读java代码,并且可以处理annotations。为了达到这个目标,5.0JDK中包含了一个处理annotation的工具,称为apt。下一个版本JDK(代号Mustang)的apt将成为Java编译器的一个标准部分。更多信息,可以参见Getting Started with the Annotation Processing Tool。关于正在实现的Mustang感兴趣,可以参见Language Model APIJSR 269: Pluggable Annotation Processing API

新手入门:Java中Class类工作原理详解

未分类 | Posted by 子非舟
Apr 25 2011

【转】http://tech.ccidnet.com/art/3539/20070620/1118939_1.html
1.Class对象

Class对象包含了与类相关的信息。事实上,Class对象就是用来创建类的所有的“普通”对象的。

类是程序的一部分,每个类都有一个Class对象。换言之,每当编写并且编译了一个新类,就会产生一个Class对象(恰当地说,是被保存在一个同名的.class文件中)。在运行时,当我们想生成这个类的对象时,运行这个程序的Java虚拟机(JVM)首先检查这个类的Class对象是否已经加载。如果尚未加载,JVM就会根据类名查找.class文件,并将其载入。

一旦某个类的Class对象被载入内存,它就被用来创建这个类的所有对象。看下面示例。

SweetShop.java

package com.zj.sample;

class Candy {
static {
System.out.println("Loading Candy");
}
}

class Gum {
static {
System.out.println("Loading Gum");
}
}

class Cookie {
static {
System.out.println("Loading Cookie");
}
}

public class SweetShop {
public static void main(String[] args) {
System.out.println("inside main");
new Candy();
System.out.println("After creating Candy");
try {
Class.forName("com.zj.sample.Gum");
} catch (ClassNotFoundException e) {
System.out.println("Couldn't find Gum");
}
System.out.println("After Class.forName(\"Gum\")");
new Cookie();
System.out.println("After creating Cookie");
}
}
结果:

inside main

Loading Candy

After creating Candy

Loading Gum

After Class.forName("Gum")

Loading Cookie

After creating Cookie

2.获取Class实例的三种方式

1)利用对象调用getClass()方法获取该对象的Class实例。

2)使用Class类的静态方法forName(),用类的名字获取一个Class实例。

3)运用.class的方式来获取Class实例,对于基本数据类型的封装类,还可以采用.TYPE来获取相对应的基本数据类型的Class实例。

3.Class.forName

上面的示例中:

Class.forName("com.zj.sample.Gum");

这个方法是Class类(所有Class对象都属于这个类)的一个static成员。Class对象就和其它对象一样,我们可以获取并操作它的引用。forName()是取得Class对象的引用的一种方法。它是用一个包含目标类的文本名的String作输入参数,返回的是一个Class对象的引用。

4.类字面常量

Java还提供了另一种方法来生成对Class对象的引用,即使用“类字面常量”。对上述程序来说,可以是:

com.zj.sample.Gum.class;

5.关键字instanceof

关键字instanceof返回一个布尔值,判断是不是某个特定类型的实例。

if(x instanceof Dog) ((Dog)x).bark();

6.获取Class实例

package com.zj.sample;

class Point {
int x, y;
}

class ClassTest {
public static void main(String[] args) {
Point pt = new Point();
Class c1 = pt.getClass();
System.out.println(c1.getName());

try {
Class c2 = Class.forName("com.zj.sample.Point");
System.out.println(c2.getName());
} catch (Exception e) {
e.printStackTrace();
}

Class c3 = Point.class;
System.out.println(c3.getName());

Class c4 = int.class;
System.out.println(c4.getName());

Class c5 = Integer.TYPE;
System.out.println(c5.getName());

Class c6 = Integer.class;
System.out.println(c6.getName());
}
}
结果:

com.zj.sample.Point

com.zj.sample.Point

com.zj.sample.Point

int

int

java.lang.Integer

7.Class的其他方法

1)Class.newInstance()使用所选的Class对象生成该类的新实例。它调用了缺省(无参数)的类构造器生成新的对象。所以使用newInstance()创建的类必须有一个缺省构造器。对于newInstance()来说,可以在原先没有任何对象存在的情况下,使用它创建一个新的对象。

利用newInstance()实例化一个对象:

package com.zj.sample;

class Point {
static {
System.out.println("Loading Point");
}

int x, y;
}

class ClassTest {
public static void main(String[] args) {

try {
Class c = Class.forName("com.zj.sample.Point");
Point pt = (Point) c.newInstance();
} catch (Exception e) {
e.printStackTrace();
}

}
}
结果:

Loading Point

2)Class.isInstance()方法提供了一种动态地调用instanceof运算符的途径。

3)Class.getInterfaces()方法返回Class对象的数组,这些对象代表的是某个Class对象所包含的接口。

4)如果有一个Class对象,那么就可以通过getSuperclass()获取它的直接基类。这个方法自然也是返回一个Class引用,所以可以进一步查询其基类。这意味着在运行时,可以找到一个对象完整的类层次结构。

5)Class类支持反射的概念,Java附带的库java.lang.reflect包含了Field、Method以及Constructor类(每个类都实现了Member接口)。这些类型的对象是由JVM在运行时创建的,用以表示未知类里对应的成员。这样可以使用Constructor创建新的对象,用get()和set()方法读取和修改与Field对象关联的字段,用invoke()方法调用与Method对象关联的方法。另外,还可以调用getFields()、getMethods、getConstrucotrs()方法,返回表示字段、方法以及构造器的对象的数组。

8.利用反射API察看未知类的构造方法与方法

package com.zj.sample;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

class Point {
static {
System.out.println("Loading Point");
}

int x, y;

void output() {
System.out.println("x=" + x + "," + "y=" + y);
}

Point(int x, int y) {
this.x = x;
this.y = y;
}
}

class ClassTest {
public static void main(String[] args) {

try {
Class c = Class.forName("com.zj.sample.Point");
Constructor[] cons = c.getDeclaredConstructors();
for (int i = 0; i < cons.length; i++)// 返回所有声明的构造方法
{
System.out.println(cons[i]);
}
Method[] ms = c.getDeclaredMethods();
for (int i = 0; i < ms.length; i++)// 返回所有声明的方法
{
System.out.println(ms[i]);
}

} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:

Loading Point

com.zj.sample.Point(int,int)

void com.zj.sample.Point.output()

9.动态调用一个类的实例(完全没有出现point这个名字)

package com.zj.sample;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

class Point {
static {
System.out.println("Loading Point");
}

int x, y;

void output() {
System.out.println("x=" + x + "," + "y=" + y);
}

Point(int x, int y) {
this.x = x;
this.y = y;
}
}

class ClassTest {
public static void main(String[] args) {

try {
Class c = Class.forName("com.zj.sample.Point");
Constructor[] cons = c.getDeclaredConstructors();
Class[] params = cons[0].getParameterTypes();// 察看构造器的参数信息
Object[] paramValues = new Object[params.length];// 构建数组传递参数
for (int i = 0; i < params.length; i++) {
if (params[i].isPrimitive())// 判断class对象表示是否是基本数据类型
{
paramValues[i] = new Integer(i);
}
}
Object o = cons[0].newInstance(paramValues);// 创建一个对象的实例
Method[] ms = c.getDeclaredMethods();// 调用方法
ms[0].invoke(o, null);// 用指定的参数调用(output方法没有参数,null)
} catch (Exception e) {
e.printStackTrace();
}
}
}
结果:

Loading Point

x=0,y=1

(责任编辑:龚勋)

特别的JAVA语法格式

未分类 | Posted by 子非舟
Apr 25 2011

1.class Candy {
static {
System.out.println("Loading Candy");
}
}

深入研究java.lang.Class类

未分类 | Posted by 子非舟
Apr 25 2011

【转】http://lavasoft.blog.51cto.com/62575/15433 leizhimin 的BLOG

Java程序在运行时,Java运行时系统一直对所有的对象进行所谓的运行时类型标识。这项信息纪录了每个对象所属的类。虚拟机通常使用运行时类型信息选准正确方法去执行,用来保存这些类型信息的类是Class类。Class类封装一个对象和接口运行时的状态,当装载类时,Class类型的对象自动创建。
Class 没有公共构造方法。Class 对象是在加载类时由 Java 虚拟机以及通过调用类加载器中的 defineClass 方法自动构造的,因此不能显式地声明一个Class对象。
虚拟机为每种类型管理一个独一无二的Class对象。也就是说,每个类(型)都有一个Class对象。运行程序时,Java虚拟机(JVM)首先检查是否所要加载的类对应的Class对象是否已经加载。如果没有加载,JVM就会根据类名查找.class文件,并将其Class对象载入。
基本的 Java 类型(boolean、byte、char、short、int、long、float 和 double)和关键字 void 也都对应一个 Class 对象。
每个数组属于被映射为 Class 对象的一个类,所有具有相同元素类型和维数的数组都共享该 Class 对象。
一般某个类的Class对象被载入内存,它就用来创建这个类的所有对象。
一、如何得到Class的对象呢?有三种方法可以的获取:
1、调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法。例如:
MyObject x;
Class c1 = x.getClass();
2、使用Class类的中静态forName()方法获得与字符串对应的Class对象。例如:
Class c2=Class.forName("MyObject"),Employee必须是接口或者类的名字。
3、获取Class类型对象的第三个方法非常简单。如果T是一个Java类型,那么T.class就代表了匹配的类对象。例如
Class cl1 = Manager.class;
Class cl2 = int.class;
Class cl3 = Double[].class;
注意:Class对象实际上描述的只是类型,而这类型未必是类或者接口。例如上面的int.class是一个Class类型的对象。由于历史原因,数组类型的getName方法会返回奇怪的名字。
二、Class类的常用方法
1、getName()
一个Class对象描述了一个特定类的属性,Class类中最常用的方法getName以 String 的形式返回此 Class 对象所表示的实体(类、接口、数组类、基本类型或 void)名称。
2、newInstance()
Class还有一个有用的方法可以为类创建一个实例,这个方法叫做newInstance()。例如:
x.getClass.newInstance(),创建了一个同x一样类型的新实例。newInstance()方法调用默认构造器(无参数构造器)初始化新建对象。
3、getClassLoader()
返回该类的类加载器。
4、getComponentType()
返回表示数组组件类型的 Class。
5、getSuperclass()
返回表示此 Class 所表示的实体(类、接口、基本类型或 void)的超类的 Class。
6、isArray()
判定此 Class 对象是否表示一个数组类。
三、Class的一些使用技巧
1、forName和newInstance结合起来使用,可以根据存储在字符串中的类名创建对象。例如
Object obj = Class.forName(s).newInstance();
2、虚拟机为每种类型管理一个独一无二的Class对象。因此可以使用==操作符来比较类对象。例如:
if(e.getClass() == Employee.class)...

JAVA基础--JAVA中的反射机制详解

未分类 | Posted by 子非舟
Apr 25 2011

JAVA反射机制
【转】发布时间:2007.09.25 04:42 来源:赛迪网技术社区 作者:baoclhttp://java.ccidnet.com/art/3539/20070924/1222147_1.html
JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。
Java反射机制主要提供了以下功能: 在运行时判断任意一个对象所属的类;在运行时构造任意一个类的对象;在运行时判断任意一个类所具有的成员变量和方法;在运行时调用任意一个对象的方法;生成动态代理。
1. 得到某个对象的属性

1 public Object getProperty(Object owner, String fieldName) throws Exception {
2 Class ownerClass = owner.getClass();
3
4 Field field = ownerClass.getField(fieldName);
5
6 Object property = field.get(owner);
7
8 return property;
9 }
Class ownerClass = owner.getClass():得到该对象的Class。

Field field = ownerClass.getField(fieldName):通过Class得到类声明的属性。

Object property = field.get(owner):通过对象得到该属性的实例,如果这个属性是非公有的,这里会报IllegalAccessException。

2. 得到某个类的静态属性

1 public Object getStaticProperty(String className, String fieldName)
2 throws Exception {
3 Class ownerClass = Class.forName(className);
4
5 Field field = ownerClass.getField(fieldName);
6
7 Object property = field.get(ownerClass);
8
9 return property;
10 }

Class ownerClass = Class.forName(className) :首先得到这个类的Class。

Field field = ownerClass.getField(fieldName):和上面一样,通过Class得到类声明的属性。

Object property = field.get(ownerClass) :这里和上面有些不同,因为该属性是静态的,所以直接从类的Class里取。

3. 执行某对象的方法

1 public Object invokeMethod(Object owner, String methodName, Object[] args) throws Exception {
2
3 Class ownerClass = owner.getClass();
4
5 Class[] argsClass = new Class[args.length];
6
7 for (int i = 0, j = args.length; i < j; i++) {
8 argsClass[i] = args[i].getClass();
9 }
10
11 Method method = ownerClass.getMethod(methodName, argsClass);
12
13 return method.invoke(owner, args);
14 }
Class owner_class = owner.getClass() :首先还是必须得到这个对象的Class。

5~9行:配置参数的Class数组,作为寻找Method的条件。

Method method = ownerClass.getMethod(methodName, argsClass):通过Method名和参数的Class数组得到要执行的Method。

method.invoke(owner, args):执行该Method,invoke方法的参数是执行这个方法的对象,和参数数组。返回值是Object,也既是该方法的返回值。

4. 执行某个类的静态方法

1 public Object invokeStaticMethod(String className, String methodName,
2 Object[] args) throws Exception {
3 Class ownerClass = Class.forName(className);
4
5 Class[] argsClass = new Class[args.length];
6
7 for (int i = 0, j = args.length; i < j; i++) {
8 argsClass[i] = args[i].getClass();
9 }
10
11 Method method = ownerClass.getMethod(methodName, argsClass);
12
13 return method.invoke(null, args);
14 }

基本的原理和实例3相同,不同点是最后一行,invoke的一个参数是null,因为这是静态方法,不需要借助实例运行。

5. 新建实例
1
2 public Object newInstance(String className, Object[] args) throws Exception {
3 Class newoneClass = Class.forName(className);
4
5 Class[] argsClass = new Class[args.length];
6
7 for (int i = 0, j = args.length; i < j; i++) {
8 argsClass[i] = args[i].getClass();
9 }
10
11 Constructor cons = newoneClass.getConstructor(argsClass);
12
13 return cons.newInstance(args);
14
15 }

这里说的方法是执行带参数的构造函数来新建实例的方法。如果不需要参数,可以直接使用newoneClass.newInstance()来实现。

Class newoneClass = Class.forName(className):第一步,得到要构造的实例的Class。

第5~第9行:得到参数的Class数组。

Constructor cons = newoneClass.getConstructor(argsClass):得到构造子。

cons.newInstance(args):新建实例。

6. 判断是否为某个类的实例

1 public boolean isInstance(Object obj, Class cls) {
2 return cls.isInstance(obj);
3 }

7. 得到数组中的某个元素
1 public Object getByArray(Object array, int index) {
2 return Array.get(array,index);
3 }

【转】Restlet 框架学习--部分代码2

未分类 | Posted by 子非舟
Apr 19 2011

http://hi.baidu.com/%D4%AA%D4%C2%C8%FD%CA%AE%B6%FE%BA%C5/blog/item/f2c859f8bd1ae469024f56aa.html

/**
* Prints the resource's representation.
*
* @param client
* client Restlet.
* @param reference
* the resource's URI.
* @throws IOException
*/
public static void get(Client client, Reference reference) throws IOException
{
Response response = client.get(reference);
if (response.getStatus().isSuccess()) {
if (response.isEntityAvailable()) {
response.getEntity().write(System.out);
}
}
}

/**
* Try to update an item.
*
* @param item
* the item.
* @param client
* the Restlet HTTP client.
* @param itemUri
* the resource's URI.
*/
public static boolean updateItem(Item item,
Client client,
Reference itemUri) {
// Gathering informations into a Web form.
Form form = new Form();
form.add("name", item.getName());
form.add("description", item.getDescription());
Representation rep = form.getWebRepresentation();

// Launch the request
Response response = client.put(itemUri, rep);
return response.getStatus().isSuccess();
}

/**
* Try to delete an item.
*
* @param client
* the Restlet HTTP client.
* @param itemUri
* the resource's URI.
*/
public static boolean deleteItem(Client client, Reference itemUri) {
// Launch the request
Response response = client.delete(itemUri);
return response.getStatus().isSuccess();
}
}
Hander:
extends Object
Final handler of calls typcially created by Finders.Handler 的实例允许对处理的请求在一个线程安全的上下文

中运行.
-------------------------------------------------------------------------------------------------------
Uniform:
extends Object
显露了统一的REST接口的基类.REST架构风格与其他基于网络的开发风格的重要特征是强调组件之间的一个统一接口。
-------------------------------------------------------------------------------------------------------
Resource:
extends Handler
资源表示超连接资源目标中的概念
Intended conceptual target of a hypertext reference. "Any information that can be named can be a
resource: a document or image, a temporal service (e.g. "today's weather in Los Angeles"),
-------------------------------------------------------------------------------------------------------
Variant:
资源的可能表现形式的描述
Descriptor for available representations of a resource. It contains all the important metadata about a
representation but is not able to actually serve the representation's content itself.
--------------------------------------------------------------------------------------------------------
Representation:
表现用来展示资源(Resource)当前或即将的状态.
The content of a representataion can be retrieved several times if there is a stable and accessible
source,like a local file or a string.When the representation is obtained via a temporary source like a
network socket,
-------------------------------------------------------------------------------------------------------
Client:
Connector acting as a generic client.[作为一个通用客户端的连接器].
-------------------------------------------------------------------------------------------------------
Reference:
Reference to a URI.与Java中的URI类相比,这个接口表示互变的引用.
-------------------------------------------------------------------------------------------------------
Form:
Form是一个专门的可修改的参数列表.
-------------------------------------------------------------------------------------------------------
Restlet:
Restlet是一个提供上下文和生命周期支持的统一类。它有很多子类聚焦在采用特别方式对请求进行处理。
The context property is typically provided by a parent Component as a way to encapsulate access to

shared features such as logging and client connector.
-------------------------------------------------------------------------------------------------------
Application:
Restlet子类,可以被attached到一个或多个虚拟主机。
Application同样也有很多有用的关系服务。
@"connectorService" to declare necessary client and server connector.
#"decoderService" to automatically decode or decompress request entities.
#"metadataService" to provide access to metadata and their associated extension names.
#"statusService" to provide common representations for exception status.
# "taskService" to run tasks asynchronously.
# "tunnelService" to tunnel method names or client preferences via query parameters.
-------------------------------------------------------------------------------------------------------
Component:
Restlet子类,用来管理一系列连接器、虚拟主机和应用程序。应用Application.Application are expected to be directly attached to VirtualHosts. Components are expose several services: access logging and status setting.
从软件架构的角度,组件是什么?
Component is an abstract unit of software instruction and internal state that provides a tranformation
of data via its interface"...
-------------------------------------------------------------------------------------------------------
Virtual Host: extends Router
将来自Server连接器的请求路由到Restlets. The attached Restlets are typically Application.
虚拟主机下的三个类:

Route attach(Restlet target)
Attaches a target Restlet to this router with an empty URI pattern.
Route attach(String uriPattern, Restlet target)
Atttaches a target Restlet to this router based on a given URI pattern.
Route attachDefault(Restlet defaultTarget)

认识环境变量

未分类 | Posted by 子非舟
Apr 17 2011

CLASSPATH:用来指定我们自己所写的或要用到的类文件 (.jar文件) 所在的目录。

Java虚拟机(JVM)借助类装载器装入应用程序使用的类,具体装入哪些类根据当时的需要决定。CLASSPATH环境变量告诉类装载器到哪里去寻找第三方提供的类和用户定义的类。转自http://www.yesky.com/60/1817560.shtml

【转】Restlet 指南

未分类 | Posted by 子非舟
Apr 17 2011

关于本指南


本指南的翻译工作经过了Restlet社区的官方授权,cleverpig作为贡献者完成了本文的翻译和整理工作。在此发布Matrix社区试读版的目的是为了让更多的技术爱好者阅读并提出翻译中的不足之处,以提高本指南的质量,以期修改后正式发布。

Servlet的限制

在2003年末,Jetty Web容器的作者、Servlet规范的贡献者:Greg Wilkins在其博客上对Servlet的问题进行了如下总计:

* 没有对协议与应用之间的关系进行清洗的划分。
* 由于在设计Servlet时存在对阻塞IO的假设,因此不能充分利用非阻塞NIO机制。
* 所有的Servlet Web容器对于某些应用来讲是过度设计的。

他提出构思新的API规范,使其能够真实地脱离协议,并定义能够暴露内容和元数据的contentlets。这些想法就是Restlet项目创建的灵感源泉。在之后的文章中,Greg Wilkins解释了为什么当前Servlet API限制非阻塞NIO API得到高效使用的详细理由:这种传统的用法针对每个HTTP请求都创建独立的线程进行处理。并提出了他对下一代Servlet技术的设想

另一个主要问题就是Servlet API鼓励应用开发者在应用或者用户会话级别直接将session状态保存于内存中,尽管这看上去不错,但它造成了Servlet容器扩展性和高可用性的主要问题。为了克服这些问题,就必须实现复杂的负载均衡、session复制、持久化机制。这导致了可扩展性必然成为灾难。

Restlet简介

当复杂核心化模式日趋强大之时,面向对象设计范例已经不总是Web开发中的最佳选择,Java开发者需要认识到这一点,并且在开发新的Web服务端或是AJAX Web客户端时开始思考更加RESTfully的设计。Restlet这个开源项目为那些要采用REST结构体系来构建应用程序的Java开发者提供了一个具体的解决方案。它的非常简单易用的功能和RESTfully的Web框架,这使其成为了Web2.0开发中的又一利器。好吧,朋友们,下面就让我们开始Restlet探索之旅吧!

1. 注册一个Restlet实现

Restlet框架由两部分构成。第一部分是"Restlet API", 这个中立的API完美地实现了REST概念并简化了客户端和服务端应用的调用处理。在使用它之前,我们还需要一个支持此API的Restlet实现。Restlet的诸多实现可以通过开源项目或者商业产品获得。

API与实现的分离和Servlet API与web容器的分离(就像Jetty或Tomcat)、JDBC API与相应JDBC驱动的分离非常类似。目前,"Noelios Restlet Engine" (缩写为NRE)是Restle tAPI的参考实现之一。当下载Restlet发布版本时,API和NRE就绑定在一起,以备随时使用。如果你需要使用不同的实现,那么只需要添加JAR 文件到classpath,并删除com.noelios.restlet.jar这个NRE的JAR文件即可。

API实现的注册过程是完全自动的,如果你对此存在疑问,那么请参考JAR规范。当完成实现装载工作后,它将自动回调org.restlet.util.Engine.setInstance()方法,来进行自注册。

2. 接收Web页面的内容

正如我们在Restlet介绍中所提到的,Restlet框架即是一个客户端,又是一个服务端框架。例如,NRE能够简单地通过它的HTTP客户端 connector(连接器)访问远程资源。在REST中,connector是一种软件元素,它使两个component(组件)之间能够进行通讯,其典型的实现方式是通过某种网络协议完成通讯。NRE提供了多种客户端connector实现,这些实现都基于现存的开源项目。在connector一节中,列举出了所有可用的客户端、服务端connector,并解释了如何使用和配置它们。

下面,我们将获取一个现存资源的表示法(representation )并将其输出在JVM控制台:

// Outputting the content of a Web page
Client client = new Client(Protocol.HTTP);
client.get("http://www.restlet.org").getEntity().write(System.out);

请注意上面的示例使用了最简单的方式:通过通用的客户端类(generic Client class)调用。更加灵活的方式是创建一个新的Request对象,然后请求客户端去处理它。下面的示例展示了如何在调用时设置首选项(例如 referrer URI)。当然,也可以是接收回应时的首选语言或者媒体类型:

// Prepare the request
Request request = new Request(Method.GET, "http://www.restlet.org");
request.setReferrerRef("http://www.mysite.org");
// Handle it using an HTTP client connector Client client = new Client(Protocol.HTTP); Response response = client.handle(request);
// Write the response entity on the console Representation output = response.getEntity(); output.write(System.out);

3. 侦听浏览器

现在,我们将了解一下Restlet框架是如何侦听客户端请求并作出回应的。这里,我们选用了NRE HTTP服务端connector(例如基于Jetty的HTTP服务端connector),返回简单的字符串表达式“Hello World!”。请注意在更加实际的应用中,我们可以创建一个独立的类,此类继承自Restlet类,而不依靠这里的匿名内部类。

Restlet类与Servlet非常相似,并且在RESTful应用中处理调用时提供了有限的帮助。我们后面将看到一个提供了一些特定子类的框架,它能够更抽象、简单地进行处理。下面让我们先看一个简单的示例:

// Creating a minimal Restlet returning "Hello World"
Restlet restlet = new Restlet() {
    @Override
    public void handle(Request request, Response response) {
        response.setEntity("Hello World!", MediaType.TEXT_PLAIN);
    }
};
// Create the HTTP server and listen on port 8182 new Server(Protocol.HTTP, 8182, restlet).start();

如果你运行并启动服务端,那么你可以打开浏览器输入http://localhost:8182。实际上,输入任何的URI都可以工作,你也可以尝试一下http://localhost:8182/test/tutorial。值得注意的是,如果你从另一台服务器上测试服务端,那么就需要将localhost替换为服务器的IP地址或者它的域名。

4. REST架构概述

让我们先从REST的视角审视一下典型的web架构。在下面的图表中,端口代表了connector,而后者负责component之间的通讯(组件在图中被表示为大盒子)。链接代表了用于实际通讯的特定协议(HTTP,SMTP等)。
image

请注意,同一个component能够具有任何数量的客户端/服务端connector。例如,Web服务器B就具有一个用于回应用户代理组件(User Agent component)的服务端connector,和多个发送请求到其它服务端的客户端connector。

5. Component、virtual hosts和applications

另外,为了支持前面所表述的标准REST软件架构元素,Restlet框架也提供了一套类:它们极大地简化了在单一JVM中部署多个应用的工作。其目的在于提供一种RESTful、可移植的、比现存的Servlet API更加灵活的框架。在下面的图表中,我们将看到三种Restlet,它们用于管理上述复杂情况:Components能够管理多个Virtual Hosts和Applications。Virtual Hosts支持灵活的配置,例如同一个IP地址能够分享多个域名、使用同一个域名实现跨越多个IP地址的负载均衡。最后,我们使用应用去管理一套相关的 Restlet、Resource、Representations。另外,应用确保了在不同Restlet实现、不同Virtual Hosts之上的可移植性和可配置性。这三种Restlet的协助为我们提供了众多的功能:譬如访问日志、请求自动解码、配置状态页设置等。image

为了展示这些类,让我们尝试一个简单的示例。首先,我们创建一个component,然后在其上添加一个HTTP服务端connector,并侦听 8182端口。接着创建一个简单的、具有追踪功能的Restlet,将它放置到组件默认的Virtual Hosts上。这个默认的主机将捕捉那些没有路由到指定Virtual Hosts的请求(详见Component.hosts属性)。在后面的一个示例中,我们还将介绍应用类的使用方法。请注意,目前你并不能在控制台输出中看到任何的访问日志。

// Create a new Restlet component and add a HTTP server connector to it
Component component = new Component();
component.getServers().add(Protocol.HTTP, 8182);
// Create a new tracing Restlet Restlet restlet = new Restlet() { @Override public void handle(Request request, Response response) { // Print the requested URI path String message = "Resource URI : " + request.getResourceRef() + ' ' + "Root URI : " + request.getRootRef() + ' ' + "Routed part : " + request.getResourceRef().getBaseRef() + ' ' + "Remaining part: " + request.getResourceRef().getRemainingPart(); response.setEntity(message, MediaType.TEXT_PLAIN); } };
// Then attach it to the local host component.getDefaultHost().attach("/trace", restlet);
// Now, let's start the component! // Note that the HTTP server connector is also automatically started. component.start();

让我们通过在浏览器中输入http://localhost:8182/trace/abc/def?param=123来进行测试,得到测试结果如下:

Resource URI  : http://localhost:8182/trace/abc/def?param=123
Root URI      : http://localhost:8182/trace
Routed part   : http://localhost:8182/trace
Remaining part: /abc/def?param=123

6. 为静态文件提供服务

你遇到过提供静态页面(类似Javadocs)服务的web应用?如果正在使用,那么可以直接编写一个Directory类,而无需为它建立Apache服务。请见下面如何使用:

// Create a component
Component component = new Component();
component.getServers().add(Protocol.HTTP, 8182);
component.getClients().add(Protocol.FILE);
// Create an application Application application = new Application(component.getContext()) { @Override public Restlet createRoot() { return new Directory(getContext(), ROOT_URI); } };
// Attach the application to the component and start it component.getDefaultHost().attach("", application); component.start();

正如你所注意到的,我们通过传递应用的父组件上下文(context)的方式来实例化应用,而不是在第5章中提到的代码那样简单。而其主要原因是应用在分配客户端请求时,请求的分配工作需要客户端connector来完成,而后者被component所控制,并在所有被包含其中的应用之间贡献。

为了运行此示例,你需要为ROOT_URI提供一个有效值,该值依赖于你的Restlet安装路径。默认情况下,它被设置为"file:///D: /Restlet/www/docs/api/"。请注意,这里不需要任何附加的配置。如果你希望自定义在文件扩展名和元数据(metadata,包括媒体类型、语言、编码等)之间的映射,或是提供一个与众不同的索引名,你可以使用应用的“metadataService”属性。

7. 访问日志

有目的地记录web应用的活动是一种常见的需求。Restlet组件能够在默认的情况下生成类似Apache风格的日志、甚至自定义日志。通过使用 JDK内置的日志功能,logger能够配置为像任何标准JDK日志那样过滤信息、对它们进行重新格式化或者发送它们到指定位置。并且支持日志的循环(rotation);细节请查看java.util.logging包。

值得注意的是,你能够通过修改component的"logService"属性来为java.util.logging框架自定义logger名。如果希望完全掌控日志的配置,你需要通过设置系统属性来声明一个配置文件:

System.setProperty("java.util.logging.config.file", "/your/path/logging.config");

关于配置文件格式的细节,请查看JDK的LogManager类。

8. 显示错误页

另外一个常见的需求是:在调用处理过程中某些期望结果没有出现时,能够自定义返回的状态页面。也许它是某个资源没有找到或者一个可接受的表示是无效的。在这种情况下,或者遇到任何无法处理的异常时,Application或者Component将自动提供一个默认的状态页面。此服务与 org.restlet.util.StatusService类相关联,并可以作为被称为“statusService”的Application或者 Component的属性而被访问。

为了自定义默认的信息,你只需要简单地创建StatusService类的子类,并覆盖其getRepresentation(Status, Request, Response)方法。然后设置这个类的实例为指定的“statusService”属性即可。

9. 对敏感资源的访问保护

当你需要保护对某些Restlet的访问时,可以使用下面的方法:一种通用的方法是依靠cookie来识别客户端(或者客户端session),并根据你的应用状态检查给定的用户ID或者session ID,从而判断次访问是否被允许。Restlet通过访问Request或者Response中的CookieCookieSetting对象支持cookie。

另一种方法是基于标准HTTP认证机制。Neolios Restlet引擎目前允许基于简单HTTP方案的证书发送、接收和基于Amazon Web服务方案的证书发送。

当接收到调用时,开发者能够通过Request.challengeResponse.identifier/secret类中的Guard filter(保护过滤器)使用已经解析好的证书。过滤器是一种特殊的Restlet,它能够在调用相应Restlet之前进行预处理,或者在相应 Restlet调用返回后进行后期处理。如果你熟知Servlet API,这里的过滤器概念和Servlet API中的Filter接口非常接近。看一下我们如何修改从前的代码来对目录访问进行访问保护:

// Create a Guard
Guard guard = new Guard(getContext(),
        ChallengeScheme.HTTP_BASIC, "Tutorial");
guard.getSecrets().put("scott", "tiger".toCharArray());
// Create a Directory able to return a deep hierarchy of files Directory directory = new Directory(getContext(), ROOT_URI); guard.setNext(directory); return guard;

image

请注意:认证和授权的最终结果是完全可定制的,这只需要通过authenticate()和authorize()方法便可完成。任何自定义的机制都能够被用来检查给定的证书是否有效、通过认证的用户是否被授权继续访问相应Restlet。下面是我们简单地硬编码了用户、密码对。为了测试,我们使用了客户端Restlet API:

// Prepare the request
Request request = new Request(Method.GET, "http://localhost:8182/");
// Add the client authentication to the call ChallengeScheme scheme = ChallengeScheme.HTTP_BASIC; ChallengeResponse authentication = new ChallengeResponse(scheme, "scott", "tiger"); request.setChallengeResponse(authentication);
// Ask to the HTTP client connector to handle the call Client client = new Client(Protocol.HTTP); Response response = client.handle(request);
if (response.getStatus().isSuccess()) { // Output the response entity on the JVM console response.getEntity().write(System.out); } else if (response.getStatus() .equals(Status.CLIENT_ERROR_UNAUTHORIZED)) { // Unauthorized access System.out .println("Access authorized by the server, " + "check your credentials"); } else { // Unexpected status System.out.println("An unexpected status was returned: " + response.getStatus()); }

你可以修改这里的user ID或者password,来检查服务端返回的response。请别忘记了在启动客户端之前,先执行Restlet服务端程序。请注意,如果你从另一台机器上测试服务端,那么在浏览器中输入URI时需要将"localhost"替换为服务器的IP地址或者域名。由于使用了默认接收任何类型URI的 VirtualHost,因此服务端无需任何修改。

10. URI重写和重定向

Restlet框架的另一个优点是对cool URI的内建支持。Jacob Nielsen在他的AlertBox中给出了对URI设计的重要性的绝佳描述

首先介绍的工具是Redirector,它能够将cool URI重写为另一个URI,并接着进行相应的自动重定向。这里支持一些重定向类型:通过客户端/浏览器的外部重定向、类似代理行为的connector重定向。在下面的例子中,我们将基于Google为名为"mysite.org"的站点定义一个检索服务。与URI相关的"/search"就是检索服务,它通过"kwd"参数接收一些检索关键字:

// Create an application
Application application = new Application(component.getContext()) {
    @Override
    public Restlet createRoot() {
        // Create a Redirector to Google search service
        String target =
           "http://www.google.com/search?q=site:mysite.org+{keywords}";
        return new Redirector(getContext(), target,
                Redirector.MODE_CLIENT_TEMPORARY);
    }
};
// Attach the application to the component's default host Route route = component.getDefaultHost().attach("/search", application);
// While routing requests to the application, extract a query parameter // For instance : // http://localhost:8182/search?kwd=myKeyword1+myKeyword2 // will be routed to // http://www.google.com/search?q=site:mysite.org+myKeyword1%20myKeyword2 route.extractQuery("keywords", "kwd", true);

请注意,Redirector只需要三个参数。第一个参数是父级上下文,第二个参数定义了如何基于URI模板重写URI。这里的URI模板将被Template类处理。第三个参数定义了重定向类型:出于简化的目的,我们选择了客户端重定向。

同时,当调用被传递给application时,我们使用了Route类从request中提取查询参数“kwd”。如果发现参数,参数将被复制到request的“keywords”属性中,以便Redirector在格式化目标URI时使用。

11. 路由器和分层URI

作为Redirector的补充,我们还具有另一个管理cool URI的工具:Router(路由器)。它们是一种特殊的Restlet,能够使其它Restlet(例如Finder和Filter)依附于它们,并基于URI模板进行自动委派调用(delegate call)。通常,你可以将Router设置为Application的根。

这里,我们将解释一下如何处理下面的URI模板:

1. /docs/ 用于显示静态文件
2. /users/{user} 用于显示用户帐号
3. /users/{user}/orders 用于显示特定用户的所有订单
4. /users/{user}/orders/{order} 用于显示特定的订单

实际上,这些URI包含了可变的部分(在大括号中)并且没有文件扩展名,这在传统的web容器中很难处理。而现在,你只需要做的只是使用URI模板将目标Restlet附着到Router上。在Restlet框架运行时,与request的URI最为匹配的Route将接收调用,并调用它所附着的 Restlet。同时,request的属性表也将自动更新为URI模板变量。
image

请看下面的具体实现代码。在真实的应用中,你可能希望创建单独的子类来代替我们这里使用的匿名类:

// Create a component
Component component = new Component();
component.getServers().add(Protocol.HTTP, 8182);
component.getClients().add(Protocol.FILE);
// Create an application Application application = new Application(component.getContext()) { @Override public Restlet createRoot() { // Create a root router Router router = new Router(getContext());
// Attach a guard to secure access to the directory Guard guard = new Guard(getContext(), ChallengeScheme.HTTP_BASIC, "Restlet tutorial"); guard.getSecrets().put("scott", "tiger".toCharArray()); router.attach("/docs/", guard);
// Create a directory able to expose a hierarchy of files Directory directory = new Directory(getContext(), ROOT_URI); guard.setNext(directory);
// Create the account handler Restlet account = new Restlet() { @Override public void handle(Request request, Response response) { // Print the requested URI path String message = "Account of user \"" + request.getAttributes().get("user") + "\""; response.setEntity(message, MediaType.TEXT_PLAIN); } };
// Create the orders handler Restlet orders = new Restlet(getContext()) { @Override public void handle(Request request, Response response) { // Print the user name of the requested orders String message = "Orders of user \"" + request.getAttributes().get("user") + "\""; response.setEntity(message, MediaType.TEXT_PLAIN); } };
// Create the order handler Restlet order = new Restlet(getContext()) { @Override public void handle(Request request, Response response) { // Print the user name of the requested orders String message = "Order \"" + request.getAttributes().get("order") + "\" for user \"" + request.getAttributes().get("user") + "\""; response.setEntity(message, MediaType.TEXT_PLAIN); } };
// Attach the handlers to the root router router.attach("/users/{user}", account); router.attach("/users/{user}/orders", orders); router.attach("/users/{user}/orders/{order}", order);
// Return the root router return router; } };
// Attach the application to the component and start it component.getDefaultHost().attach(application); component.start();

请注意,变量的值是直接从URI中提取的,因此这是没有精确解码的。为了实现这样的工作,请查看手册中的decode(String)方法

12. 抵达目标资源

在前面的示例中,在从目标URI中提取那些有趣部分时,我们利用了Restlet框架非常灵活的路由特性对request进行路由。但是,我们没有注意request方法和客户端对于它所期望的response的偏好。于是,我们如何才能将Restlet处理器和后台系统、域对象联系在一起呢?

到目前为止,我们已经介绍了一些在Restlet中超越传统Servlet API的特性。但我们并没有在"Restlet"这个框架名称中使用"REST"。如果你还没有做的话,我推荐你学习一些关于REST架构风格和将其应用于Web应用的最佳实践。这里提供了相关的FAQ记录,希望能给你一些启示,同时我们也运营着很有用的REST搜索引擎(基于Google)。如果你对传统MVC框架有一定了解,那么你可以阅读一下另一个FAQ记录,它提供了对MVC与Restlet关系的详细说明。
image

总结一下,request中含有标识目标资源的URI,而目标资源就是调用的主旨。这种资源信息被保存在Request.resourceRef属性中,并能够像我们之前所见那样服务于路由机制。因此在处理request时的首要目标就是发现目标资源。。。Resource类的实例或者其子类中的某个。为了帮助我们完成此项任务,我们可以使用专用的Finder,一个Restlet子类,它将Resource类引用作为参数并在request到来时自动实例化它。然后Finder将动态将调用分配给最新创建的实例,实际上就是根据request方法调用它的handle*()方法中的某一个(handleGet,handleDelete等)。当然,我们可以自定义这种行为,甚至使用Router的attach()方法,将URI模板和 Resource类作为其参数透明地创建Finder!现在,让我们看一下展示了示例中主框架类之间关系的全景图表:

回到代码中,我们在这里重构了Application.createRoot()方法。出于简化目的,我们没有提供具有静态文件的目录。你可以发现将Resource类直接指派给Router的方法。

// Create a router
Router router = new Router(getContext());
// Attach the resources to the router router.attach("/users/{user}", UserResource.class); router.attach("/users/{user}/orders", OrdersResource.class); router.attach("/users/{user}/orders/{order}", OrderResource.class);
// Return the root router return router;

我们最后将重审一下UserResource类。这个类继承自org.restlet.resource.Resource类,因此它覆盖了具有三个参数的构造方法。此方法初始化了"context"、"request"和"response"属性。接着,我们使用从"/users/{user} "URI模板中提取出的"user"属性,将它的值保存在一个方便使用的成员变量中。然后,我们便可以在整个application中查找与"user" 相关的域对象了。最终,我们声明了用于暴露给用户的表示变量(representation variants),在这个简单的例子中只是文字而已。它将用于在运行时透明地完成一些内容导航,以便为每个request选择适合的变量,所有这些工作都是透明的。

public class UserResource extends Resource {
    String userName;
Object user;
public UserResource(Context context, Request request, Response response) { super(context, request, response); this.userName = (String) request.getAttributes().get("user"); this.user = null; // Could be a lookup to a domain object.
// Here we add the representation variants exposed getVariants().add(new Variant(MediaType.TEXT_PLAIN)); }
@Override public Representation getRepresentation(Variant variant) { Representation result = null; if (variant.getMediaType().equals(MediaType.TEXT_PLAIN)) { result = new StringRepresentation("Account of user \"" + this.userName + "\""); } return result; } }

你可以查看本指南中提供的代码包并对应用进行测试,并能够以仅接受Get请求的方式获得在第十一章中的相同行为。如果你希望使用PUT方法,那么就需要在UserResource中创建一个"allowPut()"方法并简单地返回"true",并且添加一个"put (Representation)"方法来处理调用。关于详细内容请查阅Restlet的Javadocs。

结论

我们已经涵盖了Restlet框架的许多方面。在你打算行动之前,让我们先回顾一下展示了本指南的主要概念和它们之间关系的两个层次图表:
image

这里是核心表示类:
image

除了本指南,你最好的信息来源就是Restlet API的Javadocs、Restlet扩展和NRE。还可以阅读一下connector一节,它列举出了客户端和服务端connector,并解释了如何使用、配置它们。集成一节列出了提供可插入特性的所有可用扩展:例如与servlet容器的集成、动态表示的生成等。你还可以在我们的讨论组中提出问题并帮助别人。

原文地址:http://my.oschina.net/javagg/blog/3254