新手指南
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属性的值」方法。