`
dingchd
  • 浏览: 15688 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

超轻量级的REST框架实现

 
阅读更多

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<String, Object> singleObject = new HashMap<String, Object>();
	// key-desc value-ProcessDesc
	private Map<String, ProcessDesc> mapping = new HashMap<String, ProcessDesc>();

	/**
	 * 将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<ProcessDesc> 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<String, String[]> 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;

/**
 * 类字节码访问器,从字节码中解析出处理器描述
 * 一个处理器描述包含:
 * <li>访问的url</li>
 * <li>方法名字</li>
 * <li>类全路径</li>
 * 请求参数映射表
 * @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<AnnotationNode> visibleAnnotations = new ArrayList<AnnotationNode>();

	private String fqcn; // 类全路径 a/b/c

	// key 方法名, value 方法描述
	private Map<String, MethodNode> methods = new HashMap<String, MethodNode>();

	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<ProcessDesc> getMethodProcessDesc() {
		List<ProcessDesc> list = new ArrayList<ProcessDesc>();

		for (Entry<String, MethodNode> entry : methods.entrySet()) {
			MethodNode methodNode = entry.getValue();
			String methodName = entry.getKey();

			List<AnnotationNode> 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<AnnotationNode>[] pNodes = methodNode.visibleParameterAnnotations;

		ArrayList<String> requestVariables = new ArrayList<String>();

		if (pNodes != null) {
			for (List<AnnotationNode> 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<String, String[]> 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();
		}
	}
}

 

 详细工程见附件

 


 

 

  • 大小: 19.3 KB
  • 大小: 9.4 KB
分享到:
评论

相关推荐

    jxrest:简单、轻量级的 Java REST 框架

    概述jxrest 是一个简单、轻量级的框架,可实现基于 JSON 的 REST API 的真正快速开发。 让我们看看编写 REST API 有多么简单: import ...

    SlwRest-1.0(Super Lightweight Rest)超轻量RESTful框架

    **SlwRest-1.0:超级轻量级RESTful框架** 在当今的软件开发领域,RESTful架构已经成为构建Web服务的主流选择,因为它提供了高效、...如果你正在寻找一个轻量级、易用的REST框架,SlwRest无疑是一个值得考虑的选项。

    轻量级JavaWeb框架Fast-FrameWork.zip

    轻量级 Java Web 框架 基于 JDK 8 规范 基于 Servlet 3.0 规范 零配置 REST 服务接口 基于 JSON 传输 目前提供 MVC ... 解决方案 ...

    exprest:轻量级 REST 框架

    Exprest 是一个轻量级的 REST 框架,主要设计用于 JavaScript 开发环境,特别是在构建 Web 服务和 API 接口时提供便捷。REST(Representational State Transfer)是一种软件架构风格,常用于设计网络应用程序,特别...

    轻量级 Java Web 框架

    服务端可发布 REST 服务(使用 REST 插件) 客户端通过 AJAX 获取服务端数据并进行界面渲染 提高应用程序的开发效率 面向基于 Web 的中小规模的应用程序 新手能在较短时间内入门 核心具有良好的定制性且插件易于扩展

    端点:轻量级REST api后端框架,可自动将URL映射到python模块和类

    Endpoints是一个用python编写的轻量级REST api框架,可用于每天处理数百万个请求的多个生产系统中。 5分钟入门 安装 首先,使用以下命令安装端点。 $ pip install endpoints 如果您想要最新和最伟大的产品,还可以...

    轻量级django高清目录结构PDF最新版+附源码.zip

    轻量级django(书+代码)。通过选取用于创建轻量级应用组件的形式来理解进行Django解耦设计的方法。通过本书的学习,你将具备创建单页面响应实时交互应用的能力。如果你熟练掌握了Python和JavaScript,则可以开始...

    基于SpringBoot的轻量级物联网综合业务支撑平台源码.zip

    标题中的“基于SpringBoot的轻量级物联网综合业务支撑平台源码”表明这是一个使用SpringBoot框架构建的项目,主要用于物联网(IoT)领域的业务支持。SpringBoot是Java生态系统中的一个热门框架,它简化了创建独立的、...

    轻量级Django

    《轻量级Django》这本书聚焦于如何在实际开发中运用Django这一强大的Python Web框架,结合现代前端技术,如REST API、WebSockets和Backbone.js,来构建高效、响应迅速的Web应用。以下是对这些核心知识点的详细解读:...

    Java Web轻量级开发全体验14代码

    标签"Java Web 轻量级"进一步确认了这个压缩包的内容焦点,它将重点放在Java语言在Web开发中的轻量级实现上,可能涉及的轻量级框架和技术包括: 1. **Spring Boot**:Spring Boot是Spring Framework的扩展,旨在...

    轻量级Java EE企业应用实战 第3版 纯 源代码

    在Java EE(Enterprise Edition)开发中,"轻量级"通常指的是使用非重量级的容器或者框架,如Spring、Hibernate等,来替代传统的EJB(Enterprise JavaBeans),以降低系统的复杂性和提高开发效率。这一版本的实战...

    基于Java的WSHttpHelper轻量级Http请求框架设计源码

    该项目为基于Java的WSHttpHelper轻量级Http请求框架设计源码,涵盖175个文件,包括143个Java源文件、7个PNG图片文件、7个XML配置文件、5个Markdown文件、3个YAML文件、2个Markdown文件、2个属性文件、2个HTML文件、1...

    轻量级前后端 Web 开发框架选取及应用.pdf

    ### 轻量级前后端 Web 开发框架选取及应用 #### 一、Web前后端分离思想 在探讨具体的框架之前,我们先了解下前后端分离的概念及其带来的优势。 ##### 1.1 传统Java Web的缺点 在传统的Java Web项目中,前端页面...

    Java-micro一个用于构建微服务的轻量级Java框架

    Java-micro是一个专门为构建微服务设计的轻量级Java框架,它旨在简化微服务的开发过程,提高开发效率,同时保持高性能和可扩展性。在Java开发领域,REST(Representational State Transfer)框架已经成为构建分布式...

    restless:适用于Python的轻量级REST微型框架

    不安 适用于Python的轻量级REST微型框架。 文档位于 。 可与 , , , 和,但对于许多其他Python Web框架应该很有用。 基于从和其他REST库中学到的经验教训。特征小型,快速的代码库默认为JSON输出,但可覆盖RESTful...

    轻量级javaee企业应用实战第4版源码

    在JavaEE(Java Platform, Enterprise Edition)开发中,轻量级框架因其低耦合、高可测试性和易于维护的特性而备受青睐。本教材可能涉及的知识点包括但不限于以下几个方面: 1. **MVC设计模式**:书中可能通过一个...

    go-json-rest框架实现Golang Restful API项目的开发

    `go-json-rest`是一个轻量级且功能丰富的框架,专门设计用于快速构建RESTful APIs。本教程将深入探讨如何利用`go-json-rest`来实现一个用户管理的示例项目。 首先,`go-json-rest`框架的核心在于它提供了对HTTP请求...

    Everest-使用JavaFX构建的一个漂亮且轻量级的REST客户端

    Everest是一款基于JavaFX技术构建的美观且轻量级的REST客户端工具,它专为开发者设计,便于测试和调试RESTful API。通过使用JavaFX,Everest提供了丰富的用户界面和流畅的交互体验,使得API调用变得直观而高效。 ...

Global site tag (gtag.js) - Google Analytics