新手指南
Seasar2能够进行热部署,对程序进行的修改,不需要重启服务器。 并且,基本上不需要写配置文件。
参照新手指南工程的安转, 请启动应用服务器,并对JAVA代码和*.properties等属性文件进行修改,感受热部署的威力。
index
访问http://localhost:8080/sa-struts-tutorial, 进入一系列演示应用的一览画面。
请查看webapp(Web应用的根目录)目录,并没有index.jsp(index.html)。 因此,哪个页面被显示呢。それでは、どのページが表示されているのでしょうか。
对于SAStruts来说,访问应用程序根的时候, 如果存在包名.action.IndexAction类的话,自动跳转到这个类。 根包名的详细信息,请参照这里。
IndexAction.java的代码如下。
IndexAction.java
package tutorial.action; import org.seasar.struts.annotation.Execute; public class IndexAction { @Execute(validator = false) public String index() { return "index.jsp"; } }
跳转到这个Action类之后,调用有@Execute注解的执行方法。当存在多个执行方法的时候, 应该调用哪个执行方法,请参照这里。 这回的情况只有1个执行方法,无条件调用index()。
然后将跳转到方法的返回值。IndexAction#index()执行之后将跳转到index.jsp。web.xml里定义了VIEW_PREFIX为/WEB-INF/view,所以具体的路径 为/WEB-INF/view/index.jsp。
然后请看webapp/WEB-INF/struts-config.xml, 并没有多少Action和ActionForm的定义。SAStrut能更具URL找到适当的Action,然后根据Action类的信息, 组合成struts-config.xml里定义的类似的信息。
再看一下webapp/WEB-INF/validation.xml。 也没有多少文件。SAStruts对每个属性可以增加验证用的注解,可以组合成validation.xml类似的信息。
加法运算
从首页跳转到加法运算页面。 或者直接访问加法运算页面http://localhost:8080/sa-struts-tutorial/add/。
/add对应的Action为tutorial.action.AddAction。Action的详细情报请参照这里。
AddAction.java的代码如下。
AddAction.java
package tutorial.action; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.IntegerType; import org.seasar.struts.annotation.Required; public class AddAction { @Required @IntegerType public String arg1; @Required @IntegerType public String arg2; public Integer result; @Execute(validator = false) public String index() { return "index.jsp"; } @Execute(input = "index.jsp") public String submit() { result = Integer.valueOf(arg1) + Integer.valueOf(arg2); return "index.jsp"; } }
Action是POJO,Action的状态也能在Action里定义。相关的信息都在同一类里定义比较好理解。
Seasar2将public变量视为属性。不需要定义getter,setter方法。public变量,EL和Struts也能够识别。 如果有兴趣的话,请参看org.seasar.struts.action的代码。
对于请求的处理,请参考执行方法。 执行方法,有@Execute注解,返回值为跳转路径。
如果有多个执行方法的时候,通过URL指定调用哪个执行方法。
下例调用AddAction#index()が呼び出されます。/add/后面指定了需要调用的执行方法名index。
http://localhost:8080/sa-struts-tutorial/add/index
执行方法没有定义的时候,默认调用index。下面的URL和上面的一样都是调用AddAction的index方法。
http://localhost:8080/sa-struts-tutorial/add/
表单提交时,按纽的名字就是需要调用执行方法的名字。 下例就是,调用Action的submit()方法。
<input type="submit" name="submit" value="サブミット"/>
@Execute的详细,请参照这里。
通过声明式验证,对变量添加验证用的注解,@Execute的valiator属性设为true(默认)。validator=true时,如果验证结果为NG的时候,跳转的页面必须通过input属性来指定。 验证的详细,请参照这里。
/add/index.jsp如下所示。
/add/index.jsp
<html> <head> <title>Add</title> </head> <body> <html:errors/> <s:form> <html:text property="arg1"/> + <html:text property="arg2"/> = ${f:h(result)}<br /> <input type="submit" name="submit" value="サブミット"/> </s:form> </body> </html>
所有JSP需要的共通的taglib的声明在webapp/WEB-INF/view/common/common.jsp中定义。common.jsp的详细信息请参照JSP。
为了显示验证结果为NG时的错误信息,定义了标签html:errors。
为了接收被提交的值,定义了s:form。action属性,能够自动计算出来,所以通常不需要指定。
为了接收输入值,定义了html:text标签。property属性定义了Action的属性名。
提交时为了调用AddAction#submit(),submit的name属性的值设为submit。
提交后,根据注解将验证输入的arg1,arg2的值是不是必须输入,是不是整数。
如果为NG时,跳转回input属性制定的index.jsp。 如果OK,则掉哟国内submit()方法。
submit()的返回值为index.jsp,所以还将跳转到index.jsp。
Action的result属性,和请求的属性同名所以可以调用${f:h(result)}来显示。f:h对HTML进行Escape操作。详细信息请参照这里。
文件上传
从首页访问文件上传页面。 或者通过URL http://localhost:8080/sa-struts-tutorial/upload直接访问文件上传页面。
/upload对应的Action类为tutorial.action.UploadAction。Action的详细信息,请参照这里。
UploadAction.javaのソースコードは次のようになります。
UploadAction.java
package tutorial.action; import java.io.BufferedOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import org.apache.commons.fileupload.FileUploadBase.SizeLimitExceededException; import org.apache.struts.action.ActionMessage; import org.apache.struts.action.ActionMessages; import org.apache.struts.upload.FormFile; import org.seasar.framework.container.annotation.tiger.Binding; import org.seasar.framework.container.annotation.tiger.BindingType; import org.seasar.framework.exception.IORuntimeException; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.Required; import org.seasar.struts.upload.S2MultipartRequestHandler; import org.seasar.struts.util.ActionMessagesUtil; public class UploadAction { @Required @Binding(bindingType = BindingType.NONE) public FormFile formFile; @Binding(bindingType = BindingType.NONE) public FormFile[] formFiles; public HttpServletRequest request; public ServletContext application; @Execute(validator = false) public String index() { SizeLimitExceededException e = (SizeLimitExceededException) request .getAttribute(S2MultipartRequestHandler.SIZE_EXCEPTION_KEY); if (e != null) { ActionMessages errors = new ActionMessages(); errors.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "errors.upload.size", new Object[] { e.getPermittedSize(), e.getActualSize() })); ActionMessagesUtil.addErrors(request, errors); } return "index.jsp"; } @Execute(input = "index.jsp") public String upload() { ActionMessages messages = new ActionMessages(); upload(formFile, messages); for (FormFile file : formFiles) { upload(file, messages); } ActionMessagesUtil.addMessages(request, messages); return "index.jsp"; } protected void upload(FormFile file, ActionMessages messages) { if (file.getFileSize() == 0) { return; } String path = application.getRealPath("/WEB-INF/work/" + file.getFileName()); try { OutputStream out = new BufferedOutputStream(new FileOutputStream( path)); try { out.write(file.getFileData(), 0, file.getFileSize()); } finally { out.close(); } } catch (IOException e) { throw new IORuntimeException(e); } messages.add(ActionMessages.GLOBAL_MESSAGE, new ActionMessage( "messages.upload.complete", new Object[] { path })); } }
文件上传,在Action或者ActionForm里定义FormFile类型的属性。 通过FormFile来取得上传得文件。
因为FormFile是借口,Seasar2默认为自动绑定的对象,会抛出执行时没有DI的警告。为了防止出现警告,需要在属性上增加@Binding(bindingType = BindingType.NONE)的注解。
属性的类型也可以指定为数组FormFile[],这样可以接受多个FormFile。
如果是FormFile数组时,不能指定注解式验证。 实际上传了没有,可以通过FormFile#getFileSize()返回0来进行判断。
上传文件的刀削,超过定义的上限(在struts-config.xml的标签controller的maxFileSize属性的值)时,抛出SizeLimitExceededException异常。
发生这种异常的时候,将跳转到index方法。 所以在index方法中,检测是否存在SizeLimitExceededException异常,来检测上传文件是否超过上限。
/upload/index.jsp如下所示。
/upload/index.jsp
<html> <head> <title>Upload</title> </head> <body> <html:errors/> <html:messages id="m" message="true"> ${f:h(m)}<br /> </html:messages> <s:form action="/upload" enctype="multipart/form-data"> <input type="file" name="formFile" /><br /> <c:forEach varStatus="s" begin="0" end="1"> <input type="file" name="formFiles[${s.index}]" /><br /> </c:forEach> <input type="submit" name="upload" value="アップロード"/> </s:form> </body> </html>
文件上传的时候,form的enctype属性设为"multipart/form-data"。 上传用的tag(<input type="file" ... />)的name,和Action或者ActionForm的属性值一致。FormFile数组时,name属性的值为属性名[数组的序号]。
客户端验证
从首页访问客户端验证。或者通过URL http://localhost:8080/sa-struts-tutorial/clientValidator/进行访问。
什么都没有输入的时候点击「aaaが必須」。JavaScript进行验证,并显示「aaaは必須です。」的信息。
然后点击「bbbが必須」。JavaScript进行验证,并显示「aaaは必須です。」的信息。 我们可以了解到每个按钮进行了不同的验证。
/clientValidator对应的Action类为tutorial.action.ClientValidatorAction。Action的详细信息请参照这里。
ClientValidatorAction.java的代码如下。
ClientValidatorAction.java
package tutorial.action; import org.seasar.struts.annotation.Execute; import org.seasar.struts.annotation.Required; public class ClientValidatorAction { @Required(target = "submit") public String aaa; @Required(target = "submit2") public String bbb; @Execute(validator = false) public String index() { return "index.jsp"; } @Execute(validator = true, input = "index.jsp") public String submit() { return "index.jsp"; } @Execute(validator = true, input = "index.jsp") public String submit2() { return "clientValidator.jsp"; } }
按钮(方法)不同调用的验证不同。 验证的注解taget属性可以指定方法名。多个方法的时候,可以用逗号分开。
/clientValidator/index.jspは次のようになります。
/clientValidator/index.jsp
<html> <head> <title>Client Validator</title> <html:javascript formName="clientValidatorActionForm_submit"/> <html:javascript formName="clientValidatorActionForm_submit2"/> </head> <body> <html:errors/> <s:form action="/clientValidator"> aaa:<html:text property="aaa"/><br /> bbb:<html:text property="bbb"/><br /> <input type="submit" name="submit" value="aaaが必須" onclick="forms[0].name='clientValidatorActionForm_submit'; return validateClientValidatorActionForm_submit(forms[0]);"/> <input type="submit" name="submit2" value="bbbが必須" onclick="forms[0].name='clientValidatorActionForm_submit2'; return validateClientValidatorActionForm_submit2(forms[0]);"/> </s:form> </body> </html>
为了输出验证用的JavaScript,使用标签html:javascript。formName属性指定为「Action名 + Form + _ + 方法名」。
为了调用验证的JavaScript按钮的onclick属性里,form的name属性设为formName属性的值,然后调用 「validate + formName属性的值」方法。