JavaEE中的MVC(三)定制Struts——命令模式

注:这是一个“重复造轮子”的过程,本文谈谈如何简单的MVC框架

 

生活中,医院分为很多科室,如果病人进医院就直接找科室,那医院肯定乱套了,所以呢,医院有个挂号的地方,看什么病就挂什么号。而我们Servlet的业务那么多,我们可不可以像医院挂号这样?只保留一个Servlet,作为中央控制器,要选择什么样的业务,就去调用指定的Action!照着这个思路,接下来我们就想办法实现一下。

注:以上实例说的就是命令模式,也有说是中央控制器模式的。

better

 

其实,Sevlet底层实现就是Socket通信,而我们的MVC框架则是对Sevlet的进一步封装,其核心就是对Request和Response两个对象的操作;

服务入口,也就是我们通常所说的域名,通过域名可以访问我们的服务器,然后网址的后面一半则是具体的服务;

http://www.cnblogs.com/chenss15060100790/articles/7533345.html 

比如上面一个网址,前面是我的博客地址,后面是我的具体文章;

在代码中request.getServletPath()获取的就是后面一半的地址,根据地址,可以对请求进行分发。

 

初步实现:在Servlet调用Action类

先写一个测试用的Action,没任何实际内容,只是打印了一段话,为了测试可以多写几个类似的。

public class HiAction {
    public void excute() {
        System.out.println("this is hiAction");
    }
}

接下来去设计我们的Servlet(其实Filter就可以)

@WebServlet(name="PrepareAndExcuteServlet",urlPatterns="/*")
public class PrepareAndExcuteServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        String requestPath = request.getRequestURI();
        System.out.println(requestPath);
        try {
            String path = requestPath.substring(request.getContextPath().length() + 1, requestPath.indexOf(".action"));
            System.out.println(path);
            System.out.println("=====================");
            if ("HiAction".equals(path)) {
                HiAction hiAction=new HiAction();
                hiAction.excute();
            }
        } catch (IndexOutOfBoundsException e) {
            System.out.println(e.getMessage());
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

代码很简单,下面就是执行结果了。

demo1.0

配置文件的设计

在第一个版本,就照着中央控制器的思路,简单地实现了一下,我们是根据Uri判断要new哪一个Action,很明显,照着那种方式写,随着业务变多、变复杂之后,if…else…就会越变越多,这个Servlet就会变得很难管理,所以,接下来我们就开始写我们的Plus版本,要解决这个if…else…的问题,我的想法是通过配置文件的方式来做。

首先就设计我们的Xml配置文件,取名就叫my_struts,然后放在根目录上,Action节点就两个属性:

  • name:Action类名。
  • class:Action的全类名,用于反射,创建出实例。
<?xml version="1.0" encoding="UTF-8"?>
<mystruts>
    <action name="HiAction" class="com.action.HiAction"/>
    <action name="HelloAction" class="com.action.HelloAction"/>
</mystruts>

然后设计一下Action的接口,用父类引用接收子类实例,这样就不要强转了,强转就放到业务逻辑中,必须强转的时候弄吧。

public interface MyAction {
    void excute();
}

最后就是ActionFactory的设计了,这个类的职责就是解析Xml配置文件。它内部包含一个Map,Map的Key值就是类名,而Value值就是Action实例。这样在Servlet中解析完路径的时候,就可以直接根据Key值获取对应的Action。

public class ActionFactory {
    private static Map<String, MyAction> actions = new HashMap<>();
    private static ActionFactory factory;

    private ActionFactory() {
        System.out.println("======================");
        System.out.println("ActionFactory is OK");
    }

    /**
     * 单例模式
     */
    public static void init() {
        if (factory == null) {
            synchronized (ActionFactory.class) {
                if (factory == null) {
                    factory = new ActionFactory();
                }
            }
        }
    }

    /**
     * 在静态块儿中就开始解析配置文件
     */
    static {
        parseFile("/my_struts.xml");
    }

    public static MyAction getAction(String actionName) throws Exception {
        MyAction action = actions.get(actionName);
        //这里创建一个action的备份,考虑到Action可能同时被多个用户访问,所以每次给Servlet的Action其实都是一个备份,他们是彼此独立的
        return action.getClass().newInstance();
    }

    /**
     * 根据路径解析
     */
    private static void parseFile(String path) {
        try {
            InputStream inputStream = ActionFactory.class.getResourceAsStream(path);
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();
            Document document = builder.parse(inputStream);

            NodeList beanNodeList = document.getElementsByTagName("action");
            for (int i = 0; i < beanNodeList.getLength(); i++) {
                Node bean = beanNodeList.item(i);
                parseBeanNodes(bean);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 解析Node
     */
    private static void parseBeanNodes(Node node) {
        Element element = (Element) node;
        String idName = element.getAttribute("name");
        String className = element.getAttribute("class");
        actions.put(idName, createInstance(className));
    }

    /**
     * 根据全类名反射成对象
     */
    private static MyAction createInstance(String fullClassName) {
        try {
            return (MyAction) Class.forName(fullClassName).newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

我们的Servlet中的代码就变得十分简洁了:

@WebServlet(name = "PrepareAndExcuteServlet", urlPatterns = "/*")
public class PrepareAndExcuteServlet extends HttpServlet {
    private static final long serialVersionUID = 1965314831568094829L;

    /**
     * 初始化的时候,顺带地把ActionFactory初始化好
     */
    @Override
    public void init() throws ServletException {
        super.init();
        ActionFactory.init();
    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        try {
            //解析URI,Uri的规则我调整了一下
            String requestPath = request.getRequestURI();
            String path = requestPath.substring(request.getContextPath().length() + 1);

            //根据URI找到对应的Action,然后执行excute()方法
            MyAction action = ActionFactory.getAction(path);
            action.excute();
        } catch (Exception e) {
            System.out.println("action is null");
        }
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        doGet(request, response);
    }
}

运行效果如下:
这里写图片描述

 

posted on 2016-12-18 00:31  疯狂的妞妞  阅读(274)  评论(0编辑  收藏  举报

导航