`

struts2框架自带项目struts2-mailreader学习笔记

阅读更多
struts2框架自带项目struts2-mailreader学习笔记


我们依照使用该项目的步骤来说。
第一、一个网页请求转到welcome.do的网页,我们去查看web.xml中的配置,发现要调用StrutsPrepareAndExecuteFilte这个过滤器。而这个过滤器是干什么用的呢?
查看源代码我们发现:
1.StrutsPrepareAndExecuteFilte 实现了Filter接口。
服务器启动调用StrutsPrepareAndExecuteFilter .init()初始化来初始化几个重要的类,比如Dispatcher,FilterHostConfig。
2.当前台有请求发来,StrutsPrepareAndExecuteFilter 的doFilter()被调用,这个doFilter实现了封装request,查找 ActionMapper,以确定这个请求是否需要调用某个 Action。
那么,为什么使用StrutsPrepareAndExecuteFilter而不是用FilterDispatcher呢?可参见http://wellfrog.iteye.com/blog/773482
在这里,还要注意,项目源码为:
execute.executeAction(request, response, mapping);

而这个execute是ExecuteOperations的一个实例,这个execute调用的这个方法executeAction实际上是通过调用Dispatcher类的serviceAction方法来实现的:
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
        dispatcher.serviceAction(request, response, servletContext, mapping);
    }

再查看Dispatcher的源码,发现
:Configuration config = configurationManager.getConfiguration();
            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, method, extraContext, true, false);

            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

            // if the ActionMapping says to go straight to a result, do it!
            if (mapping.getResult() != null) {
                Result result = mapping.getResult();
                result.execute(proxy.getInvocation());
            } else {
                proxy.execute();
            }

这也就验证了struts2工作流程中的把控制权交给 ActionProxy,然后ActionProxy 依照框架的配置文件(struts.xml),找到需要调用的 Action 类。即
<action name="Welcome" class="mailreader2.Welcome">
            <result>/Welcome.jsp</result>
            <interceptor-ref name="guest"/>
        </action>
ActionProxy 创建一个 ActionInvocation 的实例。ActionInvocation 先调用相关的拦截器 (Action 调用之前的部分),最后调用 Action。 具体的拦截器工作流程请参见http://wlzjdm.iteye.com/blog/1525899


第二、找到相应的welcome.java即这个继承ActionSupport的类后,其经过一些操作后返回SUCCESS,去struts.xml,在该项目中为mailreader-support.xml,因为struts.xml中为
<include file="mailreader-default.xml"/>
<include file="mailreader-support.xml"/>

<action name="Welcome" class="mailreader2.Welcome">
            <result>/Welcome.jsp</result>
            <interceptor-ref name="guest"/>
        </action>

这里的1.result的name默认值为SUCCESS,type的默认值为dispatcher。返回相应的视图。

2.interceptor-ref是指定某个action使用哪一个拦截器。可以是一个interceptor,也可以是一个interceptor-stack。再看mailreader-default.xml中
<interceptor-stack name="guest" >
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>
代表这个guest拦截器是一个拦截器栈,使用的是defaultStack这个拦截器栈,在struts2-core-xxx.jar包下找到struts-default.xml,再在其中找到如下代码:
<interceptor-stack name="defaultStack">
                <interceptor-ref name="exception"/>
                <interceptor-ref name="alias"/>
                <interceptor-ref name="servletConfig"/>
                <interceptor-ref name="i18n"/>
                <interceptor-ref name="prepare"/>
                <interceptor-ref name="chain"/>
                <interceptor-ref name="scopedModelDriven"/>
                <interceptor-ref name="modelDriven"/>
                <interceptor-ref name="fileUpload"/>
                <interceptor-ref name="checkbox"/>
                <interceptor-ref name="multiselect"/>
                <interceptor-ref name="staticParams"/>
                <interceptor-ref name="actionMappingParams"/>
                <interceptor-ref name="params">
                    <param name="excludeParams">dojo\..*,^struts\..*,^session\..*,^request\..*,^application\..*,^servlet(Request|Response)\..*,parameters\...*</param>
                </interceptor-ref>
                <interceptor-ref name="conversionError"/>
                <interceptor-ref name="validation">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="workflow">
                    <param name="excludeMethods">input,back,cancel,browse</param>
                </interceptor-ref>
                <interceptor-ref name="debugging"/>
            </interceptor-stack>

            <!-- The completeStack is here for backwards compatibility for
                 applications that still refer to the defaultStack by the
                 old name -->
            <interceptor-stack name="completeStack">
                <interceptor-ref name="defaultStack"/>
            </interceptor-stack>

各拦截器的作用详见http://blog.csdn.net/jadyer/article/details/5887529

第三、我们查看返回的页面Welcome.jsp。1.如下国际化代码需要注意:
<s:url id="en" action="Welcome">
            <s:param name="request_locale">en</s:param>
        </s:url>
<s:a href="%{en}">English</s:a>

查看struts2参考手册的关于Generic Tags的内容,我们发现struts2的url标签的用法是这样的:
引用
This tag is used to create a URL.
You can use the <param> tag inside the body to provide additional request parameters. If the value of a param is an Array or an Iterable all the values will be added to the URL.

可以理解为:这个标签用来创建一个请求,而param这个标签提供附加的请求参数值,注意请求参数为request_locale,请求参数值为en。这个en实际上就是国际化文件的语言码_国家码,因为这里的国际化文件名就为MailReaderSupport_en.properties,所以我们就写一个语言码即可。综上所述,这里相当于创建这样一个请求:
http://localhost:8080/MyStrutsTest3/Welcome.do?request_locale=en
这里就要说下国际化的实现过程:该国际化是通过Struts2的I18nInterceptor拦截器会拦截所有的Action而实现的,它主要做的事情为:1.从客户端发送过来的请求参数中寻找是否存在名为request_locale的参数。2.若有,则将request_locale的value转化为locale保存起来
该locale是保存在以WW_TRANS_I18N_LOCALE所命名的session里面的。查看拦截器源码,发现:session.put(attributeName, locale)其中attributeName即为protected String attributeName = DEFAULT_SESSION_ATTRIBUTE
和public static final String DEFAULT_SESSION_ATTRIBUTE = "WW_TRANS_I18N_LOCALE"
于是便可通过这种方式实现一个选择页面所显示的语言环境的功能。
而这个
<s:url id="en" action="Welcome">
的id,现在应该使用var属性取代,它的作用是:
引用
Name used to reference the value pushed into the Value Stack
,也就是用这个id属性的值去命名它,以便我们可以用它去引用值栈中的值。

2.注意一下代码:
<s:text name="index.title"/>

查看参考手册得知:
引用
Render a I18n text message.

The message must be in a resource bundle with the same name as the action that it is associated with. In practice this means that you should create a properties file in the same package as your Java class with the same name as your class, but with .properties extension.

If the named message is not found in a property file, then the body of the tag will be used as default message. If no body is used, then the stack will be searched, and if a value is returned, it will written to the output. If no value is found on the stack, the key of the message will be written out.

也就是说这个标签用于呈现国际化的文件,再查询有关本地化的参考文档,得知
引用
Resource bundles are searched in the following order:
    ActionClass.properties
    Interface.properties (every interface and sub-interface)
    BaseClass.properties (all the way to Object.properties)
    ModelDriven's model (if implements ModelDriven), for the model object repeat from 1
    package.properties (of the directory where class is located and every parent directory all the way to the root directory)
    search up the i18n message key hierarchy itself
    global resource properties

也就是说我们这样查找:
Welcome这个Action的资源文件Welcome_xx_xx.properties(类级别)注意类级别的文件名定义:Action类名_语言码_国家码.properties,比如:LoginAction_en_US.properties,LoginAction_zh_CN.properties;;
找不到,则查找与它所实现的接口同名的资源文件MyInterface.properties;
再查找其父文件ParentAction.properties;
判断当前ChildAction是否实现接口ModelDriven。如果是,调用getModel()获得对象,查找与其同名的资源文件;
查找当前包下的package.properties文件(包级别);
查找当前包的父包,直到最顶层包;
查找i8n资源文件?;
查找全局资源文件,如果是在全局中定义资源文件,则需要在struts.xml里配置,例子:<constant name="struts.custom.i18n.resources" value="message" />,其中message为资源文件的起始名;(全局级别)
如果还找不到,则在值栈中搜索,参考http://txyly998.iteye.com/blog/336357
所以我们这里是查找相应的父类MailReaderSupport的资源文件MailReaderSupport.properties.
3.我们再看如下代码
<li><a href="<s:url action="Registration_input"/>"><s:text
            name="index.registration"/></a></li>

这里就要注意我们上面提到的url的用法,即
引用
This tag is used to create a URL.
我们查询相应的mailreader-support配置文件得到:
<action name="Registration_*" method="{1}" class="mailreader2.Registration">
            <result name="input">/Registration.jsp</result>
            <result type="redirectAction">MainMenu</result>
            <interceptor-ref name="guest"/>
        </action>

这里要注意这个{1}的用法即代表这个*号,在这里就为调用Registration中的input方法,我们查看代码发现经过一系列操作后,其返回input视图,对应Registration这个页面.

第四、我们查看这个Registration页面,发现如下值得关注
<s:if test="task=='Create'">
        <title><s:text name="registration.title.create"/></title>
    </s:if>
    <s:if test="task=='Edit'">
        <title><s:text name="registration.title.edit"/></title>
    </s:if>

这里的<s:if test="">是struts2的控制标签,其中的task为Registration这个Action的属性。这里是使用OGNL表达式从值栈中取出的。这个Task属性的值如果是Create就代表在session中没有这个用户名,所以就在该注册页面中显示注册信息,而不显示注册以后显示的信息,而这个注册以后的信息是否显示则是通过判断task是否为Edit来得到。
再往下看,
<s:actionerror/>
<s:form action="Registration_save" validate="false">
    <s:token />
    <s:hidden name="task"/>
    <s:if test="task == 'Create'">
        <s:textfield key="username"/>
    </s:if>
    <s:else>
        <s:label key="username"/>
        <s:hidden name="username"/>
    </s:else>

    <s:password key="password" showPassword="true"/>
    <s:password key="password2"/>
    <s:textfield key="user.fullName"/>
    <s:textfield key="user.fromAddress"/>
    <s:textfield key="user.replyToAddress"/>

    <s:if test="task == 'Create'">
        <s:submit key="button.save" action="Registration_save"/>
        <s:reset key="button.reset"/>
        <s:submit action="Welcome" key="button.cancel"
                    onclick="form.onsubmit=null"/>
    </s:if>
    <s:else>
        <s:submit key="button.save" action="Registration"/>
        <s:reset key="button.reset"/>
        <s:submit action="MainMenu" key="button.cancel"
                    onclick="form.onsubmit=null"/>
    </s:else>

</s:form>

<s:form>是表单用法,<s:token/>用法如下
引用
The token tag is used to help with the "double click" submission problem. It is needed if you are using the TokenInterceptor or the TokenSessionInterceptor. The s:token tag merely places a hidden element that contains the unique token.

此外,这些标签的name属性代表这些元素的名称,而key属性则用于访问国际化文件内容,
这里我们要介绍下几种调用国际化资源的方式:
1、jsp页面的国际化,a.使用标签
<s:text name ="资源文件中中定义的key">
输出国际化b.使用struts2标签的key属性,此时一定要注意theme不能设置为simple,否则不能成功引用国家化文件,如<s:textfield name =“name”key ="资源化文件中的key">。c.使用<s:i18n></s:i18n>在对应资源文件中查找如
<s:i18n name = "globalMessages">
                                 <s:text name ="username"></s:text>
                              </s:i18n>

2、getText(String key)可在所有需要使用的地方使用,如a.
<s:textfield label ="%{getText("username")}" name = "username">
其中%{}是取出值栈中的Action对象 然后我们直接调用它的相应方法取出这个username属性。或者说我们对这个值栈中的action对象我们调用它相应的方法。
b.action中的validate方法中增加错误信息的国际化
this.addFieldError(username,this.getText("username.invalid"));
this.addActionError(this.getText("username.invalid"));

c.xml验证框架中的错误信息国际化
<message key = "name.invalid"></message>
或<message>${getText("name.invalid")}</message>
而<s:hidden>是
引用
Renders an HTML input element of type hidden, populated by the specified property from the ValueStack.
对应用户名不符合要求时显示的文字,如下面的Password is not in the range 4 through 10.

我们可以通过看运行界面来理解这些标签:
Username:  123
Password is not in the range 4 through 10.
Password:  ***
(Repeat) Password:*** 
Full Name:  123
From Address is an invalid e-mail address.
From Address:  123
Reply To Address is an invalid e-mail address.
Reply To Address:  123
继续查看代码,发现这个页面的输入结果提交给Registration_save这个action,同样是调用Registration这个Action的save方法。查看save方法,发现如下需要注意
addActionError(getText("error.username.unique"));
这个代码意思是出错后就在相应jsp页面的<s:actionerror>中显示信息,而这个信息通过getText这个方法从国际化文件中得到。
 
第五、我们回到welcome.jsp,看他的第二个链接
引用
Log on to the MailReader Demonstration Application

这个链接是调用Login的input方法,
<action name="Login_*"  method="{1}" class="mailreader2.Login">
            <result name="input">/Login.jsp</result>
            <result name="cancel" type="redirectAction">Welcome</result>
            <result type="redirectAction">MainMenu</result>
            <result name="expired" type="chain">ChangePassword</result>
            <exception-mapping
                    exception="org.apache.struts.apps.mailreader.dao.ExpiredPasswordException"
                    result="expired"/>
            <interceptor-ref name="guest"/>
        </action>

这里虽然Login没有定义input方法,但是他的父类ActionSupport定义了input方法,它直接返回input字符串。这个结果集就要我们返回login.jsp页面。再来看这个Login.jsp页面。它的action就为Login,那么我们对于这个action映射可以看到它的method实际上应该调用的是LoginAction的execute(),我们可以理解其为:不写method,其默认值就是execute()。完成相应的execute()中的操作后,其返回SUCCESS,这个对应的result为MainMenu,因为
<result type="redirectAction">MainMenu</result>
默认的name为SUCCESS。得到这个主页面,然后也有两个链接,
1.一个是编辑自己的信息,这个返回到刚刚我们提到的Registration页面,和上面的不同在于,由于
<s:if test ="task=='Edit'">
这里就显示相应的编辑信息。
2.另一个就是注销当前用户,返回到welcome.jsp。由于代码差不多,这里就不在赘述了。
第六、我们看客户端的校验框架,
<validators>

    <field name="username">
        <field-validator type="requiredstring">
            <message key="error.username.required"/>
        </field-validator>
    </field>

    <field name="user.fullName">
        <field-validator type="requiredstring">
            <message key="error.fullName.required"/>
        </field-validator>
    </field>

    <field name="user.fromAddress">
        <field-validator type="requiredstring">
            <message key="error.fromAddress.required"/>
        </field-validator>
        <field-validator type="email">
            <message key="errors.email"/>
        </field-validator>
    </field>

    <field name="user.replyToAddress">
        <field-validator type="email">
            <message key="errors.email"/>
        </field-validator>
    </field>

</validators>
如果我们要使用上述客户端校验框架,我们必须在所检验的字段的这个页面的form标签中将<s:form validate="true">的validate属性设置为TRUE,并且不能将<s:form theme="">的theme属性设置为simple,我的理解是,我们使用simple主题,排版中就不会有错误信息显示的地方了。设置<s:form validate = "true">属性之后,表单的onsubmit="return validateForm_register();"即在源码中多出了一段函数名为validateForm_register()的JavaScript代码,调用完成后再提交到相关页面。

这里还要注意一点,被校验的Action类要继承ActionSupport类,并要在action配置中指定名为input的jsp,因为struts2在校验失败后会自动返回input页面

具体的项目文件见附件,也可去官网下载。
分享到:
评论

相关推荐

    struts2-mailreader

    struts2自带的项目之一struts2-mailreader,本人已调试通过,能直接运行,是学习的好例子!

    struts框架jar包

    struts-1.3.8 包( &lt;br&gt;antlr-2.7.2.jar bsf-2.3.0.jar commons-beanutils-1.7.0....mailreader-dao-1.3.8.jar struts-scripting-1.3.8.jar struts-taglib-1.3.8.jar struts-tiles-1.3.8.jar )

    struts2.0官方项目之四(showcase)

    ------------------------------------------------ 实例说明: &lt;br&gt; 将 &lt;br&gt; “struts2-mailreader-2.0.9.war” &lt;br&gt; 改为 &lt;br&gt; “struts2-mailreader-2.0.9.rar” &lt;br&gt; 使用解压工具解压-...

    struts2.0官方项目之一(blank)

    ------------------------------------------------ 实例说明: &lt;br&gt; 将 &lt;br&gt; “struts2-mailreader-2.0.9.war” &lt;br&gt; 改为 &lt;br&gt; “struts2-mailreader-2.0.9.rar” &lt;br&gt; 使用解压工具解压-...

    struts2.0官方项目之三(portlet)

    ------------------------------------------------ 实例说明: &lt;br&gt; 将 &lt;br&gt; “struts2-mailreader-2.0.9.war” &lt;br&gt; 改为 &lt;br&gt; “struts2-mailreader-2.0.9.rar” &lt;br&gt; 使用解压工具解压-...

    struts2例子二

    struts2-mailreader-2.0.11.2.war官方提供的入门例子之一,简单易懂,eclipse用户直接导入war文件即可将工程导入

    struts编写的mailreader中文例子

    struts编写的mailreader中文例子,希望对你们有所帮助

    SSH基于Eclipse实现Web层的分页功能源代码

    struts-mailreader-dao-1.3.10.jar struts-scripting-1.3.10.jar struts-taglib-1.3.10.jar struts-tiles-1.3.10.jar (五)、Spring+Struts: \spring-framework-2.5.5\dist\modules\spring-webmvc-struts.jar ...

    实现Web层的分页功能源代码

    struts-mailreader-dao-1.3.10.jar struts-scripting-1.3.10.jar struts-taglib-1.3.10.jar struts-tiles-1.3.10.jar (五)、Spring+Struts: \spring-framework-2.5.5\dist\modules\spring-webmvc-struts.jar ...

    将Web层分页封装成通用模块源代码

    struts-mailreader-dao-1.3.10.jar struts-scripting-1.3.10.jar struts-taglib-1.3.10.jar struts-tiles-1.3.10.jar (五)、Spring+Struts: \spring-framework-2.5.5\dist\modules\spring-webmvc-struts.jar ...

    SSH基于Eclipse将Web层分页封装成通用模块

    struts-mailreader-dao-1.3.10.jar struts-scripting-1.3.10.jar struts-taglib-1.3.10.jar struts-tiles-1.3.10.jar (五)、Spring+Struts: \spring-framework-2.5.5\dist\modules\spring-webmvc-struts.jar ...

    JSF与Shale开发用包

    开发JSF或Shale开发用的包,自己准备研究这两个东西却发现需要很多包一下子又找不全,总算找了差不多了,拿出来与大家分享.其中包含:shale-core.jar ...web.jar spring-webmvc.jar struts-mailreader-dao.jar

    struts2例子

    struts.apps.mailreader 页面跳转 后台类请求

    mailreader

    mailreader

Global site tag (gtag.js) - Google Analytics