zoukankan      html  css  js  c++  java
  • Struts、Ajax、Restful、Web Service 狂想曲框架实现篇(三)

          在第一篇时就说过框架要在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的具体实现。

     

    /**
    * 匹配规则为:
    * 1、符合/Action/method/param格式,
    * 2、并且Action在actionMap中的确存在
    * 3、method在此Action中存在
    */
    @Override
    public boolean route(String relativeUri, UrlFilter urlFilter) {
    Map<String, String> actionMap = 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。

  • 相关阅读:
    深度优先和广度优先
    水管工游戏(深度优先)
    炸弹人
    广度优先(迷宫找人)
    System.Data.Entity.Core.MetadataException: 无法加载指定的无数据资源
    Element Cascader 级联选择器 单选操作优化
    Windows服务 ProjectInstaller 获取 路径
    Quartz.NET ScheduledFireTimeUtc 当超过1分钟时出现的问题。
    记录:一个SQL SERVER奇怪的问题。
    log4.net 配置
  • 原文地址:https://www.cnblogs.com/mahang/p/2263660.html
Copyright © 2011-2022 走看看