在第一篇时就说过框架要在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关于请求的配置:
?
<action name="login" class="com.lscmjx.action.LoginAction" method="login"> <result name="success"> main.jsp </result> <result name="failure"> login.jsp </result> </action>
? ? 它提交的url会是http://xxx.xxx.xxx/login,访问web服务器时会把此url传递到struts框架交给它处理,之后struts会在struts.xml中寻找login的相关的配置,像上面例子,struts会找到LoginAction的类,并且调用其login的方法。
? ? ? 写到这里,我请问这是最好的方式吗?当然不是,至少我在使用struts时就认为这是相当撇脚的设计。上面例子只是列举一个login方法,假如一个系统中要对后台调用的方法是100个,那岂不是就需要在struts.xml中写100个与之类似的配置。想想都头大,这样繁琐的工作,应该是由框架自己去处理,而不是人工给配置。
?
? ? ? 再来看实现相同功能的Unicorn?web框架的配置。
?
<action class="com.mh.action.UserAction"></action>
? ? ? 当然提交的url肯定需要包含多一些的信息,来保证能通过url正确调用框架Action里的方法。这里提交的url方式:http://xxx.xxx.xxx/UserAction/login/
? ? ? 通过在url里附加调用的Action类的信息,可以省略为不同的方法都在xml里配置的麻烦。假如UserAction里有100个方法,框架也只需这一行的配置。
? ? ? 有了大体的认识之后,来看框架的核心部分AMPPathRouter的具体实现。
?
/** * 检查url是否是此Router类要处理的,/Action/Method/Param 格式的将会被检查合格,返回true * @param relativeUri * @param actionMap * @return */ public boolean checkUrl(String relativeUri, Map<String, ActionSupport> actionMap) { Pattern pattern = Pattern.compile("^/\\w+/\\w+/\\S*"); Matcher matcher = pattern.matcher(relativeUri); if(matcher.matches()) { String actionName = relativeUri.split("/")[1]; ActionSupport actionSupport = actionMap.get(actionName); if(null != actionSupport) { String actionMethodName = relativeUri.split("/")[2]; Class<?> actionClass = actionSupport.getClass(); Method[] methods = actionClass.getMethods(); for(int i = 0; i < methods.length; i++) { Method method = methods[i]; String methodName = method.getName(); if(methodName.equals(actionMethodName)) { return true; } } } } return false; } /** * 匹配规则为: * 1、符合/Action/method/param格式, * 2、并且Action在actionMap中的确存在 * 3、method在此Action中存在 */ @Override public boolean route(String relativeUri, UrlFilter urlFilter) { Map<String, ActionSupport> actionMap = urlFilter.getActionMap(); if(!this.checkUrl(relativeUri, actionMap)) { return false; } // 拦截Action/Method/Param方式的请求,并构建ActionSupport类的属性 String[] params = relativeUri.split("/"); try { ActionSupport actionSupport = actionMap.get(params[1]); if(null == actionSupport) { throw new Exception("找不到" + params[1] + "类"); } Class<?> action = actionSupport.getClass(); Method method = action.getMethod(params[2], new Class[] {}); if(params.length > 3) { this.boxingRequest(urlFilter.getRequest(), params[3]); } // 只要找到ActionSupport的子类,则初始化其所具有的属性 Object newInstance = action.newInstance(); this.initActionSupport(newInstance, urlFilter); String result = (String) method.invoke(newInstance, new Object[] {}); if (null == result || ActionSupport.AJAX.equals(result) || ActionSupport.FORWARD.equals(result)) { return true; } else { urlFilter.getResponse().sendRedirect(result); return true; } } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (InstantiationException 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(Object obj, UrlFilter urlFilter) { ActionSupport action = (ActionSupport) obj; action.setRequest(urlFilter.getRequest()); action.setResponse(urlFilter.getResponse()); action.setSession(urlFilter.getSession()); action.setApplication(urlFilter.getApplication()); }
? ? ? 这便是AMPPathRouter的全部内容,其中在把请求分发到ActionSupport的子类 并调用相关方法时 是通过反射实现,其他地方地方都是相当容易理解的。
? ? ? 空说无凭,把框架应用到实战中才是硬道理:
?
? ? ? 好了,下一篇介绍Action 和 json。