- 浏览: 417000 次
文章分类
最新评论
-
xieke:
就一句话?内容呢?
用RMI实现基于Java的分布式计算 -
grandboy:
搞成功了,但是生成的代码不好。我不想用这个生成了,还是用mid ...
用PowerDesigner生成Hibernate映射文件的步骤 -
rmn190:
麻烦你帮我看下这个关于struts-menu方面的问题吧: h ...
Struts-menu源码分析 -
wcily123:
按照你的这个搞了半天都没有成功!
用PowerDesigner生成Hibernate映射文件的步骤 -
beijixuem:
骗人的家伙!
JAVA + Oracle存储过程返回查询结果集
Struts源码研究 - Action-Input属性篇
初学Struts,写了一个很简单的应用,主要功能和页面如下:
1、首页显示一个“添加新用户”的链接,点击该链接出发一个forward动作,页面导向到添加用户的jsp页面
2、添加用户的jsp页面中,可供用户输入“用户名”和“用户描述”两项
3、用户输入完毕,将做输入数据合法性检查,检查通过,将输入信息保存进入文件(使用了Properties类),然后返回首页;检查失败返回添加用户页面
4、数据合法性检查分成两块,第一部分检查条件使用Struts的Validator,检查条件配置在Validator.xml中;第二部分检查放在ActionForm中,
检查失败将错误信息置入ActionErrors中,然后返回到添加用户的页面并显示错误信息。
JSP页面、ActionForm和Action类的代码书写都参照了struts-example应用,所以这里代码不再列举,请看附件中的代码包
这里值得一提的是,在开发过程中,碰到了一个小问题,正是由于该问题,才导致查看Struts源码,刨根问底的查找错误原因的过程
该错误发生在Struts的配置文件中,首先将错误的配置文件列出如下:
====================================================
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<!-- ======================================== Form Bean Definitions -->
<form-beans>
<form-bean
name="CreateUserForm"
type="com.zchome.CreateUserForm"/>
</form-beans>
<!-- ================================= Global Exception Definitions -->
<global-exceptions>
</global-exceptions>
<!-- =================================== Global Forward Definitions -->
<global-forwards>
<!-- Default forward to "Welcome" action -->
<!-- Demonstrates using index.jsp to forward -->
<forward name="welcome" path="/Welcome.do"/>
</global-forwards>
<!-- =================================== Action Mapping Definitions -->
<action-mappings>
<!-- Default "Welcome" action -->
<!-- Forwards to Welcome.jsp -->
<action
path="/Welcome"
type="org.apache.struts.actions.ForwardAction"
parameter="/jsp/Welcome.jsp"/>
<action path="/createuserpage" forward="/jsp/createuser.jsp">
</action>
<action
path="/docreateuser"
type="com.zchome.CreateUserAction"
name="CreateUserForm"
scope="request"
input="createuser">
<forward name="createusersuccess" path="/jsp/Welcome.jsp"/>
<forward name="createuser" path="/jsp/createuser.jsp"/>
</action>
</action-mappings>
<!-- ===================================== Controller Configuration -->
<controller>
<set-property property="processorClass" value="org.apache.struts.tiles.TilesRequestProcessor"/>
</controller>
<!-- ================================ Message Resources Definitions -->
<message-resources parameter="resources.application"/>
<!-- ======================================= Plug Ins Configuration -->
<!-- ========== Tiles plugin =================== -->
<!-- -->
<!--
This plugin initialize Tiles definition factory. This later can takes some
parameters explained here after. The plugin first read parameters from web.xml, then
overload them with parameters defined here. All parameters are optional.
The plugin should be declared in each struts-config file.
- definitions-config: (optional)
Specify configuration file names. There can be several comma
separated file names (default: ?? )
- moduleAware: (optional - struts1.1)
Specify if the Tiles definition factory is module aware. If true (default),
there will be one factory for each Struts module.
If false, there will be one common factory for all module. In this later case,
it is still needed to declare one plugin per module. The factory will be
initialized with parameters found in the first initialized plugin (generally the
one associated with the default module).
true : One factory per module. (default)
false : one single shared factory for all modules
- definitions-parser-validate: (optional)
Specify if xml parser should validate the Tiles configuration file.
true : validate. DTD should be specified in file header. (default)
false : no validation
Paths found in Tiles definitions are relative to the main context.
-->
<!-- comment following if struts1.0.x -->
<plug-in className="org.apache.struts.tiles.TilesPlugin" >
<set-property property="definitions-config"
value="/WEB-INF/tiles-defs.xml" />
<set-property property="moduleAware" value="true" />
<set-property property="definitions-parser-validate" value="true" />
</plug-in>
<!-- end comment if struts1.0.x -->
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in>
</struts-config>
====================================================
首先描述一下系统的出错背景:
1、从首页点击链接来到添加用户的页面 正常
2、在添加用户页面中输入Vlidator.xml文件中定义的错误数据,弹出Javascript对话框,提示出错 正常
3、在添加用户页面中输入合法数据,数据保存进入文件并重定向到首页 正常
4、在添加用户页面中输入ActionForm中定义的非法数据,系统应返回到添加用户的页面 出错!!!
OK,来着重看这个添加动作的定义,如下:
====================================================
<action
path="/docreateuser"
type="com.zchome.CreateUserAction"
name="CreateUserForm"
scope="request"
input="createuser">
<forward name="createusersuccess" path="/jsp/Welcome.jsp"/>
<forward name="createuser" path="/jsp/createuser.jsp"/>
</action>
====================================================
从以上的定义可以看出,如果Validate验证出错,Struts应该为我们重定向到input域所定义的uri,即/jsp/createuser.jsp
看起来应该没有问题,再来看看出错信息,如下:
====================================================
java.lang.IllegalArgumentException: Path createuser does not start with a "/" character
at org.apache.catalina.core.ApplicationContext.getRequestDispatcher(ApplicationContext.java:1179)
at org.apache.catalina.core.ApplicationContextFacade.getRequestDispatcher(ApplicationContextFacade.java:174)
at org.apache.struts.action.RequestProcessor.doForward(RequestProcessor.java:1062)
at org.apache.struts.tiles.TilesRequestProcessor.doForward(TilesRequestProcessor.java:274)
at org.apache.struts.action.RequestProcessor.internalModuleRelativeForward(RequestProcessor.java:1012)
at org.apache.struts.tiles.TilesRequestProcessor.internalModuleRelativeForward(TilesRequestProcessor.java:345)
at org.apache.struts.action.RequestProcessor.processValidate(RequestProcessor.java:980)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:255)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:525)
。。。以下省略。。。
====================================================
出错信息清楚的说明,“createuser”这个path应该以“/”字符开头
为定位这个错误,从以上错误信息,开始打开Struts的源码RequestProcessor.java进行研究,首先来到这一段:
====================================================
public class RequestProcessor {
。。。。。。
protected boolean processValidate(HttpServletRequest request,
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
if (form == null) {
return (true);
}
// Was this request cancelled?
if (request.getAttribute(Globals.CANCEL_KEY) != null) {
if (log.isDebugEnabled()) {
log.debug(" Cancelled transaction, skipping validation");
}
return (true);
}
// Has validation been turned off for this mapping?
if (!mapping.getValidate()) {
return (true);
}
// Call the form bean's validation method
if (log.isDebugEnabled()) {
log.debug(" Validating input form properties");
}
ActionMessages errors = form.validate(mapping, request);
if ((errors == null) || errors.isEmpty()) {
if (log.isTraceEnabled()) {
log.trace(" No errors detected, accepting input");
}
return (true);
}
// Special handling for multipart request
if (form.getMultipartRequestHandler() != null) {
if (log.isTraceEnabled()) {
log.trace(" Rolling back multipart request");
}
form.getMultipartRequestHandler().rollback();
}
// Has an input form been specified for this mapping?
String input = mapping.getInput();
if (input == null) {
if (log.isTraceEnabled()) {
log.trace(" Validation failed but no input form available");
}
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("noInput",
mapping.getPath()));
return (false);
}
// Save our error messages and return to the input form if possible
if (log.isDebugEnabled()) {
log.debug(" Validation failed, returning to '" + input + "'");
}
request.setAttribute(Globals.ERROR_KEY, errors);
if (moduleConfig.getControllerConfig().getInputForward()) {
ForwardConfig forward = mapping.findForward(input);
processForwardConfig( request, response, forward);
} else {
internalModuleRelativeForward(input, request, response);
}
return (false);
}
====================================================
在出错信息中,提到了internalModuleRelativeForward这个方法,所以着重看以上代码的最后几行,可以看到,如果
moduleConfig.getControllerConfig().getInputForward()这个方法返回了false,那么internalModuleRelativeForward
这个方法将被调用。inputForward是什么?ModuleConfig是管理所有配置信息的一个manager类,那么moduleConfig.getControllerConfig()
这个方法返回的肯定是ControllerConfig这个类的一个实例,那么inputForward肯定是ControllerConfig类的一个成员变量了
再看看struts-config.xml,里面有<controller>这个标签,初步猜测ControllerConfig应该是读取这个标签的一个配置类
而<controller>这个标签应该定义了ActionServlet作为Controller的一些行为!
OK,再来看ControllerConfig这个类中有关inputForward这个成员变量的一些代码,如下:
====================================================
/**
* <p>Should the <code>input</code> property of {@link ActionConfig}
* instances associated with this module be treated as the
* name of a corresponding {@link ForwardConfig}. A <code>false</code>
* value treats them as a module-relative path (consistent
* with the hard coded behavior of earlier versions of Struts.</p>
*
* @since Struts 1.1
*/
protected boolean inputForward = false;
public boolean getInputForward() {
return (this.inputForward);
}
public void setInputForward(boolean inputForward) {
this.inputForward = inputForward;
}
====================================================
开始有点明白了,原来inputForward这个属性默认值是false,那么由于没有配置这个属性,那么上述的那个方法
moduleConfig.getControllerConfig().getInputForward()自然就返回false了,Bingo!
那么重点就转移到了internalModuleRelativeForward这个方法了,看这个方法的源代码,如下:
====================================================
protected void internalModuleRelativeForward(
String uri,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Construct a request dispatcher for the specified path
uri = moduleConfig.getPrefix() + uri;
// Delegate the processing of this request
// FIXME - exception handling?
if (log.isDebugEnabled()) {
log.debug(" Delegating via forward to '" + uri + "'");
}
doForward(uri, request, response);
}
protected void doForward(
String uri,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Unwrap the multipart request, if there is one.
if (request instanceof MultipartRequestWrapper) {
request = ((MultipartRequestWrapper) request).getRequest();
}
RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
if (rd == null) {
response.sendError(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("requestDispatcher", uri));
return;
}
rd.forward(request, response);
}
====================================================
从上可以看到,这个方法是将uri = moduleConfig.getPrefix() + uri;这个东东传给了doForward方法
而doForward这个方法又调用了javax.servlet.ServletContext的方法getRequestDispatcher这个方法
既然出错信息中是路径出了问题,那么看来这个参数uri非常的重要,极有可能就是这个uri发生了错误导致了出错
OK,开始剖析这个uri,从头开始看,这个uri是这样被赋值的:
uri = moduleConfig.getPrefix() + uri
1、moduleConfig.getPrefix()这个方法返回的应该是""
(这个请看ActionServlet的Init方法,如果在web.xml文件中定义ActionServlet的时候,给定了一些init-params,那么这个prefix就有可能
不为空,这里不再列举了)
2、代码右边的这个uri是从processValidate这个方法中定义的input,如下:
String input = mapping.getInput();
这个input应该是struts-config.xml文件中定义的那个action的input,也就是“createuser”,如果Struts将其做了进一步的解析,那么这个
input应该进一步被转化成为“/jsp/createuser.jsp”
好,到此为止,可以看到,这个uri不是“createuser”,那就是“/jsp/createuser.jsp”,再来看getRequestDispatcher这个方法的定义,
翻开Servlet的API文档,可以看到如下一段话:
====================================================
public RequestDispatcher
getRequestDispatcher(java.lang.String path)Returns a RequestDispatcher object that acts as a wrapper
for the resource located at the given path. A RequestDispatcher object can be used to forward a request
to the resource or to include the resource in a response. The resource can be dynamic or static.
The pathname must begin with a "/" and is interpreted as relative to the current context root.
Use getContext to obtain a RequestDispatcher for resources in foreign contexts. This method returns null
if the ServletContext cannot return a RequestDispatcher.
====================================================
终于有拨云见日的感觉了,因为这段话和出错信息实在是太一致了!由上面这段话,我们可以断定,uri这个变量的值
肯定是“createuser”,而不是我们所希望的“/jsp/createuser.jsp”。为什么会这样呢?显然是struts-config.xml中配置
有些还是不对,或是缺了点什么。想到这里,很自然的就联想到上面所提到的InputForward这个配置项了,因为从字面意思上
看来,这个配置项的用处就应该是将input的值解析成forward中对应的值,而且在ControllerConfig中,这个变量默认值是
false,所以猜测将其改成true是不是就可以了呢?
为了寻找答案,再次翻开struts-example(因为这个例子中的action也定义了input),终于找到了答案,和之前猜测的果然
十分吻合,如下:
====================================================
<controller>
<set-property property="inputForward" value="true"/>
</controller>
====================================================
至此,问题解决,正确的action配置可以是如下两种:
====================================================
1、不使用inputForward
<action
path="/docreateuser"
type="com.zchome.CreateUserAction"
name="CreateUserForm"
scope="request"
input="/jsp/createuser.jsp">
<forward name="createusersuccess" path="/jsp/Welcome.jsp"/>
</action>
2、使用InputForward
<action
path="/docreateuser"
type="com.zchome.CreateUserAction"
name="CreateUserForm"
scope="request"
input="createuser">
<forward name="createusersuccess" path="/jsp/Welcome.jsp"/>
<forward name="createuser" path="/jsp/createuser.jsp"/>
</action>
<controller>
<set-property property="inputForward" value="true"/>
<set-property property="processorClass" value="org.apache.struts.tiles.TilesRequestProcessor"/>
</controller>
====================================================
而且,从问题的定位过程中,还学到了一招,就是javax.servlet.RequestDispatcher:
RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
if (rd == null) {
response.sendError(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("requestDispatcher", uri));
return;
}
rd.forward(request, response);
以后再做页面重定向,只要给定相对的uri就可以了,再也不用写上一层的虚拟目录名或自己拼URL了
1、首页显示一个“添加新用户”的链接,点击该链接出发一个forward动作,页面导向到添加用户的jsp页面
2、添加用户的jsp页面中,可供用户输入“用户名”和“用户描述”两项
3、用户输入完毕,将做输入数据合法性检查,检查通过,将输入信息保存进入文件(使用了Properties类),然后返回首页;检查失败返回添加用户页面
4、数据合法性检查分成两块,第一部分检查条件使用Struts的Validator,检查条件配置在Validator.xml中;第二部分检查放在ActionForm中,
检查失败将错误信息置入ActionErrors中,然后返回到添加用户的页面并显示错误信息。
JSP页面、ActionForm和Action类的代码书写都参照了struts-example应用,所以这里代码不再列举,请看附件中的代码包
这里值得一提的是,在开发过程中,碰到了一个小问题,正是由于该问题,才导致查看Struts源码,刨根问底的查找错误原因的过程
该错误发生在Struts的配置文件中,首先将错误的配置文件列出如下:
====================================================
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<struts-config>
<!-- ======================================== Form Bean Definitions -->
<form-beans>
<form-bean
name="CreateUserForm"
type="com.zchome.CreateUserForm"/>
</form-beans>
<!-- ================================= Global Exception Definitions -->
<global-exceptions>
</global-exceptions>
<!-- =================================== Global Forward Definitions -->
<global-forwards>
<!-- Default forward to "Welcome" action -->
<!-- Demonstrates using index.jsp to forward -->
<forward name="welcome" path="/Welcome.do"/>
</global-forwards>
<!-- =================================== Action Mapping Definitions -->
<action-mappings>
<!-- Default "Welcome" action -->
<!-- Forwards to Welcome.jsp -->
<action
path="/Welcome"
type="org.apache.struts.actions.ForwardAction"
parameter="/jsp/Welcome.jsp"/>
<action path="/createuserpage" forward="/jsp/createuser.jsp">
</action>
<action
path="/docreateuser"
type="com.zchome.CreateUserAction"
name="CreateUserForm"
scope="request"
input="createuser">
<forward name="createusersuccess" path="/jsp/Welcome.jsp"/>
<forward name="createuser" path="/jsp/createuser.jsp"/>
</action>
</action-mappings>
<!-- ===================================== Controller Configuration -->
<controller>
<set-property property="processorClass" value="org.apache.struts.tiles.TilesRequestProcessor"/>
</controller>
<!-- ================================ Message Resources Definitions -->
<message-resources parameter="resources.application"/>
<!-- ======================================= Plug Ins Configuration -->
<!-- ========== Tiles plugin =================== -->
<!-- -->
<!--
This plugin initialize Tiles definition factory. This later can takes some
parameters explained here after. The plugin first read parameters from web.xml, then
overload them with parameters defined here. All parameters are optional.
The plugin should be declared in each struts-config file.
- definitions-config: (optional)
Specify configuration file names. There can be several comma
separated file names (default: ?? )
- moduleAware: (optional - struts1.1)
Specify if the Tiles definition factory is module aware. If true (default),
there will be one factory for each Struts module.
If false, there will be one common factory for all module. In this later case,
it is still needed to declare one plugin per module. The factory will be
initialized with parameters found in the first initialized plugin (generally the
one associated with the default module).
true : One factory per module. (default)
false : one single shared factory for all modules
- definitions-parser-validate: (optional)
Specify if xml parser should validate the Tiles configuration file.
true : validate. DTD should be specified in file header. (default)
false : no validation
Paths found in Tiles definitions are relative to the main context.
-->
<!-- comment following if struts1.0.x -->
<plug-in className="org.apache.struts.tiles.TilesPlugin" >
<set-property property="definitions-config"
value="/WEB-INF/tiles-defs.xml" />
<set-property property="moduleAware" value="true" />
<set-property property="definitions-parser-validate" value="true" />
</plug-in>
<!-- end comment if struts1.0.x -->
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">
<set-property
property="pathnames"
value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml"/>
</plug-in>
</struts-config>
====================================================
首先描述一下系统的出错背景:
1、从首页点击链接来到添加用户的页面 正常
2、在添加用户页面中输入Vlidator.xml文件中定义的错误数据,弹出Javascript对话框,提示出错 正常
3、在添加用户页面中输入合法数据,数据保存进入文件并重定向到首页 正常
4、在添加用户页面中输入ActionForm中定义的非法数据,系统应返回到添加用户的页面 出错!!!
OK,来着重看这个添加动作的定义,如下:
====================================================
<action
path="/docreateuser"
type="com.zchome.CreateUserAction"
name="CreateUserForm"
scope="request"
input="createuser">
<forward name="createusersuccess" path="/jsp/Welcome.jsp"/>
<forward name="createuser" path="/jsp/createuser.jsp"/>
</action>
====================================================
从以上的定义可以看出,如果Validate验证出错,Struts应该为我们重定向到input域所定义的uri,即/jsp/createuser.jsp
看起来应该没有问题,再来看看出错信息,如下:
====================================================
java.lang.IllegalArgumentException: Path createuser does not start with a "/" character
at org.apache.catalina.core.ApplicationContext.getRequestDispatcher(ApplicationContext.java:1179)
at org.apache.catalina.core.ApplicationContextFacade.getRequestDispatcher(ApplicationContextFacade.java:174)
at org.apache.struts.action.RequestProcessor.doForward(RequestProcessor.java:1062)
at org.apache.struts.tiles.TilesRequestProcessor.doForward(TilesRequestProcessor.java:274)
at org.apache.struts.action.RequestProcessor.internalModuleRelativeForward(RequestProcessor.java:1012)
at org.apache.struts.tiles.TilesRequestProcessor.internalModuleRelativeForward(TilesRequestProcessor.java:345)
at org.apache.struts.action.RequestProcessor.processValidate(RequestProcessor.java:980)
at org.apache.struts.action.RequestProcessor.process(RequestProcessor.java:255)
at org.apache.struts.action.ActionServlet.process(ActionServlet.java:1482)
at org.apache.struts.action.ActionServlet.doPost(ActionServlet.java:525)
。。。以下省略。。。
====================================================
出错信息清楚的说明,“createuser”这个path应该以“/”字符开头
为定位这个错误,从以上错误信息,开始打开Struts的源码RequestProcessor.java进行研究,首先来到这一段:
====================================================
public class RequestProcessor {
。。。。。。
protected boolean processValidate(HttpServletRequest request,
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
if (form == null) {
return (true);
}
// Was this request cancelled?
if (request.getAttribute(Globals.CANCEL_KEY) != null) {
if (log.isDebugEnabled()) {
log.debug(" Cancelled transaction, skipping validation");
}
return (true);
}
// Has validation been turned off for this mapping?
if (!mapping.getValidate()) {
return (true);
}
// Call the form bean's validation method
if (log.isDebugEnabled()) {
log.debug(" Validating input form properties");
}
ActionMessages errors = form.validate(mapping, request);
if ((errors == null) || errors.isEmpty()) {
if (log.isTraceEnabled()) {
log.trace(" No errors detected, accepting input");
}
return (true);
}
// Special handling for multipart request
if (form.getMultipartRequestHandler() != null) {
if (log.isTraceEnabled()) {
log.trace(" Rolling back multipart request");
}
form.getMultipartRequestHandler().rollback();
}
// Has an input form been specified for this mapping?
String input = mapping.getInput();
if (input == null) {
if (log.isTraceEnabled()) {
log.trace(" Validation failed but no input form available");
}
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("noInput",
mapping.getPath()));
return (false);
}
// Save our error messages and return to the input form if possible
if (log.isDebugEnabled()) {
log.debug(" Validation failed, returning to '" + input + "'");
}
request.setAttribute(Globals.ERROR_KEY, errors);
if (moduleConfig.getControllerConfig().getInputForward()) {
ForwardConfig forward = mapping.findForward(input);
processForwardConfig( request, response, forward);
} else {
internalModuleRelativeForward(input, request, response);
}
return (false);
}
====================================================
在出错信息中,提到了internalModuleRelativeForward这个方法,所以着重看以上代码的最后几行,可以看到,如果
moduleConfig.getControllerConfig().getInputForward()这个方法返回了false,那么internalModuleRelativeForward
这个方法将被调用。inputForward是什么?ModuleConfig是管理所有配置信息的一个manager类,那么moduleConfig.getControllerConfig()
这个方法返回的肯定是ControllerConfig这个类的一个实例,那么inputForward肯定是ControllerConfig类的一个成员变量了
再看看struts-config.xml,里面有<controller>这个标签,初步猜测ControllerConfig应该是读取这个标签的一个配置类
而<controller>这个标签应该定义了ActionServlet作为Controller的一些行为!
OK,再来看ControllerConfig这个类中有关inputForward这个成员变量的一些代码,如下:
====================================================
/**
* <p>Should the <code>input</code> property of {@link ActionConfig}
* instances associated with this module be treated as the
* name of a corresponding {@link ForwardConfig}. A <code>false</code>
* value treats them as a module-relative path (consistent
* with the hard coded behavior of earlier versions of Struts.</p>
*
* @since Struts 1.1
*/
protected boolean inputForward = false;
public boolean getInputForward() {
return (this.inputForward);
}
public void setInputForward(boolean inputForward) {
this.inputForward = inputForward;
}
====================================================
开始有点明白了,原来inputForward这个属性默认值是false,那么由于没有配置这个属性,那么上述的那个方法
moduleConfig.getControllerConfig().getInputForward()自然就返回false了,Bingo!
那么重点就转移到了internalModuleRelativeForward这个方法了,看这个方法的源代码,如下:
====================================================
protected void internalModuleRelativeForward(
String uri,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Construct a request dispatcher for the specified path
uri = moduleConfig.getPrefix() + uri;
// Delegate the processing of this request
// FIXME - exception handling?
if (log.isDebugEnabled()) {
log.debug(" Delegating via forward to '" + uri + "'");
}
doForward(uri, request, response);
}
protected void doForward(
String uri,
HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
// Unwrap the multipart request, if there is one.
if (request instanceof MultipartRequestWrapper) {
request = ((MultipartRequestWrapper) request).getRequest();
}
RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
if (rd == null) {
response.sendError(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("requestDispatcher", uri));
return;
}
rd.forward(request, response);
}
====================================================
从上可以看到,这个方法是将uri = moduleConfig.getPrefix() + uri;这个东东传给了doForward方法
而doForward这个方法又调用了javax.servlet.ServletContext的方法getRequestDispatcher这个方法
既然出错信息中是路径出了问题,那么看来这个参数uri非常的重要,极有可能就是这个uri发生了错误导致了出错
OK,开始剖析这个uri,从头开始看,这个uri是这样被赋值的:
uri = moduleConfig.getPrefix() + uri
1、moduleConfig.getPrefix()这个方法返回的应该是""
(这个请看ActionServlet的Init方法,如果在web.xml文件中定义ActionServlet的时候,给定了一些init-params,那么这个prefix就有可能
不为空,这里不再列举了)
2、代码右边的这个uri是从processValidate这个方法中定义的input,如下:
String input = mapping.getInput();
这个input应该是struts-config.xml文件中定义的那个action的input,也就是“createuser”,如果Struts将其做了进一步的解析,那么这个
input应该进一步被转化成为“/jsp/createuser.jsp”
好,到此为止,可以看到,这个uri不是“createuser”,那就是“/jsp/createuser.jsp”,再来看getRequestDispatcher这个方法的定义,
翻开Servlet的API文档,可以看到如下一段话:
====================================================
public RequestDispatcher
getRequestDispatcher(java.lang.String path)Returns a RequestDispatcher object that acts as a wrapper
for the resource located at the given path. A RequestDispatcher object can be used to forward a request
to the resource or to include the resource in a response. The resource can be dynamic or static.
The pathname must begin with a "/" and is interpreted as relative to the current context root.
Use getContext to obtain a RequestDispatcher for resources in foreign contexts. This method returns null
if the ServletContext cannot return a RequestDispatcher.
====================================================
终于有拨云见日的感觉了,因为这段话和出错信息实在是太一致了!由上面这段话,我们可以断定,uri这个变量的值
肯定是“createuser”,而不是我们所希望的“/jsp/createuser.jsp”。为什么会这样呢?显然是struts-config.xml中配置
有些还是不对,或是缺了点什么。想到这里,很自然的就联想到上面所提到的InputForward这个配置项了,因为从字面意思上
看来,这个配置项的用处就应该是将input的值解析成forward中对应的值,而且在ControllerConfig中,这个变量默认值是
false,所以猜测将其改成true是不是就可以了呢?
为了寻找答案,再次翻开struts-example(因为这个例子中的action也定义了input),终于找到了答案,和之前猜测的果然
十分吻合,如下:
====================================================
<controller>
<set-property property="inputForward" value="true"/>
</controller>
====================================================
至此,问题解决,正确的action配置可以是如下两种:
====================================================
1、不使用inputForward
<action
path="/docreateuser"
type="com.zchome.CreateUserAction"
name="CreateUserForm"
scope="request"
input="/jsp/createuser.jsp">
<forward name="createusersuccess" path="/jsp/Welcome.jsp"/>
</action>
2、使用InputForward
<action
path="/docreateuser"
type="com.zchome.CreateUserAction"
name="CreateUserForm"
scope="request"
input="createuser">
<forward name="createusersuccess" path="/jsp/Welcome.jsp"/>
<forward name="createuser" path="/jsp/createuser.jsp"/>
</action>
<controller>
<set-property property="inputForward" value="true"/>
<set-property property="processorClass" value="org.apache.struts.tiles.TilesRequestProcessor"/>
</controller>
====================================================
而且,从问题的定位过程中,还学到了一招,就是javax.servlet.RequestDispatcher:
RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
if (rd == null) {
response.sendError(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("requestDispatcher", uri));
return;
}
rd.forward(request, response);
以后再做页面重定向,只要给定相对的uri就可以了,再也不用写上一层的虚拟目录名或自己拼URL了
11月13日 22:11 | <nobr>添加评论</nobr> | <nobr>固定链接</nobr> | <nobr>引用通告 (0)</nobr> | <nobr>记录它</nobr> | <nobr>J2EE</nobr>
Struts源码研究 - html-Cancel标签应用注意事项篇
Struts里的html:Cancel标签是在Form中经常运用的一个标签,主要功能就是cancel当前Form,一般写法如下:
=======================================================
<html:cancel>
<bean:message key="createuser.cancelbutton"/>
</html:cancel>
=======================================================
这个标签将生成如下的HTML代码:
<input type="submit" name="org.apache.struts.taglib.html.CANCEL" value="返回" onclick="bCancel=true;">
bCancel=true是一段javascript,bCancel是在使用Struts的Validator时,Struts自动为我们加的一段Javascript代码里的一个变量
这段Javascript简略摘要如下:
=======================================================
<script type="text/javascript" language="Javascript1.1">
<!-- Begin
var bCancel = false;
function validateCreateUserForm(form) {
if (bCancel)
return true;
else
return validateMaxLength(form) && validateRequired(form) && validateMinLength(form);
}
。。。以下省略
=======================================================
由上可以看到,这个bCancel=true时,Javascript将自动将表单提交(return true),也就是说,如果我们在后台Action的代码里
没有对这个Cancel动作写特定代码的话,这个Cancel标签产生的效果和submit按钮产生的动作完全一致!!(因为这个按钮的
type也等于submit)
这一点需要非常的注意!所以,一般来说,我们在Action类的execute方法里面会加上如下的一段代码来处理这个Cancel动作:
=======================================================
// Was this transaction cancelled?
if (isCancelled(request)) {
return (mapping.findForward("createusersuccess"));
}
=======================================================
有了以上的代码,Cancel动作就有了相应的处理代码,转到相关的页面了。
本来事情已经解决,但本着对Struts源码研究的精神,我们还需要对以上代码研究一下
OK,让我们来看一下isCancelled这个方法在什么地方被定义了,内容是什么?
首先发现,这个方法被定义在Action类里面,代码如下:
=======================================================
/**
* <p>Returns <code>true</code> if the current form's cancel button was
* pressed. This method will check if the <code>Globals.CANCEL_KEY</code>
* request attribute has been set, which normally occurs if the cancel
* button generated by <strong>CancelTag</strong> was pressed by the user
* in the current request. If <code>true</code>, validation performed
* by an <strong>ActionForm</strong>'s <code>validate()</code> method
* will have been skipped by the controller servlet.</p>
*
* @param request The servlet request we are processing
* @see org.apache.struts.taglib.html.CancelTag
*/
protected boolean isCancelled(HttpServletRequest request) {
return (request.getAttribute(Globals.CANCEL_KEY) != null);
}
=======================================================
哦,原来是在request对象中查找Globals.CANCEL_KEY这个key值是否绑定了一个对象,如果是,那么就代表按下Cancel按钮后,
Struts会在request对象中绑定一个对象,并以这个key值来命名
那Struts是在什么地方绑定了这个对象呢?很自然的,让我们从头找起
从ActionServlet的process方法开始找起,历经多次方法调用,终于找到了根源,原来是在RequestProcessor.java中,代码如下:
=======================================================
/**
* <p>Process an <code>HttpServletRequest</code> and create the
* corresponding <code>HttpServletResponse</code>.</p>
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a processing exception occurs
*/
public void process(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
//。。。省略代码若干
// Process any ActionForm bean related to this request
ActionForm form = processActionForm(request, response, mapping);
//答案就在这个processPopulate方法中
processPopulate(request, response, form, mapping);
if (!processValidate(request, response, form, mapping)) {
return;
}
/**
* Populate the properties of the specified ActionForm instance from
* the request parameters included with this request. In addition,
* request attribute <code>Globals.CANCEL_KEY</code> will be set if
* the request was submitted with a button created by
* <code>CancelTag</code>.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param form The ActionForm instance we are populating
* @param mapping The ActionMapping we are using
*
* @exception ServletException if thrown by RequestUtils.populate()
*/
protected void processPopulate(HttpServletRequest request,
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
throws ServletException {
if (form == null) {
return;
}
// Populate the bean properties of this ActionForm instance
if (log.isDebugEnabled()) {
log.debug(" Populating bean properties from this request");
}
form.setServlet(this.servlet);
form.reset(mapping, request);
if (mapping.getMultipartClass() != null) {
request.setAttribute(Globals.MULTIPART_KEY,
mapping.getMultipartClass());
}
RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
request);
// Set the cancellation request attribute if appropriate
if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) ||
(request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) {
request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
}
}
=======================================================
OK,看最后几行代码,Struts从request中取得Constants.CANCEL_PROPERTY这个参数,如果这个参数不为空,那么他就将
TRUE这个对象以Globals.CANCEL_KEY为key值,放到了request对象中
至于这个Constants.CANCEL_PROPERTY这个值是什么,现在都可以猜到了,显然就是html:Cancel这个标签生成的HTML代码
中,Cancel这个按钮的名称嘛!查了一下,果然是:
<input type="submit" name="org.apache.struts.taglib.html.CANCEL" value="返回" onclick="bCancel=true;">
而Constants.CANCEL_PROPERTY这个值就是org.apache.struts.taglib.html.CANCEL
至此,真相大白,看来以后使用html:Cancel这个标签要小心了 :)
=======================================================
<html:cancel>
<bean:message key="createuser.cancelbutton"/>
</html:cancel>
=======================================================
这个标签将生成如下的HTML代码:
<input type="submit" name="org.apache.struts.taglib.html.CANCEL" value="返回" onclick="bCancel=true;">
bCancel=true是一段javascript,bCancel是在使用Struts的Validator时,Struts自动为我们加的一段Javascript代码里的一个变量
这段Javascript简略摘要如下:
=======================================================
<script type="text/javascript" language="Javascript1.1">
<!-- Begin
var bCancel = false;
function validateCreateUserForm(form) {
if (bCancel)
return true;
else
return validateMaxLength(form) && validateRequired(form) && validateMinLength(form);
}
。。。以下省略
=======================================================
由上可以看到,这个bCancel=true时,Javascript将自动将表单提交(return true),也就是说,如果我们在后台Action的代码里
没有对这个Cancel动作写特定代码的话,这个Cancel标签产生的效果和submit按钮产生的动作完全一致!!(因为这个按钮的
type也等于submit)
这一点需要非常的注意!所以,一般来说,我们在Action类的execute方法里面会加上如下的一段代码来处理这个Cancel动作:
=======================================================
// Was this transaction cancelled?
if (isCancelled(request)) {
return (mapping.findForward("createusersuccess"));
}
=======================================================
有了以上的代码,Cancel动作就有了相应的处理代码,转到相关的页面了。
本来事情已经解决,但本着对Struts源码研究的精神,我们还需要对以上代码研究一下
OK,让我们来看一下isCancelled这个方法在什么地方被定义了,内容是什么?
首先发现,这个方法被定义在Action类里面,代码如下:
=======================================================
/**
* <p>Returns <code>true</code> if the current form's cancel button was
* pressed. This method will check if the <code>Globals.CANCEL_KEY</code>
* request attribute has been set, which normally occurs if the cancel
* button generated by <strong>CancelTag</strong> was pressed by the user
* in the current request. If <code>true</code>, validation performed
* by an <strong>ActionForm</strong>'s <code>validate()</code> method
* will have been skipped by the controller servlet.</p>
*
* @param request The servlet request we are processing
* @see org.apache.struts.taglib.html.CancelTag
*/
protected boolean isCancelled(HttpServletRequest request) {
return (request.getAttribute(Globals.CANCEL_KEY) != null);
}
=======================================================
哦,原来是在request对象中查找Globals.CANCEL_KEY这个key值是否绑定了一个对象,如果是,那么就代表按下Cancel按钮后,
Struts会在request对象中绑定一个对象,并以这个key值来命名
那Struts是在什么地方绑定了这个对象呢?很自然的,让我们从头找起
从ActionServlet的process方法开始找起,历经多次方法调用,终于找到了根源,原来是在RequestProcessor.java中,代码如下:
=======================================================
/**
* <p>Process an <code>HttpServletRequest</code> and create the
* corresponding <code>HttpServletResponse</code>.</p>
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a processing exception occurs
*/
public void process(HttpServletRequest request,
HttpServletResponse response)
throws IOException, ServletException {
//。。。省略代码若干
// Process any ActionForm bean related to this request
ActionForm form = processActionForm(request, response, mapping);
//答案就在这个processPopulate方法中
processPopulate(request, response, form, mapping);
if (!processValidate(request, response, form, mapping)) {
return;
}
/**
* Populate the properties of the specified ActionForm instance from
* the request parameters included with this request. In addition,
* request attribute <code>Globals.CANCEL_KEY</code> will be set if
* the request was submitted with a button created by
* <code>CancelTag</code>.
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param form The ActionForm instance we are populating
* @param mapping The ActionMapping we are using
*
* @exception ServletException if thrown by RequestUtils.populate()
*/
protected void processPopulate(HttpServletRequest request,
HttpServletResponse response,
ActionForm form,
ActionMapping mapping)
throws ServletException {
if (form == null) {
return;
}
// Populate the bean properties of this ActionForm instance
if (log.isDebugEnabled()) {
log.debug(" Populating bean properties from this request");
}
form.setServlet(this.servlet);
form.reset(mapping, request);
if (mapping.getMultipartClass() != null) {
request.setAttribute(Globals.MULTIPART_KEY,
mapping.getMultipartClass());
}
RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(),
request);
// Set the cancellation request attribute if appropriate
if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) ||
(request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) {
request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
}
}
=======================================================
OK,看最后几行代码,Struts从request中取得Constants.CANCEL_PROPERTY这个参数,如果这个参数不为空,那么他就将
TRUE这个对象以Globals.CANCEL_KEY为key值,放到了request对象中
至于这个Constants.CANCEL_PROPERTY这个值是什么,现在都可以猜到了,显然就是html:Cancel这个标签生成的HTML代码
中,Cancel这个按钮的名称嘛!查了一下,果然是:
<input type="submit" name="org.apache.struts.taglib.html.CANCEL" value="返回" onclick="bCancel=true;">
而Constants.CANCEL_PROPERTY这个值就是org.apache.struts.taglib.html.CANCEL
至此,真相大白,看来以后使用html:Cancel这个标签要小心了 :)
相关推荐
总结来说,这个“Struts框架入门学习---工程源码”提供了实践Struts框架的基础,涵盖了Action、MVC模式、配置文件、拦截器和视图等方面,对于初学者来说是一份宝贵的资料。通过分析和运行这个工程,你可以更好地理解...
接下来,你需要在Struts2的配置文件`struts.xml`中定义Action,声明使用的资源文件。例如: ```xml <package name="default" namespace="/" extends="struts-default"> <action name="hello" class=...
这篇博文将深入探讨Struts2中的result配置以及各种视图转发类型。 首先,让我们理解Result的基本概念。在Struts2的配置文件(如struts.xml)中,每个Action可以有多个结果,每个结果对应一个特定的视图。Result配置...
### Struts2 + Spring2 + Hibernate3 整合实例源码分析 #### 一、概述 随着企业级应用的发展,为了提高开发效率和系统维护性,越来越多的项目开始采用MVC设计模式。其中,Struts2作为MVC框架中的佼佼者,在前端...
5. **错误处理与验证**: Struts2允许在Action中进行字段验证,如果上传过程中出现问题,可以通过`addFieldError`方法添加错误信息,然后返回错误的结果代码,如`INPUT`,让用户重新上传。 6. **源码分析**: 要深入...
这个"Struts2+上传文件源码"是一个演示如何在Struts2框架下实现文件上传的示例代码。 首先,我们来理解上传文件的基本流程。在Struts2中,文件上传是通过`Commons FileUpload`库来处理的,这是一个Apache提供的开源...
Struts是Java Web开发中的一款经典MVC框架,它的核心组件包括Action、ActionForm、Dispatcher Servlet(Controller)以及视图技术。在这个“Struts中ActionError学习”的主题中,我们将深入探讨ActionError这一机制...
例如,`Login`和`Regist`两个Action分别对应登录和注册功能,它们的success和input结果分别指向成功或失败的页面。 - 同时,配置全局异常处理,将所有Action抛出的Exception异常映射到同一个结果页面,如`/error....
- **Action**:表示具体的业务逻辑操作,通常继承自`org.apache.struts.action.Action`类。 - **ActionForm**:用于封装客户端表单的数据,它通常与Action关联使用。 - **ActionMapping**:定义了请求URL和Action...
在Struts2中,配置文件(struts.xml)扮演着关键角色,它定义了请求和Action之间的映射关系。在用户注册程序中,我们需要在struts.xml中配置`UserRegisterAction`,指定其处理的请求路径以及返回的结果页面。例如: `...
2. **创建ActionForm**:创建一个继承自Struts1的`ActionForm`类,如`UploadForm`,并添加一个`File`类型的属性,用于存储上传的文件信息: ```java public class UploadForm extends ActionForm { private File...
1. **Struts配置**:首先,你需要在struts-config.xml文件中配置Action和ActionForm,声明处理文件上传和下载的类。例如,为文件上传设置一个`FileUploadAction`,并定义对应的`FileUploadForm`,其中包含用于存放...
在Struts2中,这通常通过表单实现,表单包含一个`<input type="file">`元素,用户可以选择本地文件。Struts2的Action类会接收这个文件,并使用`Commons FileUpload`库来处理文件内容。这个库处理文件分割、内存限制...
这个"struts2登录程序源码"是一个基础的教学资源,旨在帮助初学者理解如何在Struts2框架下实现用户登录功能。下面我们将深入探讨Struts2的核心概念以及在创建登录程序时涉及的关键知识点。 1. **Struts2框架简介**...
Action执行后,如果数据验证失败,会返回Input视图,否则根据ActionForward指定的视图路径展示结果。ActionFormBean通常用于收集和验证用户输入,而ActionServlet则负责整个流程的调度。 在实际操作中,开发者需要...
3. **配置Struts2 Action**:在Struts2的配置文件(struts.xml或@Action注解)中,定义一个接收文件上传的Action。例如: ```xml <action name="uploadAction" class="com.example.UploadAction"> ...
在 Struts2 中,我们通常会创建一个 Action 类,该类包含用于接收文件的属性,并通过表单提交来处理文件。 2. **Struts2 单个文件上传**: - **方式一**: 在这个例子中,我们创建了一个名为 `UploadAction` 的 ...
标签提到的“源码”意味着本文可能提供了Struts2 Action类或相关组件的源代码示例,帮助读者深入理解其实现原理。而“工具”可能指的是开发者在构建Struts2应用时可能用到的各种辅助工具,如IDE插件、构建工具(如...
在本资料中,"itcast Struts源码"可能是指一个包含了Struts框架的示例项目,其中整合了Spring、Hibernate(SSH)这三大开源框架,用于演示如何在实际应用中协同工作。 1. **SSH整合**:Spring、Struts和Hibernate的...
例如,表单字段`<input type="text" name="username">`对应的值会自动赋给Action的`username`属性。 **4. Action结果和重定向** 在Struts2中,Action的结果配置可以包含OGNL表达式,用于动态指定跳转的页面或传递...