struts2-Convention+Plugin 中文
Convention Plugin
原文:http://cwiki.apache.org/WW/convention-plugin.html
翻译:石太祥(ealpha@gmail.com http://www.lalfa.com)
· 1 Introduction
· 2 Setup
· 3 Converting a Codebehind based application to Convention
· 4 Hello world
· 7 Chaining
· 8 XWork packages
o 9.1 Action annotation
o 9.2 InterceptorRef annotation
o 9.3 Result annotation
o 9.4 Namespace annotation
o 9.5 ResultPath annotation
o 9.6 ParentPackage annotation
o 9.7 ExceptionMapping Annotation
· 10 Actions in jar files
· 11 Automatic configuration reloading
· 12 Troubleshooting
o 12.1 Tips
o 12.2 Common Errors
从struts2.1版本开始,Convention Plugin作为替换替换Codebehind Plugin来实现Struts2的零配置。
- 包命名习惯来指定Action位置
- 命名习惯制定结果(支持JSP,FreeMarker等)路径
- 类名到URL的约定转换
- 包名到命名空间(namespace)的约定转换
- 遵循SEO规范的链接地址(即:使用my-action 来替代 MyAction)
- 基于注解的Action名
- 基于注解的拦截机(Interceptor)
- 基于注解的命名空间(Nameespace)
- 基于注解的XWork包
- 默认action以及默认的结果(比如:/products 将会尝试寻找com.example.actions.Products 或 com.example.actions.products.Index进行处理)
无需配置Convention即可使用Convention,Convention的某些约束习惯可以通过配置属性来控制,您也可以在类中覆写其中的方法来达到扩展目地。
使用Convention插件,你需要将其JAR文件放到你应用的WEB-INF/lib目录中,你也可以在你Macen项目的POM文件中添加下面包依赖
<dependency>
<groupId>org.apache.struts</groupId>
<artifactId>struts2-convention-plugin</artifactId>
<version>2.1.6</version>
</dependency>
跳转到此页面,查看需要修改的变化和小提示
如果你想在你系统中结合Convention插件使用REST。需要在你项目的struts.xml中添加如下配置
<constantname="struts.convention.action.suffix" value="Controller"/>
<constantname="struts.convention.action.mapAllMatches" value="true"/>
<constantname="struts.convention.default.parent.package" value="rest-default"/>
到目前为止,你已经在你项目中添加了Convention插件支持,首先我们从一个非常简单的例子开始入手。本例中,我们将演示根据访问URL来访问固定的Action,默认情况下,Convention会默认所有的结果页面都存储在WEB-INF/content下,你也可以在struts的properties文件中设定struts.convention.result.path的值到一个新的路径。路径最后“/”是不必要的,Convention会自动进行处理。以下是本例的JSP文件
WEB-INF/content/hello-world.jsp
<html>
<body>
Helloworld!
</body>
</html>
启动Tomcat或其他你所使用的JEE容器,在浏览器访问http://localhost:8080/hello-world,你可看到以下信息:
WEB-INF/content/hello-world.jsp
Helloworld!
这表明,Convention已经能正常运行,并找到了结果。即使在没有action存在情况下,convention也会根据URL规则来找到结果页面。
我们继续扩展本例并添加代码实现类。为了实现本功能,首先需要Convention能正确找到我们的Action类,默认情况下,Convention会找到com.opensymphony.xwork2.Action的实现类,或制定包中以Action结尾的类 action
Convention使用以下方法来搜索类路径,首先,Convention会从根package中寻找包名含有struts, struts2, action or actions的任意packages。下一部,Convention从前一步找到的package以及其子package中寻找com.opensymphony.xwork2.Action的实现以及以Action结尾的类,下面为Convention寻找的类
Classes
com.example.actions.MainAction
com.example.actions.products.Display(implements com.opensymphony.xwork2.Action)
com.example.struts.company.details.ShowCompanyDetailsAction
每一个被Convention找到action都会对应一个明确的URL地址,URL以package的名字以及Action类名为基础。
首先Convention从根package以及类所在的package名来确定对应的URL中的路径(namespace),以下就是根据package确定的URL namespace
Namespaces
com.example.actions.MainAction-> /
com.example.actions.products.Display-> /products
com.example.struts.company.details.ShowCompanyDetailsAction-> /company/details
接下来Convention需要确定URL的具体资源部分。第一步取消类名中的Action,并以”-”来分割类名的其他部分,且将每个分部的首字母转为小写。如下所示
Full URLs
com.example.actions.MainAction-> /main
com.example.actions.products.Display-> /products/display
com.example.struts.company.details.ShowCompanyDetailsAction-> /company/details/show-company-details
你也可以通过配置struts.convention.exclude.packages来告诉Convention忽略某些包,也可以设置struts.convention.package.locators用来更改Convention默认的根packages,最后你还可以设置 struts.convention.action.packages.来让Convention只搜索特定package下的Action
以下就是action类的实现代码:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorldextends ActionSupport {
privateString message;
publicString getMessage() {
return message;
}
publicString execute() {
message ="Hello World!";
return SUCCESS;
}
}
编译以上代码,并将其class放到 WEB-INF/classes中,Convention将会将/hello-world 映射到这个Action.部署上面的类以后,我们在JSP文件中添加打印message的语句,具体代码如下:
WEB-INF/content/hello-world.jsp
<html>
<body>
Themessage is ${message}
</body>
</html>
启动应用服务器,在浏览器访问 http://localhost:8080/hello-world地址,我们看到如下结果界面:
Result
Themessage is Hello World!
Struts启动后,Convention将预设好应用中的所有的action,默认情况下,配置将包含在你应用中能找到的所有JSP文件。
同时您也可在Action代码中设置与习惯不同的结果页面。通常Action方法返回一个字符串,通过返回的字符串找到结果页面,而使用Convention允许你在action代码中指定和返回字符串不同的结果页面。
编译下面的例子。我们希望在action中返回zero 而不是success,第一步,我们更新action类,返回zero。
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
public class HelloWorldextends ActionSupport {
privateString message;
publicString getMessage() {
return message;
}
publicString execute() {
if (System.currentTimeMillis() % 2 == 0) {
message = "It's 0";
return"zero";
}
message ="It's 1";
return SUCCESS;
}
}
接下来,我们添加一个新的JSP页面 WEB-INF/content/hello-world-zero.jsp。需要注意的是,文件名的第一部分和action名是对应的,后面的部分和action返回的字符串对应。这就是convention确定具体使用那个页面来渲染结果。下面是修改后的JSP代码:
WEB-INF/content/hello-world.jsp
<html>
<body>
Theerror message is ${message}
</body>
</html>
现在,你可以编辑你的程序,重启应用,刷新页面,根据当前时间不通,会看到不通的渲染结果页面
结果页面的类型会自动匹配文件,支持的渲染页面的格式为:jsp.ftl,vm,html,htm.下面是actiong和结果模版的映射关系:
URL |
Result |
File that could match |
Result Type |
/hello |
success |
/WEB-INF/content/hello.jsp |
Dispatcher |
/hello |
success |
/WEB-INF/content/hello-success.htm |
Dispatcher |
/hello |
success |
/WEB-INF/content/hello.ftl |
FreeMarker |
/hello-world |
input |
/WEB-INF/content/hello-world-input.vm |
Velocity |
/test1/test2/hello |
error |
/WEB-INF/content/test/test2/hello-error.html |
Dispatcher |
如果在一个action结果中调用另外一个action,他们俩将被链接到一起,如果在第一个action代码中未定义result,如下代码:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.Action;
import com.opensymphony.xwork2.ActionSupport;
public class HelloActionextends ActionSupport {
@Action("foo")
publicString foo() {
return"bar";
}
@Action("foo-bar")
publicString bar() {
return SUCCESS;
}
}
“foo” action执行时候,由于找不到结果,convention尝试在同一个包下寻找action名为”foo-bar”的action。如果找到这样的action,convention将会调用并返回相关联的result。
为了避免冲突,可将action放在一个自定义XWORK的package下。package命名由action所在的Java包,action对应的URL中namespace部分以及action的parent XWork package三个部分组成。parentXWork package值在属性 struts.convention.default.parent.package中指定(默认为conventionDefault),package的属性值须继承于strutsDefault
因此,Convention插件中XWORK packages采用如下命名规则:
XWork package naming
<java-package>#<namespace>#<parent-package>
Usingour example from above, the XWork package for our action would be:
上例中,action对应的 XWORK package如下:
XWork package naming
com.example.actions#/#conventionDefault
Convention使用某些注解语句来覆写插件默认的action到url的映射和自动搜索渲染到的页面。此外,你还可以修改action配置文件中定义的父XWORK的包信息
Convention插件可以使用Action注解语句来修改action返回的URL地址。本注解同时也允许包含在Actions语句中,用来使一个action对应于多个URL。在action方法中使用本注解语句,可以参考以下代码
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
importorg.apache.struts2.convention.annotation.Action;
public class HelloWorldextends ActionSupport {
@Action("/different/url")
publicString execute() {
return SUCCESS;
}
}
现在我们action类中将使用/different/url 来替代默认的 /hello-world,如果未指定@Result(参考下节),result的路径将会使用action的namespace,上面的例子中将会返回一下路径 "/WEB-INF/content/different/url.jsp"。
Action类中的单个方法可以使用Actions注解来映射多个地址。
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
importorg.apache.struts2.convention.annotation.Action;
importorg.apache.struts2.convention.annotation.Actions;
public class HelloWorldextends ActionSupport {
@Actions({
@Action("/different/url"),
@Action("/another/url")
})
publicString execute() {
return SUCCESS;
}
}
另外的 Action 或Actions 的使用方法是,在单个action类中定义多个action方法,每个方法对应一个不同的地址。下面是多个action方法的范例:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
importorg.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
public class HelloWorldextends ActionSupport {
@Action("/different/url")
publicString execute() {
return SUCCESS;
}
@Action("url")
publicString doSomething() {
return SUCCESS;
}
}
前面的例子中,第二个URL地址是不推荐的,上面url将使用java包名作为namespace,而不会直接使用Action注解的地址。
Interceptor和 interceptor stacks同样可以使用interceptorRefs 注解来指定。下例演示了在action中同时添加"validation"和"defaultStack"拦截机。
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
importorg.apache.struts2.convention.annotation.Action;
importorg.apache.struts2.convention.annotation.Actions;
public class HelloWorldextends ActionSupport {
@Action(interceptorRefs={@InterceptorRef("validation"), @InterceptorRef("defaultStack")})
publicString execute() {
return SUCCESS;
}
@Action("url")
publicString doSomething() {
return SUCCESS;
}
}
可以通过params属性来将参数传递给结果。属性的值是一个偶数个元素的String的数组,由形如{"key0", "value0, "key1","value1" ... "keyN", "valueN"}所组成,举个例子:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.Action;
importorg.apache.struts2.convention.annotation.Actions;
public class HelloWorldextends ActionSupport {
@Action(interceptorRefs=@InterceptorRef(value="validation",params={"programmatic","false","declarative", "true}))
publicString execute() {
return SUCCESS;
}
@Action("url")
publicString doSomething() {
return SUCCESS;
}
}
如果未指定interceptors,将会使用默认的。
Interceptors可以在方法级进行指定,使用Action注解或在类上使用InterceptorRefs注解。Class级别的拦截会被应用到类包含的所有action上。可以参考下面例子:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
importorg.apache.struts2.convention.annotation.Action;
importorg.apache.struts2.convention.annotation.Actions;
@InterceptorRefs({
@InterceptorRef("interceptor-1"),
@InterceptorRef("defaultStack")
})
public class HelloWorldextends ActionSupport {
@Action(value="action1", interceptorRefs=@InterceptorRef("validation"))
publicString execute() {
return SUCCESS;
}
@Action(value="action2")
publicString doSomething() {
return SUCCESS;
}
}
下面的拦截机将会应用到“action1”中:"interceptor-1","defaultStack"中的所有拦截机,"validation"
"defaultStack"中的所有拦截机也会对”action2”生效
Convention允许action类为每个action定义不同的results,results分为两类,全局的(global)和本地的(local),全局results可以被action类中所有的action分享,这种results在action类上使用注解进行声明。本地results只能在action方法上进行声明。下面是两种results注解的例子:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
importorg.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Actions;
importorg.apache.struts2.convention.annotation.Result;
importorg.apache.struts2.convention.annotation.Results;
@Results({
@Result(name="failure", location="fail.jsp")
})
public class HelloWorldextends ActionSupport {
@Action(value="/different/url",
results={@Result(name="success", location="http://struts.apache.org", type="redirect")}
)
publicString execute() {
return SUCCESS;
}
@Action("/another/url")
publicString doSomething() {
return SUCCESS;
}
}
参数同样可以在results中通过params属性进行传递,和上面一样,由形如{"key0", "value0, "key1","value1" ... "keyN", "valueN"}所组成。可参考下例:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.Action;
importorg.apache.struts2.convention.annotation.Actions;
importorg.apache.struts2.convention.annotation.Result;
importorg.apache.struts2.convention.annotation.Results;
public class HelloWorldextends ActionSupport {
@Action(value="/different/url",
results={@Result(name="success", type="httpheader", params={"status","500", "errorMessage", "Internal Error"})}
)
publicString execute() {
return SUCCESS;
}
@Action("/another/url")
publicString doSomething() {
return SUCCESS;
}
}
namespace注解允许action使用指定的路径替代默认的以package包名作为路径。本注解可以在action类或Java包中的package-info.java类中进行设置。设置在action类中的namespace注解,对本action类中所有的action都有效,这是不完全合乎规范的actionURL处理地址。设置在package-info.java中的namespace注解,将会改变本java包下所有的action的默认namespace。下面是此注解的例子:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
importorg.apache.struts2.convention.annotation.Action;
importorg.apache.struts2.convention.annotation.Namespace;
@Namespace("/custom")
public class HelloWorldextends ActionSupport {
@Action("/different/url")
publicString execute() {
return SUCCESS;
}
@Action("url")
publicString doSomething() {
return SUCCESS;
}
}
在上例中的action会对2个不同的地址响应:/different/url 和 /custom/url
下面是一个在package-info.java中使用namespace注解的例子
com/example/actions/package-info.java
@org.apache.struts2.convention.annotation.Namespace("/custom")
package com.example.actions;
这会改变com.example.actions包下所有action的默认namespace。请注意一点,本注解不会应用到子一级的包中。
ResultPath注解用来更改默认的results存储路径,注解可以放到action的类中,也可以放到package-info.java文件夹中。参考下例:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.convention.annotation.Action;
importorg.apache.struts2.convention.annotation.ResultPath;
@ResultPath("/WEB-INF/jsps")
public class HelloWorldextends ActionSupport {
publicString execute() {
return SUCCESS;
}
}
上面的result将以WEB-INF/jsps 替换默认的WEB-INF/content
ParentPackage注解用来定义具体action类的父XWork包或java包,下面例子演示了在action类上使用本注解:
com.example.actions.HelloWorld
package com.example.actions;
import com.opensymphony.xwork2.ActionSupport;
importorg.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.ParentPackage;
@ParentPackage("customXWorkPackage")
public class HelloWorldextends ActionSupport {
publicString execute() {
return SUCCESS;
}
}
ExceptionMapping 注解用来影射action抛出的异常。可以参考exception mappingdocumentation 获得详细信息。注解用类级别,在这种情况下,注解会应用到类里面的所有action
ExceptionsActionLevelAction.java
@ExceptionMappings({
@ExceptionMapping(exception ="java.lang.NullPointerException", result ="success", params = {"param1","val1"})
})
public class ExceptionsActionLevelAction {
publicString execute()throws Exception {
returnnull;
}
}
可以在ExceptionMapping注解中使用params 属性来传递具体值给结果渲染页。ExceptionMapping注解同样可以在action级别进行设置:
public class ExceptionsMethodLevelAction {
@Action(value ="exception1", exceptionMappings = {
@ExceptionMapping(exception = "java.lang.NullPointerException", result ="success", params = {"param1","val1"})
})
publicString run1()throws Exception {
returnnull;
}
}
默认情况下,Convention插件不会从jar文件中寻找action。如果想实现这一功能,jar文件必须被struts.convention.action.includeJars所定义的正则匹配到。在例子中 myjar1.jar和 myjar2.jar 将被插件检测到:
<constantname="struts.convention.action.includeJars" value=".*/myjar1.*?jar(!/)?,.*/myjar2*?jar(!/)?"
提示:正则表达式只针对jar文件的路径进行匹配,而不是文件名。jar的URL应该包含jar文件的路径并以"!/"结尾。
Convention插件可以自动重新加载配置的功能,无需重启容器,就可以刷新类中包含的action。这自动加载automatic xml配置文件类似。你必须在struts.xml中添加以下代码来启用本功能:
<constantname="struts.devMode" value="true"/>
<constantname="struts.convention.classes.reload" value="true" />
此功能没有在所有容器中进行过测试,强力建议不要在生产环境中使用。
Namespaces and Results Make sure the namespace of the action is matched by one of the locators. The rest of the namespace after the locator, will be the namespace of the action, and will be used to find the results. For example, a class called "ViewAction" in the package "my.example.actions.orders" will be mapped to the URL /orders/view.action, and the results must be under /WEB-INF/content/orders, like /WEB-INF/content/orders/view-success.jsp. |
Use the Configuration Browser Plugin Add the Config Browser Plugin plugin to the lib folder or maven dependencies, and then visit: http://localhost:8080/CONTEXT/config-browser/index.action, to see the current action mappings. |
Enable trace or debug mode The Convention plugin can generate a rather verbose output when set to debug mode for logging. Use "Trace" logging level if you are using the JDK logger. If you are using Log4J, you can do something like: log4j.logger.org.apache.struts2.convention=DEBUG |
- I get an error like "There is no Action mapped for namespace /orders and action name view.". This means that the URL /orders/view.action is not mapping to any action class. Check the namespace and the name of the action.
- I get an error like "No result defined for action "my.example.actions.orders.ViewAction and result success". This means that the action was mapped to the right URL, but the Convention plugin was unable to find a success result for it. Check that the result file exists, like /WEB-INF/content/orders/view-success.jsp.
- I get lots of errors like "java.lang.Exception: Could not load org/apache/velocity/runtime/resource/loader/ClasspathResourceLoader.class". This happens when struts.convention.action.includeJars is matching jar URLs from external jars.
Adda constant element to your struts config file to change thevalue of a configuration setting, like:
<constantname="struts.convention.result.path" value="/WEB-INF/mytemplates/"/>
Name |
Default Value |
Description |
struts.convention.action.includeJars |
|
Comma separated list of regular expressions of jar URLs to be scanned. eg. ".*myJar-0\.2.*,.*thirdparty-0\.1.*" |
struts.convention.action.packages |
|
An optional list of action packages that this should create configuration for (they don't need to match a locator pattern) |
struts.convention.result.path |
/WEB-INF/content/ |
Directory where templates are located |
struts.convention.result.flatLayout |
true |
If set to false, the result can be put in its own directory: resultsRoot/namespace/actionName/result.extension |
struts.convention.action.suffix |
Action |
Suffix used to find actions based on class names |
struts.convention.action.disableScanning |
false |
Scan packages for actions |
struts.convention.action.mapAllMatches |
false |
Create action mappings, even if no @Action is found |
struts.convention.action.checkImplementsAction |
true |
Check if an action implements com.opensymphony.xwork2.Action to create an action mapping |
struts.convention.default.parent.package |
convention-default |
Default parent package for action mapping |
struts.convention.action.name.lowercase |
true |
Convert action name to lowercase |
struts.convention.action.name.separator |
- |
Separator used to build the action name, MyAction -> my-action. This character is also used as the separator between the action name and the result in templates, like action-result.jsp |
struts.convention.package.locators |
action,actions,struts,struts2 |
Packages whose name end with one of these strings will be scanned for actions |
struts.convention.package.locators.disable |
false |
Disable the scanning of packages based on package locators |
struts.convention.exclude.packages |
org.apache.struts.*, |
Packages excluded from the action scanning |
struts.convention.package.locators.basePackage |
|
If set, only packages that start with its value will be scanned for actions |
struts.convention.relative.result.types |
dispatcher,velocity,freemarker |
The list of result types that can have locations that are relative and the result location (which is the resultPath plus the namespace) prepended to them |
struts.convention.redirect.to.slash |
true |
A boolean parameter that controls whether or not this will handle unknown actions in the same manner as Apache, Tomcat and other web servers. This handling will send back a redirect for URLs such as /foo to /foo/ if there doesn't exist an action that responds to /foo |
Children Show Children