虽然目前Struts MVC框架不怎么用了,但它确是个能帮助大家很好地入门Web MVC框架,而且,一些历史项目可能还用Struts,反正技多不压身,大家如果能在面试中通过项目证明自己Struts这块也很熟,这也是个非常好的加分项。
这里我们就从搭建Struts基本框架入手,再深入讲解些面试中常会靠到的Struts知识点,本文的文字和案例根据java web轻量级开发面试教程改编。
///////////////////////////////////////////////////////////////////////////////////////
1 搭建Struts框架
1.1 开发前端的JSP代码
创建一个Java Web项目,名为strutsDemo,在其中的WebRoot目录下,创建前端的JSP代码calSum.jsp。
1 1 <%@ page language="java" pageEncoding="GBK" %> 2 2 <%@ taglib prefix="s" uri="/struts-tags"%> 3 3 <html> 4 4 <head> 5 5 <title>输入操作数</title> 6 6 </head> 7 7 <body> 8 8 求和<br/> 9 9 <s:form action="mystruts/calSum" > 10 10 <s:textfield name="num1" label="数1"/> 11 11 <s:textfield name="num2" label="数2" /> 12 12 <s:submit value="求和" /> 13 13 </s:form> 14 14 </body> 15 15 </html>
这个页面的效果下图所示。
在第2行里引入了Struts的标签,前缀是s,所以可以用s:form和s:textfield来定义form和文本框。
在第9行到第13行的form里,定义了两个输入框和一个提交按钮。当用户输入两个数字后,单击“求和”按钮后,本页面将根据定义在第9行的定义,跳转到mystruts/calSum.action。
1.2 在web.xml里声明使用Struts
如果要使用基于Struts的MVC,则必须要在web.xml里声明,否则系统服务器是不会知道在项目里用到了基于Struts的MVC处理器。web.xml的代码如下。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5"> 3 <filter> 4 <filter-name>struts2</filter-name> 5 <filter-class> 6 org.apache.struts2.dispatcher.FilterDispatcher 7 </filter-class> 8 </filter> 9 <filter-mapping> 10 <filter-name>struts2</filter-name> 11 <url-pattern>/*</url-pattern> 12 </filter-mapping> 13 </web-app>
从第2行到第13行之间的web-app元素里,我们声明了两件事:
(1)通过第11行的url-pattern,说明/*,也就是任何请求,都将由名为struts2的过滤器来处理。
(2)在第3行到第8行之间,指定了struts2这个过滤器它的后台处理类。
综合上述两点可知,本项目的任何请求,都将由Struts的后台处理类来处理。
1.3 配置struts.xml文件
在calSum.jsp里指定了form跳转的目的地。
<s:form action="mystruts/calSum" >
那么这个目的地究竟是哪里?一起来看一下struts.xml这个配置文件。
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE struts PUBLIC "-//Apache Software Foundation//DTD Struts Configuration2.0//EN" "http://struts.apache.org/dtds/struts-2.0.dtd"> 3 <struts> 4 <package name="struts2" namespace="/mystruts" 5 extends="struts-default"> 6 <action name="calSum" class="action.myAction"> 7 <result name="Positive">/positive.jsp</result> 8 <result name="Negative">/negative.jsp</result> 9 </action> 10 </package> 11 </struts>
在第4和第5行里,能看到名为calSum的action处于/mystruts这个命名空间里,它所对应的是action.myAction这个处理类。也就是说,calSum.jsp的请求最终是由action.myAction接收和处理。
1.4 开发Action类
随后开发myAction.java,它是放在action这个package下的。
1 package action; 2 import com.opensymphony.xwork2.ActionSupport; 3 public class myAction extends ActionSupport 4 { 5 private int num1; 6 private int num2; 7 8 public String execute() throws Exception 9 { 10 // 如果和大于等于0,则跳到positive.jsp页面 11 if (getSum() >= 0) 12 { 13 return "Positive"; 14 } 15 //否则跳到negative.jsp页面 16 else 17 { 18 return "Negative"; 19 } 20 } 21 public int getNum1() { 22 return num1; 23 } 24 public void setNum1(int num1) { 25 this.num1 = num1; 26 } 27 public int getNum2() { 28 return num2; 29 } 30 public void setNum2(int num2) { 31 this.num2 = num2; 32 } 33 public int getSum() 34 { 35 return num1 + num2; // 计算两个整数的代码数和 36 } 37 }
作为一个Struts的Action,本类继承了ActionSupport类。当一个请求最终到达本Action时,如果没有额外的配置,就会如同本类一样,由execute方法来处理请求。
在第8行的execute方法里可能看到,如果两个数的和大于0,则返回Positive字符串,反之则返回Negative。这两个返回是和struts.xml里的配置相对应的。
在struts.xml的第6行和第7行里,看到有如下的配置。
<result name="Positive">/positive.jsp</result>
<result name="Negative">/negative.jsp</result>
这说明根据不同的字符串,Struts处理容器将会跳转到两个不同的jsp里。在这个Action代码里,并没有给num1和num2赋值,这是因为它们和calSum.jsp里form中的两个输入框同名,所以会自动拿到我们输入的值。
1.5 开发两个跳转结果页面
上文提到过,Action的execute方法里返回的不同字符串后,Struts MVC处理器会读取struts.xml里的配置,并相应地,跳转到不同的页面。下面就来编写这两个页面的代码。先来看一下positive.jsp。
1 <%@ page language="java" pageEncoding="GBK"%> 2 <%@ taglib prefix="s" uri="/struts-tags" %> 3 <html> 4 <head> 5 <title>显示和</title> 6 </head> 7 <body> 8 结果大于或等于0,<h1><s:property value="sum" /></h1> 9 </body> 10 </html>
这里的关键代码是在第8行,通过一个Struts的标签,来获取在Action里名为”sum”的属性对象。在myAction.java里,定义了一个getSum的方法,所以就会自动生成一个sum的属性。结果是两个操作数之和。
Negative.jsp代码和positive.jsp很相似。唯一的差别在第8行,这里的叙述文字是“结果小于0”。
1 <%@ page language="java" pageEncoding="GBK"%> 2 <%@ taglib prefix="s" uri="/struts-tags" %> 3 <html> 4 <head> 5 <title>显示和</title> 6 </head> 7 <body> 8 结果小于0,<h1><s:property value="sum" /></h1> 9 </body> 10 </html>
2 通过运行,了解Struts的工作流程
现在来总结一下整个程序的运行和跳转流程。
第一,打开Tomcat服务器,并在浏览器里输入http://localhost:8080/strutsDemo/calSum.jsp,能看到如下图所示的页面
当输入两个数字,并单击“求和”按钮后,根据如下form里的定义,会跳转到mystruts/calSum里。
<s:form action="mystruts/calSum" >
第二,根据web.xml的定义,可知这个跳转请求将由struts来处理。再根据struts.xml的定义,得知这个请求最终将由myAction.java来处理。
<action name="calSum" class="action.myAction">
<result name="Positive">/positive.jsp</result>
<result name="Negative">/negative.jsp</result>
</action>
第三,在myAction.java的execute方法里,根据最终sum的值,分别返回两个不同的字符串,从上文struts.xml片段的第3行和第4行得知,根据两个不同的返回值,会跳转到两个不同的页面里。
3 和JSP+Servlet+JavaBean框架的比较
在一个项目里,我们应更关注“业务该怎么处理”这个问题,而不应把大多数精力放在调试JSP到Servlet之类的跳转上。
Struts给我们提供了一套跳转机制,我们可以简单地通过编写struts.xml和web.xml,就能比较省心地实现从前端JSP跳转到具体业务处理类的功能。而且,也只需在Action类里编写返回字符串,同时在struts.xml里编写返回字符串和跳转页面的对应关系,就能根据业务执行结果方便地跳转回前端页面。
Struts框架是基于MVC的,它的View部分主要由JSP页面实现。Controler部分主要由Action类来承担,而Model部分主要由ActionForm组成。需要说明的是,在Struts2.0以上的版本里,开发者不需要额外定义ActionForm,由用户在前端Form里传入的数据将被自动封装成ActionFrom对象,并被最终转发给Action。
通过下表对比一下Struts和前文提到的JSP+Servlet+JavaBean框架,综合各项对比的指标,Struts略优于JSP+Servlet+JavaBean框架。
比较项 |
Struts |
JSP+Servlet+JavaBean |
结论 |
如何在后端接收前端传来的参数 |
参数组装成ActionForm,并自动发送到Action里 |
需要在Servlet里编写接收参数的代码 |
Struts比较省心 |
如何把前端的请求发送到合适的处理页面 |
可以在struts.xml里统一定义请求和处理类的对应关系 |
可以在web.xml里统一定义请求和处理类的对应关系 |
基本持平 |
后端处理请求的方式 |
定义在Action里 |
大多定义在Servlet类的doPost或doGet方法里 |
基本持平 |
如何把后端的处理结果再回传到前端 |
可以在struts.xml里统一地定义处理结果和返回页面的对应关系 |
需要在Servlet里手动地跳转 |
Struts略优 |
项目的开发方式 |
程序员的工作量比较少,在必要的地方(比如Action类和Struts.xml)里填写必要的代码即可,Struts处理器能方便地实现MVC之间的跳转 |
程序员可能得操心必要的细节,比如Servlet里如何接收参数,如何跳转到前端,等等 |
Struts的开发流程比较省心 |
4 对Struts框架的进一步了解
Struts作为一个基于MVC的框架,能很好地处理跳转业务,能让程序员把精力更多地集中到业务开发上,通过它,程序员能很好地了解框架编程的思路和一般方法。
不过任何框架都不是十全十美的,当你比较熟悉Struts框架后,一定能感受到它在项目开发里的一些缺陷。对于Struts的局限性,不同的人有不同的观点。但是请大家记住,在面试时,当你结合你的项目自信地说出Struts框架的局限时,即使面试官和你的观点不一致,他也一定会认为你对Struts有足够的认识。这里罗列出一些局限性供大家参考,如表所示。
局限性 |
结合项目说明 |
为每个请求创建一个Action |
Struts2里,会为每个http请求实例化一个Action 对象,这对处理高并发会有些难度 |
对Action 执行前和后的处理支持不大好 |
比如每次进到Action前我们需要打印内存使用量,执行后需要记录跳转目标URL,这个当然可以直接写到Action里,但大家比较下Spring的AOP处理方式,就会发现单纯把前后处理写到Action里有什么不足 |
对跳转的支持 |
如果在一个Action里,根据处理结果可能会跳转到10个页面,那么代码可能会比较烦琐。而且一旦跳转目标页面出现变更,比如换了目录,那么在修改配置文件后,可能要求重新部署和重启Web服务器,无法实现轻量级修改 |
安全性上的瑕疵 |
我们可以通过类似方式直接执行Action里的方法:http://url:8080/proName/userLogin!list 但事实上,该系统需要用户先登录才能开放用户列表。虽然我们可以通过定义拦截器来弥补这个缺陷,但毕竟属于额外的工作量 |
属于侵入式 |
在使用Struts时,需要继承struts的类,而且要编写execute方法。这样Struts框架就侵入到项目里了。比如做的是银行业务,如果哪天要把这套业务移植到其他Spring等框架项目里,可能工作量就比较大了 |
5 关于Struts面试点的归纳
Struts的地位似乎有些尴尬,一些小型(比如10个页面左右)的商业项目可能直接会用JSP+Servlet+JavaBean+DB的开发模式,一些大型基于企业级的项目往往采用Spring+MyBatis的框架。
我们在面试初级程序员时,如果候选人没有在商业项目里用过Struts框架,这很正常,如果他在学校或者培训机构学过Struts,这或许也可以成为一个加分项。但如果我们看到某人在最近的商业项目里用过,那么就会详细问原因,为什么这个公司现在还要用Struts框架?反而有不少人,在这个问题上会弄巧成拙。我们听到的最多的合理回答是,基于某种原因,比如历史原因或者客户要求,这个项目还是得用Struts框架。
具体到面试题,大家可以从网上找,在必要的时候,大家也可以多多益善地突击准备。我们给大家推荐回答问题的方式是“结合项目”,请大家看如下的问题点。
第一,说明在这个项目里,都用到了Struts的哪些组件,比如拦截器或者验证器,你是怎么用的?
第二,结合项目,说明自己做了哪些工作,比如在Action里,你怎么和业务以及数据库代码耦合?
第三,能结合项目,告诉考官一些技术细节,比如拦截器是怎么用的,在该项目里结合一个需求,告诉考官拦截器的工作流程和开发方式。或者结合项目说明下验证器的用法。
也就是说,任何技术都别停留在纸上,需要结合项目说明。
第四,说明使用Struts框架给这个项目带来哪些痛点?
第五,和Spring框架相比,你感觉各自有什么优缺点,请结合项目说明,不要空谈。
当你学好了Spring框架后就知道该怎么说?
第六,你的项目经常会扩展,业务实现方式也经常会变更,结合Struts框架说明一旦出现变更了你需要做哪些事?
比如需要更改业务,你该更改哪些文件?一旦更改了代码,如何部署到服务器上?
第七,这个项目的访问量是多少?最高的并发访问量能达到多少?Struts框架能否很好地处理高并发的情况?
关于常用的技术问题,在本章里都已经提到,我们通过如下问题点来对本章做个总结。
①在Struts2里,如何实现一个Action?
②怎么指定进入Action后该调用哪个方法?
③定义验证器的步骤是什么?
④定义拦截器的步骤是什么?
⑤Struts2中的type类型有哪些?如果不写type,默认是什么?
⑥如何通过配置type类型,实现一个Action往另外一个Action的跳转?
⑦描述下Struts MVC的工作流程和开发模式。
⑧和JSP+Servlet+JavaBean的开发模式相比,Struts MVC有哪些好处,同时,说明下Struts框架有哪些不足。