进入j2ee的学习,听到了不少框架,可谓百家争鸣,框架对基本的操作进行了封装,如果不懂得实现原理,尽管可以配配文件,让网站跑起来,但未免有时一头雾水,不知其因,所以想要明白其理,甚至灵活运用,必须对框架的工作原理熟悉并牢记于心。

模拟Struts实现

普通的Servlet的不灵活问题

servlet负责控制调度,无论是通过接收command参数,还是截取uri,都存在相同的问题,看看代码就能发现

@Override
protectedvoid doGet(HttpServletRequest request, HttpServletResponse response)
throwsServletException, IOException {
// 截取uri参数
// /test_servlet/servlet/addUser.action
StringrequestURI = request.getRequestURI();
Stringpath = requestURI.substring(requestURI.indexOf("/", 1),requestURI.indexOf("."));
// path=/servlet/addUser
System.out.println("path="+ path);// 接收表单参数
Stringusername = request.getParameter("username");
int age = Integer.parseInt(request.getParameter("age"));// 控制调度相应的处理业务逻辑方法
UserManageruserManager = new UserManager();
Stringforward = "";
if("/servlet/delUser".equals(path)) {
userManager.del(username);
forward= "/del_success.jsp";
}elseif ("/servlet/addUser".equals(path)) {
userManager.add(username);
forward= "/add_success.jsp";
}elseif ("/servlet/modifyUser".equals(path)) {
userManager.modify(username);
forward= "/modify_success.jsp";
}elseif ("/servlet/queryUser".equals(path)) {
ListuserList = userManager.query(username);
request.setAttribute("userList",userList);
forward= "/query_success.jsp";
}else{
throw new RuntimeException("请求失败");
}// 转向
request.getRequestDispatcher(forward).forward(request,response);

  • 一个servlet完成增删改查,需要多个if判断,然后执行相应的业务逻辑,这样的话,if不稳定,可能增加、修改,改来改去
  • 转向路径代码写死,指明了特定的jsp或者servlet,都违背开放扩展,关闭修改原则
  • 表单提交,因为传的都是字符串,所以接收参数时,如果是数值型需要转换

改善:抽取业务控制器,放在Action类中

层分的越多,职责越具体化。这里就把控制器又细化了,分为前端控制器和业务控制器。
前端控制器,负责
  1. 根据一定规则截取url
  2. 根据url分发到相应的Action

业务控制器,负责:

  1. 接收表单数据
  2. 调用业务逻辑
  3. 返回某个ActionForward

TestServlet其实就是一个简单工厂,根据截取的url实例化相应的Action,把实例赋给父类Action引用,以便多态调用。

TestServlet.java

@Override
protectedvoid doGet(HttpServletRequest request, HttpServletResponse response)
throwsServletException, IOException {
// 截取uri参数
///test_servlet/servlet/addUser.action
StringrequestURI = request.getRequestURI();
Stringpath = requestURI.substring(requestURI.indexOf("/", 1),requestURI.indexOf("."));
//path=/servlet/addUser
System.out.println("path="+ path);Actionaction = null;
if("/servlet/delUser".equals(path)) {
action= new DelUserAction();
}elseif ("/servlet/addUser".equals(path)) {
action= new AddUserAction();
}elseif ("/servlet/modifyUser".equals(path)) {
action= new ModifyUserAction();
}elseif ("/servlet/queryUser".equals(path)) {
action= new QueryUserAction();
}else{
throw new RuntimeException("请求失败");
}
Stringforward = null;
try{
forward= action.execute(request, response);
}catch (Exception e) {
e.printStackTrace();
}
request.getRequestDispatcher(forward).forward(request,response);

Action接口抽取execute方法 ,供操作Action(QueryAction、AddAction)实现

Action.java

publicinterface Action {public String execute(HttpServletRequest request, HttpServletResponse response)
throwsException;
}

操作Action(QueryAction、AddAction)实现特定功能,调用相关业务逻辑,并返回(查询)操作完毕后需要转向的地址。

QueryUserAction.java

publicclass QueryUserAction implements Action {
publicString execute(HttpServletRequest request,
HttpServletResponseresponse) throws Exception {
Stringusername = request.getParameter("username");
//intage = Integer.parseInt(request.getParameter("age"));
//其他查询查询条件
//................//调用业务逻辑
UserManageruserManager = new UserManager();
ListuserList = userManager.query(username);
request.setAttribute("userList",userList);
return "/query_success.jsp"; //转向路径可以通过配置文件读取
}
}

if语句中的相应内容(接收表单参数、调用业务逻辑)放入对应Action类,但依然存在if分支,最初的问题依然没有得到解决

继续改善:利用反射去掉if分支

学习设计模式知道,想要去掉if... else...分支,使用抽象工厂+策略模式就可达到目的。

配置文件 action-config,配置请求路径、类名、转向路径信息。把if...else...的东西放到了这里,当需要更改或者增加条件分支时,直接更改或者添加对应的action标签即可,因为有类的信息,也可以动态读取类信息,进行动态实例化,从而形成生产对象的工厂。

action-config.xml

<?xmlversion="1.0" encoding="UTF-8"?>
<action-config><action path="/servlet/delUser"type="com.tch.servlet.DelUserAction">
<forwardname="success">/del_success.jsp</forward>
<forwardname="error">/del_error.jsp</forward>                </action>        <actionpath="/servlet/addUser"type="com.tch.servlet.AddUserAction"><forwardname="success">/add_success.jsp</forward><forwardname="error">/add_error.jsp</forward>                </action><actionpath="/servlet/modifyUser"type="com.tch.servlet.ModifyUserAction"><forwardname="success">/modify_success.jsp</forward><forwardname="error">/modify_error.jsp</forward>                </action>        <actionpath="/servlet/queryUser"type="com.tch.servlet.QueryUserAction"><forwardname="success">/query_success.jsp</forward><forwardname="error">/query_error.jsp</forward>                </action>
</action-config>

读取配置文件action-config,以map形式返回

每个Action对应一个ActionMapping对象,把这些对象存到map中,以path值为key值。

TestXMLReader.java

/*** 读取action-config文件* @author TCH**/
publicclass TestXMLReader {publicTestXMLReader(){}/*** 读取action-config文件* @return* * 如果是删除ActionMapping存储如下:* actionMapping {*        path="/servlet/delUser";*  type= "com.bjpowernode.servlet.DelUserAction";* forwardMap {*         key="success",value="/del_success.jsp"*     key="error", value="/del_error.jsp"*  }* }** Map map = new HashMap();* map.put("/servlet/delUser",actionMapping);* map.put("/servlet/addUser",actionMapping);* map.put("/servlet/modifyUser",actionMapping);* map.put("/servlet/queryUser",actionMapping);*/
publicstatic Map getActionMap(){
MapactionMap = new HashMap();SAXReaderreader = new SAXReader();
InputStreamin =Thread.currentThread().getContextClassLoader().getResourceAsStream("action-config.xml");
try{
Documentdoc = reader.read(in);//xml文件中action标签列表
ListactionList = doc.selectNodes("//action");
for(Iterator iter = actionList.iterator(); iter.hasNext();) {
ElementactionElt = (Element) iter.next();//读取每个action标签中的path、type、forward标签,
Stringpath = actionElt.attributeValue("path");
Stringtype = actionElt.attributeValue("type");
NodesuccessNode = actionElt.selectSingleNode("//forward[@name=\"success\"]");
Stringsuccess = successNode.getText();
NodeerrorNode = actionElt.selectSingleNode("//forward[@name=\"error\"]");
Stringerror = errorNode.getText();//存入ActionMapping
ActionMappingactionMapping = new ActionMapping();
actionMapping.setPath(path);
actionMapping.setType(type);MapforwardMap = new HashMap();
forwardMap.put("success",success);
forwardMap.put("error",error);
actionMapping.setForwardMap(forwardMap);//把对应的ActionMapping存入actionMap
actionMap.put(path,actionMapping);
}}catch (DocumentException e) {
e.printStackTrace();
}
returnactionMap;
}
}

作为中央控制器的FrontControllerServlet,是交互的纽带。

  1. 接收请求,截取uri
  2. 根据截取的uri值,动态实例化操作Action(QueryAction)对象,
  3. 执行(查询)操作,
  4. 进行转向

FrontControllerServlet.java

public class FrontControllerServlet extends HttpServlet {MapactionMap = null;
@Override
protectedvoid doGet(HttpServletRequest request, HttpServletResponse response)
throwsServletException, IOException {
//截取uri参数
// /test_servlet/servlet/addUser.do
StringrequestURI = request.getRequestURI();
Stringpath = requestURI.substring(requestURI.indexOf("/", 1),requestURI.indexOf("."));
//path=/servlet/addUser
System.out.println("path="+ path);//读取配置文件
actionMap= TestXMLReader.getActionMap();//根据截取的URL,到Map中取得本次请求对应的Action
ActionMappingactionMapping = (ActionMapping)actionMap.get(path);//取得本请求对应的Action类的完整路径
Stringtype = actionMapping.getType();Actionaction;
Stringforward = "";
try{
//采用反射动态实例化Action
action= (Action)(Class.forName(type).newInstance());
//动态调用Action中的execute方法
forward= action.execute(request, response);
}catch (Exception e) {
e.printStackTrace();
}//根据路径完成转向
request.getRequestDispatcher(forward).forward(request,response);
}@Override
protectedvoid doPost(HttpServletRequest request, HttpServletResponse response)
throwsServletException, IOException {
doGet(request,response);
}
}

web.xml

<?xmlversion="1.0" encoding="UTF-8"?>
<web-appversion="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"><servlet><servlet-name>MyServlet</servlet-name><servlet-class>com.tch.servlet.TestServlet</servlet-class></servlet><servlet-mapping><servlet-name>MyServlet</servlet-name><url-pattern>*.do</url-pattern></servlet-mapping>
</web-app>

queryUser.jsp

<html>
<head>
</head>
<body>
<formaction="servlet/queryUser.do" method="post">
姓名:<inputtype="text" name="username"><br>
<inputtype="submit" value="查询"><br>
</form>
</body>
</html>

中央控制器的操作流程

换个角度,使用时序图描述中央控制器的执行流程。

  1. 发送请求。浏览器请求Tomcat,url是有规则的,比如*.action或者*.do.
  2. 截取url。因为请求地址http://localhost:8080/simulateStruts/item.do,因为可以改变的是.do和项目名称,所以只截取/item。
  3. 根据配置文件,动态实例化特定操作Action。也就是说把操作Action、对应的path和完成操作后应该跳转的地址信息加载到内存,一个Action标签信息,对应一个ActionMapping对象,所有的ActionMapping对象都装在Map中,方便根据条件动态实例化特定的操作Action。
  4. 执行特定操作Action的execute方法。也就是业务控制器完成接收表单内容,调用B层的业务逻辑,并返回操作完毕后的转向地址。
  5. 根据特定操作Action返回的地址信息,进行转向。

小结

模拟了Strtus工作流程,自然对该框架的原理了然于胸。灵活之处在于配置文件,使用抽象工厂和反射改善了死板的if...else...。可见,配置文件是设计模式的一种非常灵活的实现方式。

学习Struts框架系列(一):模拟Struts工作流程相关推荐

  1. c#Struts框架理念和自制Struts框架下 复杂版

    在上文中,我提到了一个"简单的zjy框架"的数据流程,但是那只是很少的一部分,用法和功能也比较简单,他能够完成的只是把一个数据库显示到页面(或者窗体)上.在这个第四章节中,我想将这 ...

  2. 2019 年起如何开始学习 ABP 框架系列文章-开篇有益

    阅读文本大概需要 3.3 分钟. 本系列文章推荐阅读地址为:52ABP 开发文档https://www.52abp.com/Wiki/52abp/latest/Welcome-to-52abp 本文的 ...

  3. 每天一点儿时间学习Spring框架系列—IOC(2)

    每天一点儿时间学习Spring框架--Ioc(2) 前篇简单入门了Spring的结构,这次就来开始讨论其中的控制反转的思想和自己的一些编写实例分享.(编写不一定能够完美的实现,建议大家了解其中的基本步 ...

  4. 从0开始学管理系列(五) —— 工作流程标准化

    文章目录 一 什么是管理?我们为什么要管理? 二 我们如何去管理?我们管理会碰到哪些问题?如何解决? 三  如何打造一个高效的项目团队? 四 管理中提高效率的工具有哪些 五 工作流程标准化 前言 上篇 ...

  5. 深度学习开源框架系列:基础算法之傅立叶变换:1:概要介绍

    傅立叶变换时数字信号处理的重要方法之一,是法国数学家傅立叶在1807年在法国科学学会上发表的一篇文章中所提出的,在文章中使用了正弦函数描述温度分布,而且提出了一个著名的论断:任何连续性的周期信号都可以 ...

  6. Java开发框架——Struts框架

    为了解决这些问题,出现了Struts框架,它是一个完美的MVC实现,它有一个中央控制类(一个Servlet),针对不同的业务,我们需要一个Action类负责页面跳转和后台逻辑运算,一个或几个JSP页面 ...

  7. 3D角色模型的雕刻技巧:电影般的艺术工作流程,靠的是时间的叠加

    作者简介:嗨!我是来自安大略省多伦多的3D艺术家Taj Nabhani,他在VFX行业拥有5年以上的经验.我在OCAD大学学习插图,在Sheridan College的计算机动画学院做过研究生,并在许 ...

  8. 使用Tand自动化您的机器学习工作流程

    This is a post on TanD, a no-code framework to automate the machine learning workflow. Its purpose i ...

  9. Mybatis体系结构及工作流程

    相关内容: 架构师系列内容:架构师学习笔记(持续更新) Mybatis工作流程 首先在MyBatis 启动的时候我们要去解析配置文件,包括全局配置文件和映射器配置文件,这里面包含了我们怎么控制MyBa ...

最新文章

  1. iOS 关于UIView覆盖StatusBar的小知识点
  2. Linux QtCreator 设置mingw编译器生成windows程序
  3. 报工提示错误:“没有内部作业价格可被确认”的解决方法
  4. 实验二:进程的创建与可执行程序的加载
  5. 2.图像作为函数 | 裁剪、颜色通道、图像相加_5
  6. Android ADB命令?这一次我再也不死记了!【简单说】
  7. c语言程序设计二级考试哪些题型,计算机二级考试题型
  8. 蓝桥杯c语言基础试题答案,蓝桥杯试题C语言答案.doc
  9. Java开发,需要学习什么内容?
  10. hadoop 集群txid不一致
  11. JavaScript学习(五)
  12. 【最优估计学习笔记】贝叶斯公式的深入理解
  13. preg_match_all 和 preg_replace 区别
  14. 如何从iCloud共享iWork文档
  15. sm2263xt量产工具,sm2269xt开卡软件,SM2263XT、sm2269xt固态硬盘使用量产工具进行开卡教程,慧荣sm2263xt、sm2269xt怎么量产
  16. [原创]桓泽学音频编解码(3):AAC 系统算法分析
  17. 使用Lambda Power Tuner UI节省资金并提高性能
  18. 批量生成文件夹内所有文件md5
  19. JavaScript把字符串中每一个单词的字母翻转
  20. 用计算机探索商的变化规律教案,商不变的规律教案

热门文章

  1. 42个面向前端开发人员的很棒JavaScript 库和框架
  2. node linux cache补释放,linux下释放cache内存
  3. 0x80073712_处理win10更新提示错误代码“0x80073712”的方法
  4. N沟道增强型MOS管双向低频开关电路
  5. 超低功耗研发-STM32L151C8T6芯片(三)RTC自动唤醒机制
  6. md5sum命令的使用
  7. web漏洞-远端WWW服务支持TRACE请求
  8. Linux软件集成开发环境
  9. 【Android】打包生成APK教程
  10. 分布式限流的解决方案