如果你对java反射机制很熟悉,那么你会很容易理解这个框架,它实际上只是做如下四件事:
- 依据参数配置来构造方法的参数值
- 在配置的对象上调用方法
- 保存方法的结果
- 处理URL转发
对象源是框架的核心接口,用于获取和保存对象。 你使用此框架要做的大部分工作就是告诉框架如何从对象源中取得参数值,以及如何保存方法的结果。
可执行对象是框架的另一核心接口, 它仅依赖对象源就可以自我执行。它在框架中有两个实现,一是调用、一是动作, 它们都是对方法(java.lang.reflect.Method)的高级封装,一个动作可以包含多个调用,一个调用对应一个方法。
解决对象是框架的一个重要概念,方法的调用目标即是它。 它在框架中并没有明确的接口体现,因为它完全是由框架使用者定义的,你可以把它理解为你应用中的业务对象。
下面从可配置项开始,来详细介绍框架。
-
<global-config>
全局配置块
-
<generic-converter>
通用转换器
- <converter> 辅助转换器
- <exception-handler> 异常处理器
-
<generic-converter>
通用转换器
-
<includes>
包含模块
- <file> 模块配置文件
-
<resolvers>
解决对象配置块
- <resolver> 解决对象
- <executables> 可执行对象配置块
全局配置块。它只是作为一个父标签,包含框架的一些全局配置项,本身没有任何属性。
通用转换器配置。框架的一个内置功能是支持类型转换,当对象源中保存的对象与要取得对象的类型不匹配时, 框架会尝试进行类型转换(参考通用转换器接口)。 比如,如果在配置文件中直接定义了调用方法参数的值,那么解析器会在解析时将这个字符串值转换为实际参数类型的对象。
特别是在WEB环境下,请求参数值都是字符串,而调用的方法参数可能是任何类型的,类型转换尤为重要。
它包含如下属性:
-
class(可选)
自定义通用转换器实现类,你可以配置它来完全替换框架的实现。 要注意的是,这个类必须实现通用转换器接口并提供默认无参构造方法。
如果你不配置这个属性,框架将使用默认的WEB通用转换器。
辅助转换器配置。你可以配置多个辅助转换器,来为通用转换器添加更多的类型转换支持,或者替换旧的辅助转换器(后添加的将替换掉先前添加的)。
默认的WEB通用转换器已经添加了大部分常用的辅助转换器(参考DefaultGenericConverter类说明),
它还支持将“java.util.Map”转换为JavaBean对象。
它包含如下属性:
-
src(必须)
转换器支持转换的源类型 -
dest(必须)
转换器支持转换的目标类型 -
class(必须)
转换器类
下面是一个自定义辅助日期转换器配置,框架内置的将被替换:
<generic-converter> <converter src="java.lang.String" dest="java.util.Date" class="my.converter.DateConverter" /> </generic-converter>
异常处理器配置。当框架出现执行异常时,会将异常转交给这个异常处理器。
可能的源异常都定义在ExceptionType类中,
一般你只需要处理INVOCATION异常。
如果你不配置异常处理器,执行异常将会一直向上抛出。
它包含如下属性:
-
executable-name(必须)
处理异常的可执行对象名称,你应该在之后定义该名称的可执行对象 -
exception-arg-key(必须)
执行异常对象的存储关键字。它告诉框架应该如何存储执行异常,以便之后的异常处理可执行对象能根据这个关键字取得它。
下面是一个异常处理器配置示例,当出现执行异常时,框架将执行动作“/handleException.do”:
<global-config> <exception-handler executable-name="/handleException.do" exception-arg-key="session.executeExceptoin" /> </global-config> <executables> <action name="/handleException.do"> <invoke method="handleException" resolver="exceptionResolver"> <arg key="session.executeExceptoin" /> </invoke> <target url="/error.jsp" /> </action> ... ... </executables>
包含模块。它只是作为一个父标签,本身没有任何属性。
模块配置文件。这个配置文件可以是类路径的资源文件,也可以是应用“/WEB-INF”下的文件。
注意,模块配置文件中只有<resolvers>和<executables>才是有效的。
下面是一个示例:
<includes> <file>my/config/module.1.xml</file> <file>my/config/module.2.xml</file> <file>/WEB-INF/config/module.3.xml</file> <file>/WEB-INF/config/module.4.xml</file> <file>/home/myapp/config/module.5.xml</file> </includes>
解决对象配置块。它只是作为一个父标签,没有任何属性。
解决对象配置。你可以配置多个解决对象,以供后面的可执行对象使用。
它包含如下属性:
-
id(必须)
解决对象ID,后面的可执行对象通过这个ID来引用它 -
class(必须)
解决对象类名,配置解析器会在解析时创建它的单一实例,供所有与之相关的可执行对象使用
下面是一个示例:
<resolvers> <resolver id="myResolver0" class="my.resolver.MyResolver0" /> <resolver id="myResolver1" class="my.resolver.MyResolver0" /> <resolver id="myResolver2" class="my.resolver.MyResolver1" /> </resolvers>
可执行对象配置块。它只是作为一个父标签,没有任何属性。
调用配置。它可以直接添加在<executables>标签下(全局的),也可以添加到<action>标签下(局部的)。
它包含如下属性:
-
name(可选)
定义调用的名称,只有当它是全局调用时,该属性才需要被配置 。 -
method(必须)
定义调用的方法名称,这个方法必须在resolver-class或者resolver类中存在。 配置解析器会在解析时根据这个名称查找对应的方法对象,如果你定义了resolver-class属性,解析器会优先使用它来查找; 否则,使用resolver定义的解决对象来查找。 -
resolver-class(不定)
定义解决对象类名,如果方法是类方法,你仅需要定义这个属性即可,而不需定义下面的resolver属性, 因为类方法执行时不需要解决对象实例。 -
resolver(不定)
定义解决对象引用ID,这个ID可以是该文件域内的解决对象ID,也可以是任何包含模块文件中的解决对象ID。 如果该调用方法不是类方法,这个属性必须被定义。 -
result-key(可选)
调用方法结果在对象源中中的保存关键字。无论方法是否有返回结果,只要你定义了result-key, 框架总会执行保存操作,只不过无返回结果的话是以null值保存的;反之,则不会保存。 如果这个结果关键字里面包含访问符'.',那么它必须以框架支持的作用域开头(参考WebConstants.Scope类说明)。
下面是合法的结果关键字:-
myResultKey
结果将以“myResultKey”关键字被保存到“request”作用域中 -
request.myResultKey
同上 -
session.myResultKey
结果将以“myResultKey”关键字被保存到“session”作用域中 -
application.myResultKey
结果将以“myResultKey”关键字被保存到“application”作用域中
-
myResultKey
调用方法的参数配置。<arg>的个数是由调用方法决定的, 方法有几个参数,你就需要配置几个<arg>, 如果方法没有参数,你就不必要配置它。
它包含如下属性:
-
key(不定)
定义调用方法的参数关键字,框架将根据该关键字从对象源取得参数值。 如果这个关键字里面包含访问符'.',那么它必须以框架支持的作用域开头(参考WebConstants.Scope类说明)。
下面是合法的参数关键字:-
param
整个请求参数映射表。如果调用方法参数是java.util.Map类型, 那么这个请求参数映射表会原封不动地传递给方法;如果是其他类型,框架会首先将此映射表转换为这个类型的对象,然后传递给方法。 -
myArgKey
请求参数映射表中以“myArgKey”开头的请求参数。 如果这个参数有明确的值,框架会将这个值进行类型转换(需要的话)后传递给方法;否则,就根据“myArgKey”来对参数映射表进行过滤, 产生一个新的映射表(它的关键字是原始关键字“myArgKey.”之后的部分,比如由“beanName.propertyName”变为“propertyName”), 然后,与上面提到的一样,根据调用方法参数的类型直接传递这个新映射表或者传递转换后的对象。 -
param.myArgKey
同上。 -
request
请求HttpServletRequest对象。框架本身并没有提供它的转换器,如果调用方法的参数类型不是“HttpServletRequest”, 那么你需要自定义“javax.servlet.http.HttpServletRequest”到参数类型的辅助转换器。 -
request.myArgKey
请求属性中的“myArgKey”关键字对应的对象。框架不会对此对象执行类型转换,调用方法的参数类型应该与这个关键字对应的对象一致。 -
session
会话HttpSession对象。框架本身并没有提供它的转换器,如果调用方法的参数类型不是“HttpSession”, 那么你需要自定义“javax.servlet.http.HttpSession”到参数类型的辅助转换器。 -
session.myArgKey
会话属性中的“myArgKey”关键字对应的对象。框架不会对此对象执行类型转换。 -
application
应用ServletContext对象。如果调用方法的参数类型不是“ServletContext”, 那么你需要自定义“javax.servlet.ServletContext”到参数类型的辅助转换器。 -
application.myArgKey
应用属性中的“myArgKey”关键字对应的对象。框架不会对此对象执行类型转换。 -
response
回应HttpServletResponse对象。如果调用方法的参数类型不是“HttpServletResponse”, 那么你需要自定义“javax.servlet.http.HttpServletResponse”到参数类型的辅助转换器。
-
param
-
value(不定)
直接定义参数的值,解析器会在解析时将这个字符串值转换为实际的参数类型对象(只支持基本类型)。 如果你指定了这个参数值,则key是不需要的,框架会忽略它。
下面是一些配置示例:
假设解决对象calculator的方法定义为:
public int multiply(int a, int b);
那么这个配置:
<invoke name="/multiply.do" method="multiply" resolver="calculator" result-key="multiplyResult"> <arg key="a" /> <arg key="param.b" /> </invoke>即是,从请求参数中取得a参数的值作为方法的第一个参数,取得b参数的值作为方法的第二个参数, 并将方法的执行结果以multiplyResult关键字保存到请求对象的属性中。
而对于方法:
public void doMultiply(HttpServletRequest request, HttpServletResponse response);应该这样配置:
<invoke name="/doMultiply.do" method="doMultiply" resolver="calculator"> <arg key="request" /> <arg key="response" /> </invoke>
动作配置。你可以为其配置多个<invoke>和<ref>子元素, 它们将被顺序地执行。
它包含如下属性:
-
name(必须)
定义动作的名称。
下面是一个动作示例:
<action name="/myAction.do"> <invoke method="method0" resolver="myResolver" result-key="result0" /> <invoke method="method1" resolver="myResolver"> <arg key="param.a" /> <arg key="request.result0" /> <arg value="true" /> </invoke> <ref name="gloablInvoke0" /> <ref name="gloablInvoke1" /> <target url="/action_result.jsp" /> </action>
引用配置。你可以将多个全局可执行对象添加到这个动作中。
它包含如下属性:
-
name(必须)
要引用的全局可执行对象名。
动作的目标配置。你可以使用它定义动作的转发URL。
它包含如下属性:
-
url(必须)
目标URL,如果是应用内的URL,则必须以“/”开头。 -
type(可选)
类型,它仅有两个可用的值:forward和redirect。 如果你没有定义,框架将默认按照forward来处理目标。