在第一篇时就说过框架要在URL上作文章,是的,本文就框架怎样充分利用url上作尽可能详细的说明。
做web开发的不可能对url陌生,早在web1.0时代,url作为统一资源定位符,在对web中资源的如何获得上起到巨大作用。不论用户请求的时静态页面或者是各种图片、脚本文件,通过url总能从web网站获取要访问的资源。Web2.0更是常常使用url作为get请求时参数的传递,如http://xxx.xxx.xxx/xxx.jsp?user=admin。以及近几年很火restful web service 摒弃soap而使用url传递请求参数 都说明合理利用url的可行和流行。
当然不止是使用了url就算好的实践,而是能够做到优雅的使用,保证层次分明和整体的简洁,这才算是好的方式,这也正是本框架对使用url 所追求的目标。
首先来看几个例子:
http://www.cnblogs.com/p2 http://www.xxx.com/index.do?page=2 http://www.xxx.com/product/ http://www.xxx.com/channel.do?channel=product http://www.xxx.com/product/mobile http://www.xxx.com/channel.do?channel=product&&subChannel=mobile
相信各位看官不用我说也能明白,这几组的实现肯定第一种实现的方式更佳。抛开它能屏蔽服务器端使用的技术这一特性不说,它还能够更好地说明动态网站的层次结构,让用户在访问时能明确知道在网站的什么位置,而不会觉得是陷入了一个迷宫。
当然上面列举的例子是网站前端所使用的url表现方式,因为表现方式可以多种多样,个人喜好不同,本框架在设计时没有给指定前端url的表现方式,而是定义接口,把这个权利留给使用的用户。框架将考虑更多 通用性的东西而不是 个性 自由的东西。
下面对框架里默认使用的url Router AMPPathRouter做详细的介绍,包括设计的思想和实现的方式。首先AMPPathRouter的用途定位为后台使用。为了理解快速的理解它的工作原理,先来和struts做一下对比。
Struts关于请求的配置:
main.jsp login.jsp
它提交的url会是http://xxx.xxx.xxx/login,访问web服务器时会把此url传递到struts框架交给它处理,之后struts会在struts.xml中寻找login的相关的配置,像上面例子,struts会找到LoginAction的类,并且调用其login的方法。
写到这里,我请问这是最好的方式吗?当然不是,至少我在使用struts时就认为这是相当撇脚的设计。上面例子只是列举一个login方法,假如一个系统中要对后台调用的方法是100个,那岂不是就需要在struts.xml中写100个与之类似的配置。想想都头大,这样繁琐的工作,应该是由框架自己去处理,而不是人工给配置。
再来看实现相同功能的Unicorn web框架的配置。
当然提交的url肯定需要包含多一些的信息,来保证能通过url正确调用框架Action里的方法。这里提交的url方式:
通过在url里附加调用的Action类的信息,可以省略为不同的方法都在xml里配置的麻烦。假如UserAction里有100个方法,框架也只需这一行的配置。
有了大体的认识之后,来看框架的核心部分AMPPathRouter的具体实现。
/** * 匹配规则为: * 1、符合/Action/method/param格式, * 2、并且Action在actionMap中的确存在 * 3、method在此Action中存在 */ @Override public boolean route(String relativeUri, UrlFilter urlFilter) { MapactionMap = urlFilter.getActionMap(); Pattern pattern = Pattern.compile("^/\\w+/\\w+/\\S*"); Matcher matcher = pattern.matcher(relativeUri); // 检查url是否是此Router类要处理的,/Action/Method/Param 格式的将会被检查合格,返回true if(matcher.matches()) { String[] params = relativeUri.split("/"); String actionName = params[1]; String fullActionName = actionMap.get(actionName); if(null == fullActionName) { return false; } try { Class clz = Class.forName(fullActionName); ActionSupport actionSupport = (ActionSupport) clz.newInstance(); String actionMethodName = params[2]; Method[] methods = clz.getMethods(); for(int i = 0; i < methods.length; i++) { Method method = methods[i]; String methodName = method.getName(); if(methodName.equals(actionMethodName)) { // 拦截Action/Method/Param方式的请求,并构建ActionSupport类的属性 if(params.length > 3) { this.boxingRequest(urlFilter.getRequest(), params[3]); } this.initActionSupport(actionSupport, urlFilter); return this.invokeActionMethod(actionSupport, method, urlFilter); } } } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } return false; } // 调用Action里的方法 private boolean invokeActionMethod(ActionSupport actionSupport, Method method, UrlFilter urlFilter) { try { String result = (String) method.invoke(actionSupport, new Object[] {}); if (null == result || ActionSupport.AJAX.equals(result) || ActionSupport.FORWARD.equals(result) || ActionSupport.WEB_SERVICE.equals(result)) { return true; } if(ActionSupport.REDIRECT.equals(result)) { urlFilter.getResponse().sendRedirect(result); return true; } } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (Exception e) { e.printStackTrace(); } return false; } /** * 把url中得param加入到request的attribute里 * @param request * @param parameter */ private void boxingRequest(HttpServletRequest request, String parameter) { String[] parameters = parameter.split("&"); for (int i = 0; i < parameters.length; i++) { String param = parameters[i]; String[] key_value = param.split("="); if(key_value.length == 2) { request.setAttribute(key_value[0], key_value[1]); } } } /** * 初始化ActionSupport类中所需的request、response、session、application等对象 * @param obj * @param urlFilter */ private void initActionSupport(ActionSupport actionSupport, UrlFilter urlFilter) { actionSupport.setRequest(urlFilter.getRequest()); actionSupport.setResponse(urlFilter.getResponse()); actionSupport.setSession(urlFilter.getSession()); actionSupport.setApplication(urlFilter.getApplication()); }
这便是AMPPathRouter的全部内容,其中在把请求分发到ActionSupport的子类 并调用相关方法时 是通过反射实现,其他地方地方都是相当容易理解的。
空说无凭,把框架应用到实战中才是硬道理:
好了,下一篇介绍Action 和 json。