RESTful webservice相比SOAP webservice复杂度低很多,REST鼓励无状态设计,完全由http协议,且返回值为json
本文设计基于Servlet请求转发的一个超轻量级的REST框架(某种程度也可视为MVC框架)
类UML如下图:
ClassParser扩展自ClassVisitor用于扫描指定路径下的class文件,并建立url同处理器的对应关系
ProcessDesc描述了一个处理器,包含请求的url、调用的单例fqcn、方法名、参数映射列表
ProcessorManager实现了具体的创建请求url同处理器的对应关系,并提供运行时执行引擎
RequestHandlerServlet为前端执行分发器,需要在web.xml中进行配置
设计三种注解:
Resource:用于标注类为处理器
Path:用于标注方法对应的url路径
RequestVariable:用于标注方法参数映射的Http请求的参数名字
实现一个简单的处理器类:
@Resource
public class ActionResource {
@Path("/a/b")
public String getId(@RequestVariable("tid") String tid,
@RequestVariable("bid") String bid) {
return "hello "+tid+":"+bid;
}
}
在浏览器中输入 localhost:8080/WebHandler/a/b?tid=100&bid=haha
则返回:
至此,实现了简单的REST框架,原理即是将所有HTTP请求都映射到RequestHandlerSevlet,ProcessorManager在Servlet.init阶段扫描工程路径下的所有class文件,并建立对应关系,Servlet处理http请求时根据不同的url分发到指定的处理器(即上图的ActionResource),处理器应当是POJO的。
核心源码如下:
package test;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.objectweb.asm.ClassReader;
/**
* 处理器管理器类,提供: 指定路径的类文件并建立url同处理器描述的对应关系 运行时根据url获得指定处理器描述
*
* @author dingchunda
*
*/
public class ProcessorManager {
private static ProcessorManager single;
// key-fqcn value-object
private Map singleObject = new HashMap();
// key-desc value-ProcessDesc
private Map mapping = new HashMap();
/**
* 将path目录下的所有class文件扫描,建立url到处理器的初始映射
*
* @param path
* @throws DOFException
*/
public void scan(String path) throws DOFException {
// 获取当前目录列表
File base = new File(path);
File[] childrenFiles = base.listFiles();
for (File childFile : childrenFiles) {
if (childFile.isDirectory()) {
scan(childFile.getAbsolutePath());
} else if (childFile.isFile()
&& childFile.getName().endsWith(".class")) {
scanFile(childFile);
}
}
}
/**
* 扫描当前文件
*
* @param file
* @throws DOFException
*/
private void scanFile(File file) throws DOFException {
ClassParser parser = null;
try {
FileInputStream fin = new FileInputStream(file);
// 使用asm工具访问类文件
parser = new ClassParser();
ClassReader reader = new ClassReader(fin);
reader.accept(parser, 0);
} catch (IOException e) {
String str = "parse class file failed:" + e.toString();
throw new DOFException(str, e, DOFException.DEFAULT);
}
// 如果class文件存在类注解Resource则进行解析
if (parser.isResouce()) {
String fqcn = parser.getFQCN();
// 如果已经检测过,则不进行解析
if (!singleObject.containsKey(fqcn)) {
// 创建单例对象
try {
Class> c = Class.forName(fqcn);
Object o = c.newInstance();
singleObject.put(fqcn, o);
} catch (Exception e) {
String str = "create singleton object failed:"
+ e.toString();
throw new DOFException(str, e, DOFException.DEFAULT);
}
List descs = parser.getMethodProcessDesc();
for (ProcessDesc desc : descs) {
mapping.put(desc.getURL(), desc);
}
}
}
}
public boolean isRoutable(String url) {
return mapping.containsKey(url);
}
/**
* 方法调用
*
* @param url
* 请求的url
* @param params
* 运行时方法参数
* @return json
*
* @throws DOFException
*/
public String invoke(String url, Map params)
throws DOFException {
// 根据url获取处理器描述
ProcessDesc desc = mapping.get(url);
// 获取处理器参数列表描述
String[] paramVariables = desc.getParamNames();
int paramLen = paramVariables.length;
// 组装方法参数类型,均为String类型
Class>[] paramTypes = new Class>[paramVariables.length];
for (int i = 0; i < paramLen; i++) {
paramTypes[i] = String.class;
}
// 获取方法句柄
Object o = singleObject.get(desc.getFqcn());
Method m = null;
try {
m = o.getClass().getMethod(desc.getMethod(), paramTypes);
} catch (Exception e) {
String str = "get method handler failed:" + e.toString();
throw new DOFException(str, e, DOFException.DEFAULT);
}
// 组装运行时参数
Object[] runtimeParams = new String[paramLen];
for (int i = 0; i < paramLen; i++) {
runtimeParams[i] = params.get(paramVariables[i])[0];
}
// 方法调用
String json = null;
try {
json = (String) m.invoke(o, runtimeParams);
} catch (Exception e) {
String str = "invoke handler failed:" + e.toString();
throw new DOFException(str, e, DOFException.DEFAULT);
}
return json;
}
/**
* 获取单例
*
* @return
*/
public synchronized static ProcessorManager getSingleInstance() {
if (single == null) {
single = new ProcessorManager();
}
return single;
}
}
---
package test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.tree.AnnotationNode;
import org.objectweb.asm.tree.MethodNode;
/**
* 类字节码访问器,从字节码中解析出处理器描述
* 一个处理器描述包含:
* 访问的url
* 方法名字
* 类全路径
* 请求参数映射表
* @see ProcessDesc
* @author dingchunda
*
*/
public class ClassParser implements ClassVisitor {
private static final String RESOURCE = "Resource";
private static final String PATH = "Path";
private static final String REQUEST = "RequestVariable";
private List visibleAnnotations = new ArrayList();
private String fqcn; // 类全路径 a/b/c
// key 方法名, value 方法描述
private Map methods = new HashMap();
public String getFQCN() {
return fqcn.replace("/", ".");
}
private final String getPrefix() {
int pos = fqcn.lastIndexOf("/");
return "L" + fqcn.substring(0, pos);
}
@Override
public void visit(int arg0, int arg1, String arg2, String arg3,
String arg4, String[] arg5) {
fqcn = arg2;
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
AnnotationNode an = new AnnotationNode(desc);
if (visible) {
visibleAnnotations.add(an);
}
return an;
}
@Override
public void visitAttribute(Attribute arg0) {
}
@Override
public void visitEnd() {
}
@Override
public FieldVisitor visitField(int arg0, String arg1, String arg2,
String arg3, Object arg4) {
return null;
}
@Override
public void visitInnerClass(String arg0, String arg1, String arg2, int arg3) {
}
@Override
public MethodVisitor visitMethod(int accessFlag, String methodName,
String desc, String signature, String[] exceptions) {
MethodNode node = new MethodNode(accessFlag, methodName, desc,
signature, exceptions);
methods.put(methodName, node);
return node;
}
@Override
public void visitOuterClass(String arg0, String arg1, String arg2) {
}
@Override
public void visitSource(String arg0, String arg1) {
}
/**
* 检查当前类是否使用Resouce注解标记
*
* @return
*/
public boolean isResouce() {
for (AnnotationNode annotation : visibleAnnotations) {
String checkString = getPrefix() + "/" + RESOURCE + ";";
if (checkString.equals(annotation.desc)) {
return true;
}
}
return false;
}
/**
* 获取当前类的映射方法集合
*
* @return
*/
@SuppressWarnings("unchecked")
public List getMethodProcessDesc() {
List list = new ArrayList();
for (Entry entry : methods.entrySet()) {
MethodNode methodNode = entry.getValue();
String methodName = entry.getKey();
List methodAnnotations = methodNode.visibleAnnotations;
if (methodAnnotations != null) {
for (AnnotationNode methodAnnotation : methodAnnotations) {
String checkString = getPrefix() + "/" + PATH + ";";
if (checkString.equals(methodAnnotation.desc)) {
String url = (String) methodAnnotation.values.get(1);
list.add(getSingleMethodProcessDesc(methodNode, url,
methodName));
break;
}
}
}
}
return list;
}
/**
* 解析单个方法
*
* @param methodNode
* @param url
* @param methodName
* @return
*/
@SuppressWarnings("unchecked")
private ProcessDesc getSingleMethodProcessDesc(MethodNode methodNode,
String url, String methodName) {
List[] pNodes = methodNode.visibleParameterAnnotations;
ArrayList requestVariables = new ArrayList();
if (pNodes != null) {
for (List pNode : pNodes) {
for (AnnotationNode ano : pNode) {
String checkString = getPrefix() + "/" + REQUEST + ";";
if (checkString.equals(ano.desc)) {
requestVariables.add((String) ano.values.get(1));
}
}
}
}
String[] requests = new String[requestVariables.size()];
requestVariables.toArray(requests);
// 使用"/"分割的fqcn,以便类加载器加载
String fqcn = this.fqcn.replace("/", ".");
return new ProcessDesc(url, methodName, fqcn, requests);
}
}
---
package test;
import java.io.IOException;
import java.util.Map;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class RequestHandlerServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static ProcessorManager manager;
@Override
public void init(ServletConfig config) throws ServletException {
super.init(config);
String path = config.getServletContext().getRealPath("/");
try {
manager = ProcessorManager.getSingleInstance();
manager.scan(path);
} catch (DOFException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void destroy() {
}
@Override
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response;
// 获取访问的具体url
String uri = httpRequest.getRequestURI();
uri = uri.substring(uri.indexOf("/", 1));
int index = uri.indexOf("?");
if (index != -1) {
uri = uri.substring(0, index);
}
if (!manager.isRoutable(uri)) {
super.service(httpRequest, httpResponse);
}
// 请求路由并处理
Map params = request.getParameterMap();
try {
String json = manager.invoke(uri, params);
response.getOutputStream().write(json.getBytes("utf-8"));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
详细工程见附件