背景:
从本期的电子杂志内容介绍中,大家已经看到了web发展的整个历史,
Struts 自从 2001 6月1.0 Release之后经历了漫长的春秋,有着庞大的用户群体, 并且IBM, Tomcat Web控制台都采用了Struts Framework..
web框架有好多种选择,并不一定要使用Struts, 但如果你正刚开始使用的话,希望下面的一些实践能给你带来帮助, 仅此而已. 所以,请原谅,本文没有什么实例代码, 因为今天早上,我的好友Leemassn 跟我说,这东西已经烂掉了:( ,所以相信基本用法你们了解的比我多,也并没有涉及到一些高级用法, 我下面所说的内容, 也均属纸上谈兵.
1. 正确是导向,才是使用Web Framework的真正目的.
并不时因为Struts 耳儒目染的多了,我们才一定要使用Struts ,并不时MVC就一定要使用Struts ,当你能灵活游刃于StrutsActionForm ,Action ,Lib之间, "疏能跑马,密不透风" 而且能够扩展的你的Web 框架时,之后才是我们运用框架的真正灵便之处.
先说一些与Struts 无关的话题,因为Struts并不能取代你应用框架的全部,无论项目大或小,复杂或者简单
一些建议:
1. 请选用支持广泛的JDO 或者Hibernate,或者iBates,来做你的数据持久层,
2. 请加入Struts-el标签,
3. 请选用应用服务器的JNDI连接数据库
4. 请使用StrutsTestCase做单元测试
5. 请阅读sun 的命名规范.
1.Struts 之对象使用篇
Struts 版本换了好多了,,如果你只是使用最基本的Form,Action相信0.5 就够了,但眼下大家用的都是1.1吧
每写个应用都用继承BaseAction ,和BaseForm , 当然BaseAction 最好是继承LookupDispatchAction, BaseForm 最好是继承ValidatorActionForm (这两个都是从1.1开始有的)
ValidatorActionForm也是继承ActionForm , 因为1.1有了PlugIn,当你使用ValidatorPlugIn使得你的FormBean具备了验证能力啦.
(1.1 也同时 DynaActionForm ,相信大家都知道这个Bean,但它似乎除了让你少写一些ActionForm之外没什么用处,除非你是想完全不给予ActionForm,这样偶尔用到ActionForm的时候也可以小用一把DynaActionForm, 看来Struts对自己的ActionForm似乎没有多信息,欲弃欲留,我们都知道ActionForm 是不能完全充当Model(Model已应该是你的持久层)的,那我们至少可以用它来传值吧,方便支出就是不需要 一个个表单内容去get ,再set了.所以,我们有必要使用ActionForm , 同时我们也要对每个属性进行用户端的输入校验,于是我们选用ValidatorActionForm.)
LookupDispatchAction 继承DispatchAction, 自己多加两个方法,就是getKeyMethod(),localMap ,它能帮你 从本地submit的资源文件,比如submite 的名字是add 就能找到你的add方法.
2.Struts 之标签库篇.
相信Struts是很讲究复用的
标签库主要还是用做View的,所以,在设计ActionForm的时候大可大胆设计,别担心他们不能在jsp页面上很好的显示, 该是Data类型就是,也可以使用List,Map ,这样的符合类型,也可以是引用对象类型,因为他们能很方便的在页面上显示
如果Test1Form包含了 各种Model对象,
Test1Form{
Student student //有id,name等属性
Teach teach //有id, classid等属性
List classes //一组Classes 对象 Classes又含有 classid等属性
Test2Form test2form --- //有 attr2 等property
....
}
那么
<html:form action="/test1">
<bean:define name="test1Form" property="test2Form" id="test2"
type="com.yourcompony.Test2Form" />
<html:text name="test2" property="test2.attr2" />
<html: text name="studentsid" property="student.id">
<html: text name="techname" property="teach.name">
<html: text name="classid" property="classes[0].classid">
</html:form >
看到了,它们其实什么都能得到,发挥你的想象标签库能展示你的任何对象属性值.
记得在ActionForm reset里初始化一下,并且依次得到对象.这样你同样能把这些内容全部递交给ActionServlet. 如果没使用过这些,赶紧试试吧.
尽量使用EL标签: 主要用于复杂的逻辑判断,
举个例子 <logic:present name="yourForm" property="a"> 判断了a是否存在,但你又想知道a 是否等于1,怎么样头痛了吧,换成el 那就是 <c:if test='${yourForm.a =="1" }'> ....</c:if>
补充一些:对于前台的显示,,Struts标签不得不说是似乎已经成了一根鸡肋, 美丽,容易操作的的界面,往往要以来于其他技术,Struts的标签使用的最大的目的也是为了方便于数据显示, 美丽复杂操作还是要依赖你的Html与JavaScript.
3.Struts 之异常处理篇.
基本上来讲,Struts 给异常处理提供了较为完善的框架.
按道理讲,当一个系统正式交付的时候,,我们还让用户看到那些 一大串英文字母是一种极端粗暴的行为, 所以,我们从两头专起.
1.使用validationForm做可验证的确formBean , 当然你也可以使用你积累下来比较丰富的javascript,无论那种方式,都有一点会让你受益,就是学会使用正则表达.
2.Action处理中,在try catch 使用saveMessage 使用saveErrors()或者saveMessaes是,因为(Struts1.2开始抛弃了ActionErrors,全部是ActionMessages了)
这里就不得不说到一些构架上的一些东西了. Struts Action中处理的异常,已经不该是你的原始异常了.在你的Dao层要抛出DataException,而在你的业务层,需要捕获到,作为你的自定义异常抛出,这样你在struts里捕获到的异常才是友好的,且是有意义的.
我们都知道Dao那一层都是抛一个统一的异常比如DataExpcetion ,
然后到你的业务处理 Service 那一层能就捕获DataExpcetion, 因为Serivce那一层,你总归是知道自己要做什么, 因此建议在Service 那层捕获DataException 之后,打印 debug日志, 然后再抛出业务逻辑异常
比如在业务处理,添加一个学生过程,如果
public void doAddStudent(Student student) throws DupKeyExption,Ser{
try{
if((Student)load(student.getId()).getId()==student.getId()){
throw new DupKeyExcption("改同学已经存在");
}
studentDao.add();
}catch(DataException e){
logger.debug("错误信息:",e);
throws new StudentException("增加失败");
}
}
try {
service.doAddStudent();
}catch (DupikeyException e1){
//重复添加了
errors.add("message",new ActionError("student.error.dupkey"));
}catch (Exception e) {
//添加失败
errors.add("message",new ActionError("student.error.add"));
logger.error(e.getMessage());
e.printStackTrace();
}
saveErrors(request, errors);
至于显示嘛,直接使用<html:errors/>就OK了.但是你要觉得没有空白的地方显示,也可以使用
<html:messages id="message">
<script language=javascript>window.alert('<bean:write name="message"/>')</script>
</html:messages>
对于,全局的异常,那你最好forward一个 gloable-forward 的 showErorr.jsp之类的jsp
(注:Struts1.2开始,抛弃了ActionErrors了,统一使用使用ActionMessages了 error其实是个多余的对象,用法一样)
4 Struts 之 扩展篇
Struts 的Plugin 给Struts应用程序算是留了一到后门,扩展Struts 可以由此窃入
Plugin 能使得你的web应用程序在startup或shutdown 的时候,能完成一部分操作. 它是基于配置的,你可以很方便的外挂一些应用程序,
比如HibernatePlugin
public class HibernatePlugIn implements PlugIn {
public static final String SESSION_FACTORY_KEY
= SessionFactory.class.getName();
private static Log _log = LogFactory.getLog(HibernatePlugIn.class);
private boolean _storedInServletContext = true;
private String _configFilePath = "/hibernate.cfg.xml";
private ActionServlet _servlet = null;
private ModuleConfig _config = null;
private SessionFactory _factory = null;
/**
* Destroys the <code>SessionFactory</code> instance.
*/
public void destroy() {
_servlet = null;
_config = null;
try {
_log.debug("Destroying SessionFactory...");
_factory.close();
_log.debug("SessionFactory destroyed...");
} catch (Exception e) {
_log.error("Unable to destroy SessionFactory...(exception ignored)",
e);
}
}
/**
* Initializes the <code>SessionFactory</code>.
* @param servlet the <code>ActionServlet</code> instance under which the
* plugin will run.
* @param config the <code>ModuleConfig</code> for the module under which
* the plugin will run.
*/
public void init(ActionServlet servlet, ModuleConfig config)
throws ServletException {
_servlet = servlet;
_config = config;
initHibernate();
}
/**
* Initializes Hibernate with the config file found at
* <code>configFilePath</code>.
*/
private void initHibernate() throws ServletException {
Configuration configuration = null;
URL configFileURL = null;
ServletContext context = null;
try {
configFileURL = HibernatePlugIn.class.getResource(_configFilePath);
context = _servlet.getServletContext();
if (_log.isDebugEnabled()) {
_log.debug("Initializing Hibernate from "
+ _configFilePath + "...");
}
configuration = (new Configuration()).configure(configFileURL);
_factory = configuration.buildSessionFactory();
if (_storedInServletContext) {
_log.debug("Storing SessionFactory in ServletContext...");
context.setAttribute(SESSION_FACTORY_KEY, _factory);
}
} catch (Throwable t) {
_log.error("Exception while initializing Hibernate.");
_log.error("Rethrowing exception...", t);
throw (new ServletException(t));
}
}
/**
* Setter for property configFilePath.
* @param configFilePath New value of property configFilePath.
*/
public void setConfigFilePath(String configFilePath) {
if ((configFilePath == null) || (configFilePath.trim().length() == 0)) {
throw new IllegalArgumentException(
"configFilePath cannot be blank or null.");
}
if (_log.isDebugEnabled()) {
_log.debug("Setting 'configFilePath' to '"
+ configFilePath + "'...");
}
_configFilePath = configFilePath;
}
/**
* Setter for property storedInServletContext.
* @param storedInServletContext New value of property storedInServletContext.
*/
public void setStoredInServletContext(String storedInServletContext) {
if ((storedInServletContext == null)
|| (storedInServletContext.trim().length() == 0)) {
storedInServletContext = "false";
}
if (_log.isDebugEnabled()) {
_log.debug("Setting 'storedInServletContext' to '"
+ storedInServletContext + "'...");
}
_storedInServletContext
= new Boolean(storedInServletContext).booleanValue();
}
}
5. 测试篇
Struts通过传统的手工输入进行测试, 效率教低, 请尽量使用单元测试
基本使用如下
public class BaseActionTest extends MockStrutsTestCase {
public StudentActionTest(String arg0) {
super(arg0);
}
public static void main(String[] args) {
junit.textui.TestRunner.run(UserActionTest.class);
}
public void setUp() throws Exception {
super.setUp();
setContextDirectory(new File("E://webapp//webContext"));
}
protected void tearDown() throws Exception {
super.tearDown();
}
public void testAdd() {
setRequestPathInfo("/student");
addRequestParameter("method","add");
actionPerform(); //执行
verifyForward("success");
}
}