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

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

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

其实,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);
}
}

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

配置文件的设计

在第一个版本,就照着中央控制器的思路,简单地实现了一下,我们是根据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);
}
}

运行效果如下:

JavaEE中的MVC(三)定制Struts——命令模式的更多相关文章

  1. JavaEE中的MVC(五)定制Struts——Action跳转JSP

    在JavaEE中的MVC(三)中,我在Servlet中引入了命令模式的使用,采用Xml配置的方式,实现了一个Servlet调用多个不同的Action类,但是还不能实现页面地跳转,这一篇博客从之前的代码 ...

  2. JavaEE中的MVC(四)AOP代理

    咱们来吹牛,JDK的动态代理在AOP(Aspect Oriented Programming,面向切面编程)中被称为AOP代理,而AOP是Spring框架中的重要组成部分. 代理模式 但是什么是代理模 ...

  3. JavaEE中的MVC(一)Dao层彻底封装

    最近Android工作实在难找,考虑是不是该转行做Java了,今天开始,花几天的事件,研究一下JavaEE各层优化. 首先是Dao层 增删改方法封装 使用PreparedStatement执行一条Sq ...

  4. JavaEE中的MVC(二)Xml配置实现IOC控制反转

    毕竟我的经验有限,这篇文章要是有什么谬误,欢迎留言并指出,我们可以一起讨论讨论. 我要讲的是IOC控制反转,然后我要拿它做一件什么事?两个字:"解耦",形象点就是:表明当前类中需要 ...

  5. 前端开发中的 MVC、MVP、MVVM 模式

    MVC,MVP和MVVM都是常见的软件架构设计模式(Architectural Pattern),它通过分离关注点来改进代码的组织方式.不同于设计模式(Design Pattern),只是为了解决一类 ...

  6. MVC、MVP、MVVM 模式对比

    MVC.MVP和MVVM这些开发模式为了分离视图(View)和模型(Model)而提出来的,直白说就是为了前后端分离. 1. MVC(Model View Controller)模式 MVC是比较直观 ...

  7. 设计模式(六):控制台中的“命令模式”(Command Pattern)

    今天的博客中就来系统的整理一下“命令模式”.说到命令模式,我就想起了控制台(Console)中的命令.无论是Windows操作系统(cmd.exe)还是Linux操作系统(命令行式shell(Comm ...

  8. 举例说,在命令模式(Command Pattern)

    在前面加上 谈到命令,大部分的人脑海中会想到以下这幅画面   这在现实生活中是一副讽刺漫画,做决定的人不清楚运行决定的人有何特点,瞎指挥.外行领导内行说的就是这样的.只是在软件设计领域,我们显然要为这 ...

  9. Head First设计模式之命令模式

    一.定义 定义:将一个请求封装成一个对象,从而使您可以用不同的请求对客户进行参数化.对请求排队或记录请求日志,以及支持可撤消的操作. 主要解决:在软件系统中,行为请求者与行为实现者通常是一种紧耦合的关 ...

随机推荐

  1. Java方法使用的有点总结

    方法使用的优点: 1-将解决问题的方法与主函数代码分开,逻辑更清晰,代码可读性更强. 2-若方法出错,则程序可以缩小为只在该方法中查找错误,使代码更容易调试. 3-方法是解决一类问题的抽象,一旦写成功 ...

  2. 手把手教你解决无法创建 JPA 工程的问题

    原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7703803.html ------------------------------------ ...

  3. 白夜追凶 :手 Q 图片的显示和发送逻辑

    欢迎大家前往腾讯云社区,获取更多腾讯海量技术实践干货哦~ 作者:陈舜尧 导语: "这张图片在快捷发图栏背景是黑色的,为啥发到AIO(会话窗口)里背景就变成白的了?" 通过一个bug ...

  4. LeetCode 342. Power of Four (4的次方)

    Given an integer (signed 32 bits), write a function to check whether it is a power of 4. Example:Giv ...

  5. 循环渐进linux笔记——linux系统基本机构

    第一篇 linux的基础介绍 1.linux是一个支持多用户多任务的超棒系统,它具由六个字符控制台,每个控制台都能独立作业,不会互相影响,如果需要在几个字符控制台互相切换的话可以用组合键 ctrl+a ...

  6. swift之函数式编程(二)

    本文的主要内容来自<Functional Programming in Swift>这本书,有点所谓的观后总结 在本书的Introduction章中: we will try to foc ...

  7. CentOS6.9中挂载NTFS移动硬盘

    公司需要本地备份,不占用公网带宽,而本地服务器硬盘容量不够,所以需要将本地服务器centos 6.9系统的备份数据拷贝到移动硬盘. 所以需要在centos上挂载NTFS格式的移动硬盘. 方法/步骤: ...

  8. [板子]Floyd&Dijkstra

    谨以此笔记记录jjw高三党四个月学习NOI的历程..如转载请标记出处 Floyd算法: 默认是业界最短路最简单的写法,并且只有五行.时间复杂度为O(N3),空间复杂度为O(N2). ;k<=n; ...

  9. C#语言支持的特性,.NET却不支持,那么C#不被.NET支持的部分又是如何在.NET上运行的呢?

    阅读<C#高级编程>系列丛书中,介绍C#与.NET的关系,提到C#是语言,.NET是平台(C#不是.NET的一部分),说".NET支持的一些特性,C#并不支持",这个可 ...

  10. scanf和cin性能的比较

    我的实验机器配置是: 处理器:Intel(R) Core(TM) i3-7100U CPU @ 2.40GHz 2.40GHz 随机访问存储器:4.00GB 操作系统:Windows10 集成开发环境 ...