Struts是Apache组织研发的一个MVC开源框架,基于J2EE平台,目前我学习的版本是1.3.8,接近两个礼拜的学习后,写下此笔记。
首先应该从普通的JSP+Servlet+JavaBean(后文略写为JSJ)谈起,这样的话才能体现出Struts框架的优秀特点,这里我把纯JSP开发和Struts1.X做个对比。
1.JSJ开发Web应用时,把经常用到的数据全部封装JavaBean,在当时看来,这是件很好的事情,但是当我们的Web应用变得相对比较庞大时就暴露出JavaBean的不足,当获取到数据时,我们难免都要get or set数据一下,这无疑是纯粹的手工劳动,那有什么解决方法呢?我们留到后面讲。
2.JSP传递参数到Servlet的时候,Servlet使用HttpServletRequest对象的getParameter方法接收JSP传递过来的参数,当表单的数据量比较多的时候,呵呵,比如一个资料比较详细的用户注册,
那么只能写N多个getParameter。
3.当要做多个业务的时候,比如做一个用户登录和购物的例子,使用JSJ开发的时候需要把相应的业务传到Servlet的doGet or doPost方法中根据传递的参数进行判断需要调用哪个Model,像购物车有添加商品、修改商品数量、删除商品、购买、清空购物车等等操作,我们用JSJ的时候是不是根据动作参数来判断是购买呢还是删除?那这样的话就购物业务的Servlet的doGet or doPost中就写了许多的判断动作的代码,前期写的时候也许条理很清晰,但是后期维护的话是相当麻烦的。
Struts1.X解决这些赘重问题有了一套非常不错的MVC架构,层与层之间的耦合度缩小使开发人员后期维护变得不那么复杂,但节省代码量就得付出配置的代价,Struts1.X的struts-config.xml为Struts专用的xml配置文件,当我们添加MyE的Struts支持时,此文件就已经生成了,如果你要更改struts-config.xml的名称,同时你就得必须在Web.xml中修改加载时读入的xml文件名,如下:
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value> //改成你修改后的名称
</init-param>
我们打开web.xml来分析一下下面这几对标签
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
ActionServlet为Struts1.X的前端控制的Servlet,此Servlet的作用把struts-config.xml中配置的信息映射到相应的操作中,在添加Struts1.X支持的时候我们习惯性的使用action这个名字,上面的<servlet-mapping>标签又起到一个什么样的作用呢?我们可以把ActionServlet想像成一个前端拦截器,<url-pattern>*.do</url-pattern>是拦截所有以.do结尾的路径。
说到前端控制器我们不得不思考一个问题,JSJ有没有前端控制器、既然有前端控制器那有没有后端控制器?
答案是JSJ中有前端控制器但没有后端控制,我们以前用JSJ开发的时候是的都是一个一个的Servlet堆砌出来的前端控制,当用户提交操作的时候通过form的Action路径找到相应的控制然后调用相应的Model业务,这样做不好的地方我们上面已经说过,故此不添赘言。
而我们理想的状态是当用户提交操作的时候不需要进入前端控制器编写代码来判断需要那种业务,当然配置映射是无可避免的,不写代码又不配置,没有这样好的事情。我们再来看一下ActionServlet是怎么根据用户的提交调用相应的后端控制器,打开struts-config.xml分析一下,我们看一下<action-mappings>这个标签,见名之意,此标签为一个动作映射的配置,它里面有一个子标签叫<action> ,在这个Action标签里我们配置映射信息,比如
<action-mappings>
<action
name=”form_name”input=”/发生错误后跳转的页面” path=”/action提交的名称”type=”后端控制器的全文路径”> ///如果你的某个后端控制器有多个方法的话,则要在此标签里添加一个parameter属性,属性内容为你传递参数判断调用那个方法的变量名
<forward name=”key”path=”/pathName” />
//跳转路径,name为跳转页面(path属性)相应的key
</action>
</action-mappings>
可在action-mappings标签中添加多个action子标签,
服务器启动的时候自动在Web.xml中编译ActionServlet,并把struts-config.xml全部读到内存中,如果是第一次加载则创建动态Form,如果已编译过此Form则把Form映射到Action中,通过action标签映射到对应的类文件中。这就是ActionServlet的作用。
我们再来谈谈那些后端控制器,ActionServlet既然可以拦截所有以.do结尾的路径名,我们应该想想。。。用户从页面提交参数到服务器,那么服务器的一些控制已经通过xml配置好了,
那么它做业务分发的时候怎么传递请求响应和表单数据呢?
这里Struts1.X类叫Action,这个Action有一个方法叫
我们来看看这四个参数的用法,ActionMapping封装了一些映射的信息,比如找到服务器转发的跳转路径。ActionForm封装了表单信息,
其他两个参数为就不介绍了,当用户从页面把表单提交到服务器的时候,通过XML的配置自动会调用ActionForm类的execute方法,execute方法只有一个,业务多的话,我们怎么再做分发呢?
Struts1.X
有一个类叫DispatchAction实现于Actiond的子类
BaseAction,DispatchAction,而这个DispatchAction的execute方法与Action的execute方法参数一样,并且可以更改为你自己想要的名称,需要注意的是更改的方法必须与传递过来的参数值一致,这样的话我们就可以做到一个动态的后端控制器.
我们还要谈一下ActionForm,写一个类继承自ActionForm重写它的两个方法
Reset方法为保证数据的安全性,在传入表单数据清空其字段。
Validate为验证其字段,默认返回错误为空,程序将往下执行,如果你编写代码判断出错的话,则跳转到struts-config.xml的action标签的input属性值中,此input属性较好的解释应该是error。
当在外界程序需要设置自己编写的ActionForm子类的字段时,需要用此对象调用 get or set方法,这种get or set完全可以用DynaActionForm所代替,但实体的DTO有时候还是蛮有用的。我们再来看一下ActionForm在XML中的配置信息:
<form-beans>
<form-bean name=”form_name” type=”ActionForm子类的原文路径” />
///当我们配置action标签的时候,action的name属性值就是你配置的form-bean的name属性值
</ form-beans>
这个实体的DTO不好的地方是需要编写一个类继承自ActionForm,而DynaActionForm就做到了把bean信息完全封装在struts-config.xml,我们看一下:
<form-beans>
<form-bean name="userinfo"
type="org.apache.struts.action.DynaActionForm">
<form-property name="id"
type="java.lang.Integer" />
<form-property name="username"
type="java.lang.String" />
<form-property name="password"
type="java.lang.String" />
</form-bean>
</form-beans>
我们现在配置的bean信息是在<form-beans>标签里面配置,需要注意的是form-bean的类型是DynaActionForm,<form-property>标签里封装了以前在DTO中的字段,那么我们就可以用这个form-bean的name属性值映射到<form-property>里配置的字段了。
此笔记还得记录一下Struts1.X的架构思想,用一个关于卖衣服和鞋子工厂的例子来概述一下:
如果要开办一家工厂,首先应该想到的是做什么东西,比如我要做衣服和鞋子,而衣服和鞋子必须得有料子才行,所以我得先弄到料子(DTO或DynaActionForm),这个料子可能不止一种,所以我得先弄到我需要的料子(编写或配置不同的DTO或DynaActionForm),那么我还得创建做衣服和鞋子的部门(类似于DAO等等),部门经理总得有个上级吧,部门经理的上级叫某某经理(DispatchAction),这个某某经理只需要把总经理(ActionServlet)交代要做的一些事情分给下面的部门经理,返回东西给他就行。而总经理上面还有个头儿是董事长(View),这个董事长只需要把他需要的信息告诉下级并且返回东西给董事长就行了,其他的一些制度和约束(XML)都明摆着,按照这个流程运转就OK了。
这是我的理解,有些粗糙,但本质上是这样子的,具体的话还的多花时间去学习。期待Struts2.X。。。