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

进入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...。可见,配置文件是设计模式的一种非常灵活的实现方式。
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值