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(); } } }
详细工程见附件
相关推荐
概述jxrest 是一个简单、轻量级的框架,可实现基于 JSON 的 REST API 的真正快速开发。 让我们看看编写 REST API 有多么简单: import ...
快递 轻量级 REST 框架
轻量级 Java Web 框架 基于 JDK 8 规范 基于 Servlet 3.0 规范 零配置 REST 服务接口 基于 JSON 传输 目前提供 MVC ... 解决方案 ...
服务端可发布 REST 服务(使用 REST 插件) 客户端通过 AJAX 获取服务端数据并进行界面渲染 提高应用程序的开发效率 面向基于 Web 的中小规模的应用程序 新手能在较短时间内入门 核心具有良好的定制性且插件易于扩展
Endpoints是一个用python编写的轻量级REST api框架,可用于每天处理数百万个请求的多个生产系统中。 5分钟入门 安装 首先,使用以下命令安装端点。 $ pip install endpoints 如果您想要最新和最伟大的产品,还可以...
轻量级django(书+代码)。通过选取用于创建轻量级应用组件的形式来理解进行Django解耦设计的方法。通过本书的学习,你将具备创建单页面响应实时交互应用的能力。如果你熟练掌握了Python和JavaScript,则可以开始...
Java-micro:一个用于构建微服务的轻量级Java框架
不安 适用于Python的轻量级REST微型框架。 文档位于 。 可与 , , , 和,但对于许多其他Python Web框架应该很有用。 基于从和其他REST库中学到的经验教训。特征小型,快速的代码库默认为JSON输出,但可覆盖RESTful...
Everest - 使用JavaFX构建的一个漂亮且轻量级的REST客户端
RESTX-轻量级的Java REST框架 RESTX是一个完整的轻量级破坏堆栈,其中包括类似Swagger的ui并将REST规范测试视为文档。 它与Play等现代框架具有相似之处。 像热编译和非常富有成效的经验,但专注于REST和纯Java。 它...
Restlet项目为“建立REST概念与Java类之间的映射”提供了一个轻量级而全面的框架。它可用于实现任何种类的REST式系统,而不仅仅是REST式Web服务。 Restlet项目受到Servlet API、JSP(Java Server Pages)、...
轻量级django(书代码)。通过选取用于创建轻量级应用组件的形式来理解进行Django解耦设计的方法。通过本书的学习,你将具备创建单页面响应实时交互应用的能力。如果你熟练掌握了Python和JavaScript,则可以开始编写...
轻量级Java http服务器和Web应用程序框架。 例 基本路线 HttpServer server = HttpServer . bind( 8080 ); server . accept( new Controller () { @Get ( " / " ) public HttpHandler index () { return ok();...
JFinal 是基于 Java 语言的极速 WEB + ORM 框架,其核心设计目标是开发迅速、代码量少、学习简单、功能强大、轻量级、易扩展、Restful。在拥有Java语言所有优势的同时再拥有 ruby、python 等动态语言的开发效率!为...
HTTP-RPC是一个开放源代码框架,用于在Java中创建和使用RESTful和类似REST的Web服务。 它非常轻巧,仅需要Java运行时环境和servlet容器。 整个框架的大小不到100KB,使其成为需要最小占用空间的应用的理想选择。 本...
今天给大家介绍一个开源的轻量级跨平台实时HTML+C#.NET Web应用程序开发框架——DotNetify,允许你在C#.NET后端上创建具有React、React Native、Vue或Blazor 前端的实时、响应式、跨平台应用程序。 它的主要特点是...
Backbone 是一款基于模型-视图-控制器 MVC 模式的轻量级javascript 框架 ,可以用来帮助开发人员创建单页Web应用。 借助Backbone 我们可以使用REST的方式来最小化客户端和服务器间的数据传输,从而实现了更快加速的...
Smoke Framework是一个轻量级的服务器端服务框架,用Swift编写,默认情况下使用SwiftNIO作为其网络层。 该框架可用于类似REST或类似RPC的服务,并与服务模型(如Swagger / OpenAPI)中的代码生成器结合使用。
自Django 创建以来,各种各样的开源社区已经构建了很多Web 框架,比如JavaScript 社区创建的Angular.js 、Ember.js 和Backbone.js 之类面向前端的Web 框架,它们是现代Web 开发中的先驱。Django 从哪里入手来适应...