菜鸟学Struts2——零配置(Convention )
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了菜鸟学Struts2——零配置(Convention )相关的知识,希望对你有一定的参考价值。
又是周末,继续Struts2的学习,之前学习了,Struts的原理,Actions以及Results,今天对对Struts的Convention Plugin进行学习,如下图:
Struts Convention支持零配置进行开发,也就是约定约定优于配置的方式。
(1)环境准备
使用Convention Plugin进行开发,需要引入struts2-convention-plugin,完整的pom.xml依赖如下:
1 <dependency> 2 <groupId>org.apache.struts</groupId> 3 <artifactId>struts2-core</artifactId> 4 <version>2.3.31</version> 5 </dependency> 6 <dependency> 7 <groupId>org.apache.struts</groupId> 8 <artifactId>struts2-convention-plugin</artifactId> 9 <version>2.3.31</version> 10 </dependency>
(2)Action约定
convention有自己定义的struts-plugin.xml如下:
1 <?xml version="1.0" encoding="UTF-8" ?> 2 <!DOCTYPE struts PUBLIC 3 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 4 "http://struts.apache.org/dtds/struts-2.3.dtd"> 5 6 <struts order="20"> 7 <bean type="com.opensymphony.xwork2.UnknownHandler" name="convention" class="org.apache.struts2.convention.ConventionUnknownHandler"/> 8 9 <bean type="org.apache.struts2.convention.ActionConfigBuilder" name="convention" class="org.apache.struts2.convention.PackageBasedActionConfigBuilder"/> 10 <bean type="org.apache.struts2.convention.ActionNameBuilder" name="convention" class="org.apache.struts2.convention.SEOActionNameBuilder"/> 11 <bean type="org.apache.struts2.convention.ResultMapBuilder" name="convention" class="org.apache.struts2.convention.DefaultResultMapBuilder"/> 12 <bean type="org.apache.struts2.convention.InterceptorMapBuilder" name="convention" class="org.apache.struts2.convention.DefaultInterceptorMapBuilder"/> 13 <bean type="org.apache.struts2.convention.ConventionsService" name="convention" class="org.apache.struts2.convention.ConventionsServiceImpl"/> 14 15 <bean type="com.opensymphony.xwork2.config.PackageProvider" name="convention.packageProvider" class="org.apache.struts2.convention.ClasspathPackageProvider"/> 16 <bean type="com.opensymphony.xwork2.config.PackageProvider" name="convention.containerProvider" class="org.apache.struts2.convention.ClasspathConfigurationProvider"/> 17 18 <constant name="struts.convention.actionConfigBuilder" value="convention"/> 19 <constant name="struts.convention.actionNameBuilder" value="convention"/> 20 <constant name="struts.convention.resultMapBuilder" value="convention"/> 21 <constant name="struts.convention.interceptorMapBuilder" value="convention"/> 22 <constant name="struts.convention.conventionsService" value="convention"/> 23 24 <constant name="struts.convention.result.path" value="/WEB-INF/content/"/> 25 <constant name="struts.convention.result.flatLayout" value="true"/> 26 <constant name="struts.convention.action.suffix" value="Action"/> 27 <constant name="struts.convention.action.disableScanning" value="false"/> 28 <constant name="struts.convention.action.mapAllMatches" value="false"/> 29 <constant name="struts.convention.action.checkImplementsAction" value="true"/> 30 <constant name="struts.convention.default.parent.package" value="convention-default"/> 31 <constant name="struts.convention.action.name.lowercase" value="true"/> 32 <constant name="struts.convention.action.name.separator" value="-"/> 33 <constant name="struts.convention.package.locators" value="action,actions,struts,struts2"/> 34 <constant name="struts.convention.package.locators.disable" value="false"/> 35 <constant name="struts.convention.package.locators.basePackage" value=""/> 36 <constant name="struts.convention.exclude.packages" value="org.apache.struts.*,org.apache.struts2.*,org.springframework.web.struts.*,org.springframework.web.struts2.*,org.hibernate.*"/> 37 <constant name="struts.convention.relative.result.types" value="dispatcher,velocity,freemarker"/> 38 <constant name="struts.convention.redirect.to.slash" value="true"/> 39 <constant name="struts.convention.action.alwaysMapExecute" value="true"/> 40 <constant name="struts.mapper.alwaysSelectFullNamespace" value="true"/> 41 <!-- <constant name="struts.convention.action.includeJars" /> --> 42 <constant name="struts.convention.action.fileProtocols" value="jar" /> 43 44 <constant name="struts.convention.classes.reload" value="false" /> 45 46 <constant name="struts.convention.exclude.parentClassLoader" value="true" /> 47 48 <package name="convention-default" extends="struts-default"> 49 </package> 50 </struts>
struts-plugin.xml是会被struts加载的,struts默认回加载"struts-default.xml,struts-plugin.xml,struts.xml";这些XML。这里需要关注的是上面的24行-36行。
Convention默认扫描的包含struts
, struts2
, action
或 actions的包,在这些包中实现了com.opensymphony.xwork2.Action或者是名字以Action结尾(上面struts-plugin.xml第26行的配置)的类型会被当作Action处理。下面这些类都会被当作Action处理:
1 com.example.actions.MainAction 2 com.example.actions.products.Display (implements com.opensymphony.xwork2.Action) 3 com.example.struts.company.details.ShowCompanyDetailsAction
而struts
, struts2
, action
或 actions下面的子包会被当作命名空间(namespace)
1 com.example.actions.MainAction -> / 2 com.example.actions.products.Display -> /products 3 com.example.struts.company.details.ShowCompanyDetailsAction -> /company/details
Action类的驼峰命名规则将被“-”分隔(上面struts-plugin.xml第32行配置)如下:
1 com.example.actions.MainAction -> /main 2 com.example.actions.products.Display -> /products/display 3 com.example.struts.company.details.ShowCompanyDetailsAction -> /company/details/show-company-details
下面是一个Action约定的例子(创建3个Action分别对应上面3种规则):
1 package yaolin.core.action; 2 /** 3 * /out.action 4 * @author yaolin 5 */ 6 public class OutAction { // 对应第一种规则 7 public String execute() { 8 return "success"; 9 } 10 }
1 package yaolin.core.action.ns; 2 /** 3 * ns子包名作为命名空间 4 * /ns/in.action 5 * @author yaolin 6 */ 7 public class InAction { // 对应第二种规则 8 9 public String execute() { 10 return "input"; 11 } 12 13 // /ns/in!play.action 14 public String play() { 15 return "play"; 16 } 17 }
1 package yaolin.core.action.ns; 2 /** 3 * ns子包名作为命名空间 4 * /ns/nil-oay.action 5 * @author yaolin 6 */ 7 public class NilOayAction { // 对应第三种规则 8 9 public String execute() { 10 return "input"; 11 } 12 }
创建struts.xml 开启“!”方法匹配(这里非必要,用于匹配play方法)
1 <!DOCTYPE struts PUBLIC 2 "-//Apache Software Foundation//DTD Struts Configuration 2.3//EN" 3 "http://struts.apache.org/dtds/struts-2.3.dtd"> 4 <struts> 5 <constant name="struts.enable.DynamicMethodInvocation" value="true" /> 6 </struts>
创建对应result的jsp页面(上面struts-plugin.xml第24行指定了jsp文件的根路径问“/WEB-INF/content”)
(2)Result约定
Result约定是在action名字之后加上“-result”,即如果InAction方法返回的是"input",那么jsp的名称为"in-input"(“in-input.jsp”),如果方法放回的是“success”则既可以匹配“in.jsp”也可以匹配“in-success.jsp”,如下表:
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 |
详见Struts源码,如下:
1 org.apache.struts2.convention.ConventionUnknownHandler#handleUnknownResult 2 public Result handleUnknownResult(ActionContext actionContext, String actionName,ActionConfig actionConfig, String resultCode) throws XWorkException { 3 4 PackageConfig pkg = configuration.getPackageConfig(actionConfig.getPackageName()); 5 String ns = pkg.getNamespace(); 6 String pathPrefix = determinePath(actionConfig, ns); 7 8 Result result = scanResultsByExtension(ns, actionName, pathPrefix, resultCode, actionContext); 9 10 if (result == null) { 11 // Try /idx/action/index.jsp 12 Map<String, ResultTypeConfig> resultsByExtension = conventionsService.getResultTypesByExtension(pkg); 13 for (String ext : resultsByExtension.keySet()) { 14 if (LOG.isTraceEnabled()) { 15 String fqan = ns + "/" + actionName; 16 LOG.trace("Checking for [#0/index.#1]", fqan, ext); 17 } 18 String path = string(pathPrefix, actionName, "/index", nameSeparator, resultCode, ".", ext); 19 result = findResult(path, resultCode, ext, actionContext, resultsByExtension); 20 if (result != null) { 21 break; 22 } 23 path = string(pathPrefix, actionName, "/index.", ext); 24 result = findResult(path, resultCode, ext, actionContext, resultsByExtension); 25 if (result != null) { 26 break; 27 } 28 } 29 } 30 31 if (result == null && resultCode != null) { 32 //try to find an action to chain to. If the source action is "foo" and 33 //the result is "bar", we will try to find an action called "foo-bar" 34 //in the same package 35 String chainedTo = actionName + nameSeparator + resultCode; 36 ActionConfig chainedToConfig = pkg.getActionConfigs().get(chainedTo); 37 if (chainedToConfig != null) { 38 if (LOG.isTraceEnabled()) { 39 LOG.trace("Action [#0] used as chain result for [#1] and result [#2]", chainedTo, actionName, resultCode); 40 } 41 ResultTypeConfig chainResultType = pkg.getAllResultTypeConfigs().get("chain"); 42 result = buildResult(chainedTo, resultCode, chainResultType, actionContext); 43 } 44 } 45 return result; 46 }
(3)chain约定
如果Action的方法中返回的result中没有对应的物理视图且这个result跟这个Action类中的某个方法名一致,那么Struts,会自动转化成chain类型,规则是当前action+"-"+result(这里的"-"是上面struts-plugin.xml第32行配置)。
从上面org.apache.struts2.convention.ConventionUnknownHandler#handleUnknownResult的源码中可以看出如果result没有匹配到且“index”和默认拓展etx都没有匹配到result,那么struts会进行chain处理,如果原action是“foo”、result是“bar“,则chain为”foo-bar“,注意这里action是"action",不是Action中的方法("!"后面的),看一下下面的例子:
1 package yaolin.core.action.ns; 2 3 import org.apache.struts2.convention.annotation.Action; 4 5 /** 6 * ns子包名作为命名空间 7 * /ns/nil-oay.action 8 * @author yaolin 9 */ 10 public class NilOayAction { 11 12 public String foo() { 13 return "bar"; 14 } 15 16 // chain to 17 @Action("nil-oay-bar") 18 public String bar() { 19 return "foo-bar"; 20 } 21 }
这里foo是NilOayAction的一个方法(非execute),则访问规则为/ns/nil-oay!foo.action,但是没有为其指定"nil-oay-bar.jsp"物理视图,而NilOayAction类中有跟result一样名字的bar函数。那么Struts会将其转化为Chain,上一个action名称,即"nil-oay"(这里不是foo,foo是方法名,不是action),再加入result,即"bar",组成"nil-oay-bar",这样会访问上面的bar()方法,bar()方法@Action("nil-oay-bar"),这时返回result="foo-bar",对应的物理视图为"/WEB-INF/content/nil-oay-bar-foo-bar.jsp",如果找不到,则会直接找"/WEB-INF/content/foo-bar.jsp"。
(4)注解
(a)Action 注解:
Annotation |
Description |
---|---|
Group of @Action annotations, maps multiple URLs to the same action |
|
Defines the URL of an action |
|
Gropup of @InterceptorRef annotations |
|
Interceptor, or interceptor stack to be applied to at action |
|
Group of @Result annotations |
|
Defines a result for an action |
|
Set the path of the action URL (used to overwrite the default) |
|
Set where the results are located (used to overwrite the default) |
|
Set the parent package of the actions (used to overwrite the default) |
|
Group of @ExceptionMapping annotations |
|
Defines an exception mapping |
(b)Workflow 注解
Annotation |
Description |
---|---|
Defines what method to execute, or result to be returned if there are validation errors |
(c) Interceptor 注解
Annotation |
Description |
---|---|
Marks a action method that needs to be executed after the result. |
|
Marks a action method that needs to be executed before the main action method. |
|
Marks a action method that needs to be executed before the result. |
(d) Validation 注解
Annotation |
Description |
---|---|
Checks if there are any conversion errors for a field. |
|
Checks that a date field has a value within a specified range. |
|
Checks that a double field has a value within a specified range. |
|
Checks that a field is a valid e-mail address. |
|
Validates an expression. |
|
Uses an OGNL expression to perform its validator. |
|
Checks that a numeric field has a value within a specified range. |
|
Validates a regular expression for a field. |
|
Checks that a field is non-null. |
|
Checks that a String field is not empty. |
|
Checks that a String field is of the right length. |
|
Checks that a field is a valid URL. |
|
Marker annotation for validation at Type level. |
|
Used to group validation annotations. |
|
Invokes the validation for a property‘s object type. |
|
Use this annotation for your custom validator types. |
(e)Type Convention 注解
Annotation |
Description |
---|---|
Marker annotation for type conversions at Type level. |
|
For Collection and Map types: Create the types within the Collection or Map, if null. |
|
For Generic types: Specify the element type for Collection types and Map values. |
|
For Generic types: Specify the key type for Map keys. |
|
For Generic types: Specify the key property name value. |
|
Used for class and application wide conversion rules. |
接下来将用注解进行开发。
未完,待续。
以上是关于菜鸟学Struts2——零配置(Convention )的主要内容,如果未能解决你的问题,请参考以下文章
Struts2 Convention Plugin ( struts2 零配置 )