Terry.Li-彬

虚其心,可解天下之问;专其心,可治天下之学;静其心,可悟天下之理;恒其心,可成天下之业。

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  143 随笔 :: 344 文章 :: 130 评论 :: 0 Trackbacks

 

Convention Plugin

原文:http://cwiki.apache.org/WW/convention-plugin.html

翻译:石太祥ealpha@gmail.com   http://www.lalfa.com

·                                 Introduction

·                                 Setup

·                                 Converting a Codebehind based application to Convention

·                                 Hello world

·                                 Code behind hello world

·                                 Results and result codes

·                                 Chaining

·                                 XWork packages

·                                 Annotation reference

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

·                                 13 Configuration reference

Introduction

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即可使用ConventionConvention的某些约束习惯可以通过配置属性来控制,您也可以在类中覆写其中的方法来达到扩展目地。

安装

使用Convention插件,你需要将其JAR文件放到你应用的WEB-INF/lib目录中,你也可以在你Macen项目的POM文件中添加下面包依赖

<dependency>

 <groupId>org.apache.struts</groupId>

 <artifactId>struts2-convention-plugin</artifactId>

 <version>2.1.6</version>

</dependency>

转换基于Codebehind项目到Convention

跳转到此页面,查看需要修改的变化和小提示

如果你想在你系统中结合Convention插件使用REST。需要在你项目的struts.xml中添加如下配置

<constant name="struts.convention.action.suffix" value="Controller"/>

<constant name="struts.convention.action.mapAllMatches" value="true"/>

<constant name="struts.convention.default.parent.package" value="rest-default"/>

Hello world

到目前为止,你已经在你项目中添加了Convention插件支持,首先我们从一个非常简单的例子开始入手。本例中,我们将演示根据访问URL来访问固定的Action,默认情况下,Convention会默认所有的结果页面都存储在WEB-INF/content下,你也可以在strutsproperties文件中设定struts.convention.result.path的值到一个新的路径。路径最后“/”是不必要的,Convention会自动进行处理。以下是本例的JSP文件

WEB-INF/content/hello-world.jsp

<html>

<body>

Hello world!

</body>

</html>

启动Tomcat或其他你所使用的JEE容器,在浏览器访问http://localhost:8080/hello-world 你可看到以下信息:

WEB-INF/content/hello-world.jsp

Hello world!

这表明,Convention已经能正常运行,并找到了结果。即使在没有action存在情况下,convention也会根据URL规则来找到结果页面。

Code behind hello world

我们继续扩展本例并添加代码实现类。为了实现本功能,首先需要Convention能正确找到我们的Action 类,默认情况下,Convention会找到com.opensymphony.xwork2.Action 的实现类,或制定包中以Action结尾的类 action

Convention使用以下方法来搜索类路径,首先,Convention会从根package中寻找包名含有strutsstruts2action 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地址,URLpackage的名字以及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 HelloWorld extends 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>

The message is ${message}

</body>

</html>

启动应用服务器,在浏览器访问 http://localhost:8080/hello-world地址,我们看到如下结果界面:

Result

The message is Hello World!

Results and result codes

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 HelloWorld extends 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>

The error 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 ,他们俩将被链接到一起,如果在第一个action代码中未定义result,如下代码:

com.example.actions.HelloWorld

package com.example.actions;

import com.opensymphony.xwork2.Action;

import com.opensymphony.xwork2.ActionSupport;

public class HelloAction extends ActionSupport {

    @Action("foo")

    publicString foo() {

        return"bar";

    }

    @Action("foo-bar")

    publicString bar() {

        return SUCCESS;

    }

}

 “foo” action执行时候,由于找不到结果,convention尝试在同一个包下寻找action名为”foo-bar”action。如果找到这样的actionconvention将会调用并返回相关联的result

XWork packages

为了避免冲突,可将action放在一个自定义XWORKpackage下。package命名由action所在的Java,action对应的URLnamespace部分以及actionparent XWork package三个部分组成。parent XWork package 值在属性 struts.convention.default.parent.package 中指定(默认为conventionDefault),package的属性值须继承于 strutsDefault

因此,Convention插件中XWORK packages 采用如下命名规则:

XWork package naming

<java-package>#<namespace>#<parent-package>

Using our example from above, the XWork package for our action would be:

上例中,action对应的 XWORK package如下:

XWork package naming

com.example.actions#/#conventionDefault

Annotation 参考

Convention使用某些注解语句来覆写插件默认的actionurl的映射和自动搜索渲染到的页面。此外,你还可以修改action配置文件中定义的父XWORK的包信息

Action annotation

Convention 插件可以使用Action注解语句来修改action返回的URL地址。本注解同时也允许包含在Actions语句中,用来使一个action对应于多个URL。在action 方法中使用本注解语句,可以参考以下代码

com.example.actions.HelloWorld

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport;

import org.apache.struts2.convention.annotation.Action;

public class HelloWorld extends ActionSupport {

 @Action("/different/url")

 publicString execute() {

    return SUCCESS;

 }

}

现在我们action类中将使用 /different/url 来替代默认的 /hello-world,如果未指定@Result(参考下节),result的路径将会使用actionnamespace,上面的例子中将会返回一下路径 "/WEB-INF/content/different/url.jsp"

Action类中的单个方法可以使用Actions注解来映射多个地址。

com.example.actions.HelloWorld

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport;

import org.apache.struts2.convention.annotation.Action;

import org.apache.struts2.convention.annotation.Actions;

public class HelloWorld extends 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;

import org.apache.struts2.convention.annotation.Action;

import org.apache.struts2.convention.annotation.Actions;

public class HelloWorld extends 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;

import org.apache.struts2.convention.annotation.Action;

import org.apache.struts2.convention.annotation.Actions;

public class HelloWorld extends 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;

import org.apache.struts2.convention.annotation.Actions;

public class HelloWorld extends ActionSupport {

 @Action(interceptorRefs=@InterceptorRef(value="validation",params={"programmatic", "false", "declarative", "true}))

 publicString execute() {

    return SUCCESS;

 }

 @Action("url")

 publicString doSomething() {

    return SUCCESS;

 }

}

如果未指定interceptors,将会使用默认的。

InterceptorRef annotation

Interceptors 可以在方法级进行指定,使用Action 注解或在类上使用InterceptorRefs注解。Class级别的拦截会被应用到类包含的所有action上。可以参考下面例子:

com.example.actions.HelloWorld

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport;

import org.apache.struts2.convention.annotation.Action;

import org.apache.struts2.convention.annotation.Actions;

@InterceptorRefs({

    @InterceptorRef("interceptor-1"),

    @InterceptorRef("defaultStack")

})

public class HelloWorld extends 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”生效

Result annotation

Convention 允许action类为每个action定义不同的resultsresults分为两类,全局的(global)和本地的(local),全局results可以被action类中所有的action分享,这种resultsaction类上使用注解进行声明。本地results只能在action方法上进行声明。下面是两种results注解的例子:

com.example.actions.HelloWorld

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport;

import org.apache.struts2.convention.annotation.Action;

import org.apache.struts2.convention.annotation.Actions;

import org.apache.struts2.convention.annotation.Result;

import org.apache.struts2.convention.annotation.Results;

@Results({

 @Result(name="failure", location="fail.jsp")

})

public class HelloWorld extends 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;

import org.apache.struts2.convention.annotation.Actions;

import org.apache.struts2.convention.annotation.Result;

import org.apache.struts2.convention.annotation.Results;

public class HelloWorld extends 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 annotation

namespace注解允许action使用指定的路径替代默认的以package包名作为路径。本注解可以在action类或Java 包中的package-info.java类中进行设置。设置在action类中的namespace注解,对本action类中所有的action都有效,这是不完全合乎规范的action URL处理地址。设置在package-info.java中的namespace注解,将会改变本java包下所有的action的默认namespace。下面是此注解的例子:

com.example.actions.HelloWorld

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport;

import org.apache.struts2.convention.annotation.Action;

import org.apache.struts2.convention.annotation.Namespace;

@Namespace("/custom")

public class HelloWorld extends 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 annotation

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;

import org.apache.struts2.convention.annotation.ResultPath;

@ResultPath("/WEB-INF/jsps")

public class HelloWorld extends ActionSupport {

 publicString execute() {

    return SUCCESS;

 }

}

上面的result将以 WEB-INF/jsps替换默认的 WEB-INF/content

ParentPackage annotation

ParentPackage注解用来定义具体action类的父XWork包或java包,下面例子演示了在action类上使用本注解:

com.example.actions.HelloWorld

package com.example.actions;

import com.opensymphony.xwork2.ActionSupport;

import org.apache.struts2.convention.annotation.Action;

import org.apache.struts2.convention.annotation.ParentPackage;

@ParentPackage("customXWorkPackage")

public class HelloWorld extends ActionSupport {

 publicString execute() {

    return SUCCESS;

 }

}

ExceptionMapping Annotation

ExceptionMapping 注解用来影射action抛出的异常。可以参考exception mapping documentation 获得详细信息。注解用类级别,在这种情况下,注解会应用到类里面的所有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;

    }

}

Actions in jar files

默认情况下,Convention 插件不会从jar文件中寻找action。如果想实现这一功能,jar文件必须被struts.convention.action.includeJars所定义的正则匹配到。在例子中 myjar1.jar myjar2.jar 将被插件检测到:

<constant name="struts.convention.action.includeJars" value=".*/myjar1.*?jar(!/)?,.*/myjar2*?jar(!/)?"

提示:正则表达式只针对jar文件的路径进行匹配,而不是文件名。jarURL应该包含jar文件的路径并以"!/"结尾。

Automatic configuration reloading

Convention插件可以自动重新加载配置的功能,无需重启容器,就可以刷新类中包含的action。这自动加载automatic xml 配置文件类似。你必须在struts.xml 中添加以下代码来启用本功能:

<constant name="struts.devMode" value="true"/>

<constant name="struts.convention.classes.reload" value="true" />

此功能没有在所有容器中进行过测试,强力建议不要在生产环境中使用。

Troubleshooting

Tips

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

Common Errors

  1. 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.
  2. 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.
  3. 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.

Configuration reference

Add a constant element to your struts config file to change the value of a configuration setting, like:

<constant name="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.*,
org.apache.struts2.*,
org.springframework.web.struts.*,
org.springframework.web.struts2.*,
org.hibernate.*

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

posted on 2009-02-24 22:48 礼物 阅读(6502) 评论(0)  编辑  收藏 所属分类: struts2.0