Convention Plugin(转http:www.blogjava.netlibin2722articles256525.html)

    技术2025-12-18  10

    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中寻找包名含有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地址,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

    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.

    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

    最新回复(0)