zoukankan      html  css  js  c++  java
  • WebWork技术

    WebWork技术

     

    推荐好文:

    webwork:

    http://www.blogjava.net/moxie/archive/2006/10/20/76375.html 

    webwork Ioc集成案例:

    https://blog.csdn.net/iteye_6948/article/details/81593707

    webwork Ioc集成案例:

    http://developer.51cto.com/art/200907/136223.htm

     

     

    官网:https://docs.huihoo.com/webwork/2.2.6/WebWork.html

     

    简介

    安装、使用

    基本项目目录和包

    处理请求的Action和一些页面

    静态页面

    一个简单的webwork项目需要哪些配置?

    webwork的配置文件

    webwork的请求映射文件

    其他相关说明

    webwork采用spring Ioc注入自定义组件

    核心概念

    三个关键

    探索webwork初始化内容

    init

    doFilter

    WebWork原理

    原理图解析

    ValueStack(值堆栈)和EL(表达式语言)

    ValueStack功能测试

    ValueStack综合描述

    Interceptor(拦截器)

    WebWork提供的拦截器介绍

    实例

    接下来要读

     

     

    struts1

     

     

    webwork

     

    简介

     

    Webwork是由OpenSymhony组织开发的,致力于组件化和代码重用的J2EE Web框架。WebWork现在已经被拆分成了Xwork1和WebWork2两个项目,如上图。

    xwork简洁、灵活功能强大,它是一个标准的Command模式框架实现,并且完全从web层脱离出来。Xwork提供了很多核心功能:前端拦截机(interceptor),运行时表单属性验证,类型转换,强大的表达式语言(OGNL),Ioc容器等。

    WebWork2建立在Xwork之上,初始化HTTP请求和响应。所有的请求都会被它的前端控制器ServletDispatcher,最新版本时FilterDispatcher)截获。前端控制器对请求的数据进行包装,初始化上下文数据,根据配置文件查找请求URL对应的Action类,执行Action,将执行结果转发到相应的展现页面。WebWork2支持多视图表示,视图部分可以使用JSP,FreeMarker,XML,JasperReports,Velocity等。

    对比struts1来说,webwork2不在使用Servlet去处理请求,而是交给过滤器去处理。从整体架构蓝图上看,webwork也比struts1复杂了许多。但是万变不离其宗,虽然webwork复杂了很多,但是在蓝图中还是能看到一些类似struts1的模式,下面还是根据源码来深入了解吧。

    思考Filter为什么能作为控制器?通常作为控制器的不是Servlet吗?

    在回答这个问题之前,请不要忘记了Model1模式,在Model1模式中控制器由JSP充当,既然JSP能作为控制器,为什么Filter不能作为控制器呢?或者说:作为一个控制器需要哪些条件?还是具备哪些功能才能作控制器?

    基于三层架构的Web层需要解决的问题

    我们这里讨论的Web层,是基于典项的三层架构:Web层,业务层,数据传。那作为Web层需要解决的问题:

    1、数据的输入。如何获得基于无状态HTTP的请求数据?如何将请求的字符数据转换为对应的模型对象?

    2、输入数据的验证。如何验证输入数据的合法性并给出明确的错误消息提示?(前端/后端,这个验证对于Web层来说不值一提)

    3、数据的输出。如何展现复杂的对象结构?如何处理复杂的展现逻辑?(我觉得这东西只要遵循HTTP规范,数据输出应该不是难事)

    4、数据的传递和共享。如何在不同的请求或页面之间传递和共享数据?(session+cookie吗?)

    5、页面的流程管理。如何管理Web应用中的页面流程?(这个不是浏览器处理吗?不管V是什么,最后不都是HTML吗?)

    6、模块化的管理。如何将复杂的Web应用以模块化的方式管理?(模块化?比如说?)

    7、灵活可扩展的架构。如何支持各种不同的展现层技术?如何与业务层或数据层的各种框架整合?(这是个难点)

    8、安全和访问控制的管理。如何提供基于Web的安全机制和资源访问控制的管理?

    9、代码实现的简洁和高效。如何让开发步骤和代码维护变得简单?如何尽量减少开发的中间环节?如何将公共的功能剥离出来,并可以灵活的组装应用?

    10、其他问题。异步调用、国际化支持、文件上传、防止重复提交等等。

     

    安装、使用

    基本项目目录和包

    一个webwork项目基本文件目录:

     

     

    1、首先在pom.xml中引入webwork相关依赖包:

        <dependencies>
            <dependency>
                <groupId>com.opensymphony</groupId>
                <artifactId>webwork</artifactId>
                <version>2.2.6</version>
            </dependency>
            <dependency>
                <groupId>javax.servlet</groupId>
                <artifactId>javax.servlet-api</artifactId>
                <version>3.1.0</version>
            </dependency>
        </dependencies>

    2、web.xml中配置webwork总控制器(前端控制器FilterDispatcher)

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" 
                             "http://java.sun.com/dtd/web-app_2_3.dtd">
    <web-app>
    
        <filter>
            <filter-name>FilterDispatcher</filter-name>
            <filter-class>com.opensymphony.webwork.dispatcher.FilterDispatcher</filter-class>
        </filter>
        
        <filter-mapping>
            <filter-name>FilterDispatcher</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
        
        <!-- 如果展现层技术使用Freemarker(WebWork官方的推荐),如果在页面中需要使用标签库,必须在web.xml中配置JspSupportServlet -->
        <!-- <servlet>
            <servlet-name>JspSupportServlet</servlet-name>
            <servlet-class>com.opensymphony.webwork.views.JspSupportServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet> -->
    
    </web-app>

    处理请求的Action和一些页面

    xwork.xml文件中我们配置了一个name=welcome的Action,来看一下这个WelcomeAction:

    package action;
    
    import com.opensymphony.xwork.ActionSupport;
    
    /**
     * 即代表表单映射的实体,又能够处理请求
     * 这里的WelComeAction即作为表单映射的实体又作为一个控制器。
     * @author admin
     *
     */
    public class WelcomeAction extends ActionSupport {
        private static final long serialVersionUID = 1L;
        private String uname = "";
        private String welStr = "";
    
        @Override
        public String execute() throws Exception {
            welStr += uname + " welcome!";
            return SUCCESS;
        }
    
        public String getWelStr() {
            return welStr;
        }
    
        public void setUname(String uname) {
            this.uname = uname;
        }
    
        
    }

    疑问:在上面的代码中没有使用任何JavaServlet的API,它是如何获得请求的数据并将执行结果暴露给输入页面的呢?它是通过值堆栈和表达式语言来实现(后面会有详细介绍)。我们先看看 Action 是如何获得请求的数据。输入框的名字“ uname ”,即请求的参数的名字,它就是一个表达式语言。 WebWork 遇到这个表达式语言,就会执行被请求Action的相应的 setUname 方法,而 uname 参数的值就是执行这个方法的参数。这些是在 Action 执行 execute 方法之前完成,这样 Action 就可以获得请求的数据对象。类似的原理, WebWork 也是通过表达式语言将 getWelStr方法暴露给结果页面。

     

    思考:【在这里我不得不说一下,这个WelcomeAction不仅要控制业务逻辑(真实的业务逻辑不推荐在Action中实现),而且它还有点像一个实体类!这虽然在webwork的ValueStack中很有用,你可以使用El表达式很容易就获取ValueStack里的内容,而ValueStack里的内容则来自于你的一次WelcomeAction请求里的属性。我把这归为不分离的方式。

    对于分离方式:由于把WelcomeAction只看作为一个控制器,所以将实体单独作为WelcomeActionForm分离出来,然后在WelcomeAction中注入WelcomeActionForm,这样在ValueStack中拿到注入到WelcomeAction里的真正的实体对象WelcomeActioneForm,而不是WelcomeAction对象实体。

    如果你把FilterDispatcher看做C,把Action看做M,Jsp等展现层看做V,那么上面那种不分离是合理的,但是我更习惯把Action也看做是C,所以我将Action中的M部分剥离出来,单独作为M,这样把FilterDispatcher看做全局控制器,Action看做是业务控制器,在业务控制器中注入M和service层,这中设计分离的更加彻底。

    在查阅了部分文档后发现在webwork自身的Ioc容器是以接口注入的方式实现的,webwork2.2 bata 2版本之后就弃用了自身的Ioc容器,采用spring Ioc作为官方Ioc容器,我们可以使用Spring Ioc。】

     

    静态页面

    // index.jsp
    <form action="welcome.action" method="POST">
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="uname"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="登陆"></td>
            </tr>
        </table>
    </form>
    // success.jsp
    <%@ page language="java" contentType="text/html; charset=UTF-8"
        pageEncoding="UTF-8"%>
    <%@ taglib prefix = "ww" uri = "/webwork" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
    <title>Success</title>
    </head>
    <body>
    <ww:property value="%{welStr}"/>
    </body>
    </html>

     

    一个简单的webwork项目需要哪些配置?

     

    webwork的配置文件

    webwork.properties或webwork.xml文件,在该文件中可以配置一些webwork项目的参数,这些东西你可以在WebWorkConstants类中参考,比如配置spring,配置热加载模式等等,在这个demo中只使用了第一个即扩展Action请求URL的后缀:

    webwork.action.extension=action,do
    #webwork.devMode=true
    #webwork.objectFactory=spring

    注意webwork.properties文件必须放在运行时的classpath目录(编译后的WEB-INF/classes目录)下,所以你只需放在项目的src目录下最终编译后这些配置文件都会被放在classpath下。

    在一般的Web框架中,前端控制器会将特定后缀的请求URL映射到对应的Action请求中,而webwork的前端控制器会接收任意的请求,但它默认是将.action结尾的URL映射为WebWork的Action请求,在上面代码中我们则是扩展了一项(这个习惯来自于struts1的请求方式)。

     

     

    webwork的请求映射文件

    xwork.xml

    <!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.1.1//EN" "http://www.opensymphony.com/xwork/xwork-1.1.1.dtd"> 
    
    <xwork>
        <include file="webwork-default.xml" />
        <package name="default" extends="webwork-default">
            <action name="welcome" class="action.WelcomeAction">
                <result name="success" type="dispatcher">/sys/success.jsp</result>
                <interceptor-ref name="params" />
            </action>
        </package>
    </xwork>

    配置说明:“name”参数用于请求URL。例如:http://localhost:8080/**/welcome.action

    这样通过请求url,我们就可以在配置文件中找到对应的action。“class”即实现Action类。一个Action中可以定义多个Result。Result的“name”对应Action方法返回的字符串。在我们的WelcomeAction方法中,执行成功之后返回字符串“success”。即我们在这个Action里定义的那个result。“interceptor-ref”定义这个action所使用到的拦截器。我们这里使用了WebWork提供的params拦截器,它能自动的帮我们将请求的参数组装成Action中需要的数据对象。通过这个拦截器,它会调用Action的setUname方法,取得uname参数的值,这样Action就可以获得用户输入的uname数据。也许你会奇怪,params这个拦截器是从哪里变来的?这个xwork.xml文件包含了WebWork提供的默认配置文件webwork-default.xml,可以在webwork-2.2.6.jar文件中找到。我们定义的这个package继承了它里面的package "webwork-default",这样就可以共享“webwork-default” package里面定义的所有拦截器。

    对于action来说它代表着一次请求或调用,配置文件名被称为xwork,也就是说这一块配置的内容是No-Web的,在这里可以配置一些自定义的拦截器、action等,感觉这个文件被称之为webwork的请求映射文件并不恰当,但是又想不到更好的名字。

    配置到此,启动项目,访问http://localhost:8080/WebWork即可展现相应结果。

     

     

    其他相关说明

    1、关于前端控制器。在以前WebWork2版本里,前端控制器是ServletDispatcher,这是一个JavaServlet。而现在是一个Filter,会导致无法在页面中使用Jsp的include来包含一个WebWork的Action请求的URL。如果真的需要这样做,可以使用WebWork的action标签库。

    2、在 Jsp 页面中, WebWork 的标签库不需要在 web.xml 中定义,在页面中通过如下的代码直接引用:

    <%@ taglib prefix = "ww" uri = "/webwork" %>

    3、在Jsp页面中,默认“altSyntax”是开启的。它是用来解决标签库中的字符串和表达式语                               言混淆的问题。所以,作为变量的表达式语言应该放在%{}中,否则WebWork会把它当作字符串处理。

    4、展现层如果使用Freemarker,需要在web.xml添加如下配置:

    <servlet>
          <servlet-name>JspSupportServlet</servlet-name>
          <servlet-class>com.opensymphony.webwork.views.JspSupportServlet</servlet-class>
          <load-on-startup>1</load-on-startup>
    </servlet>

    5、还有一些其它的定制,比如:编码,标签的模板文件等等,都可以在webwork.properties文件中配置。如果在ClassPath中没有这个文件,WebWork会自动读取WebWork的Jar包里面的default.properties文件。

     

     

     

    webwork采用spring Ioc注入自定义组件

    更改目录如下:

     

     

    基于getter/setter方式注入,集成方式如下:

    1、在webwork.properties文件中启用spring Ioc容器

    webwork.action.extension=action,do
    webwork.objectFactory=spring

    2、在resource目录下创建applicationContext.xml,配置需要spring管理的bean

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://www.springframework.org/schema/beans"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <bean id="welcome" class="action.WelcomeAction"></bean>
    
    </beans>

    3、将xwork.xml中action的class属性,由class名改为applicationContext中定义的bean名

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.1.1//EN" "http://www.opensymphony.com/xwork/xwork-1.1.1.dtd"> 
    
    <xwork>
        <include file="webwork-default.xml" />
        <package name="default" extends="webwork-default">
            <action name="welcome" class="welcome">
                <result name="success" type="dispatcher">/sys/success.jsp</result>
                <interceptor-ref name="params" />
            </action>
        </package>
    </xwork>

    4、在web.xml中配置spring配置文件的查找路径context-param

    <?xml version="1.0" encoding="UTF-8"?>
    
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns="http://java.sun.com/xml/ns/javaee"
        xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
        version="2.5">
    
        <filter>
            <filter-name>FilterDispatcher</filter-name>
            <filter-class>com.opensymphony.webwork.dispatcher.FilterDispatcher</filter-class>
        </filter>
    
        <filter-mapping>
            <filter-name>FilterDispatcher</filter-name>
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    
        <listener>
            <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
    
        <!-- Context Configuration locations for Spring XML files -->
        <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:applicationContext.xml</param-value>
        </context-param>
    </web-app>

     

    5、分离WelcomeAction中的entity特征,新建一个专门的实体类WelcomeActionForm

    package bean;
    
    
    public class WelcomeActionForm{
        private String uname = "";
        private String welStr = "";
    
        public String getUname() {
            return uname;
        }
    
        public void setUname(String uname) {
            this.uname = uname;
        }
    
        public String getWelStr() {
            return welStr;
        }
    
        public void setWelStr(String welStr) {
            this.welStr = welStr;
        }
        
    }

     

    6、在WelcomeAction中使用getter/setter方式注入WelcomeActionForm

    package action;
    
    import com.opensymphony.xwork.ActionSupport;
    
    import bean.WelcomeFormAction;
    
    /**
     * 只作为处理业务逻辑的控制模块
     * 
     * @author admin
     *
     */
    public class WelcomeAction extends ActionSupport {
        private static final long serialVersionUID = 1L;
        private WelcomeFormAction welcomeFormAction;
    
        @Override
        public String execute() throws Exception {
            this.welcomeFormAction.setWelStr(this.welcomeFormAction.getUname() + " welcome!");
            return SUCCESS;
        }
    
        public WelcomeFormAction getWelcomeFormAction() {
            return welcomeFormAction;
        }
    
        public void setWelcomeFormAction(WelcomeFormAction welcomeFormAction) {
            this.welcomeFormAction = welcomeFormAction;
        }
    
    }

    7、静态页面做相应修改

    // index.jsp只要修改表单提交字段值
    <form action="welcome.action" method="POST">
        <table>
            <tr>
                <td>用户名:</td>
                <td><input type="text" name="welcomeFormAction.uname"></td>
            </tr>
            <tr>
                <td></td>
                <td><input type="submit" value="登陆"></td>
            </tr>
        </table>
    </form>
    
    // success.jsp只要修改显示值栈值
    <body>
    <ww:property value="%{welcomeFormAction.welStr}"/>
    </body>

    到此,一个简单的webwork+spring Ioc的项目就结束了,源码链接

     

     

    核心概念

     

    三个关键

    WebWork的三个关键部分:

    1、Actions。一般一个Action代表一次请求或调用。在WebWork中,一般Action需要实现Action接口,或者直接继承基础类ActionSupport。这是,它要实现默认的execute方法,并返回一个在配置文件中1定义的Resulte(也就是一个字符串而已)。当然,Action也可以只是一个POJO(普通的java对象),不用继承任何类也不用实现任何接口。Action是一次请求的控制器,同时也充当数据模型的角色,我们强烈建议不要将业务逻辑放在Action中。

    2、Results。它是一个结果页面的定义。它用来指示Action执行之后,如何显示执行的结果。Result Type表示如何以及用哪种视图技术展现结果。通过Result Type,WebWork可以方便的支持多种视图技术;而且这些视图技术可以相互转换,Action部分不需要做任何改动。

    3、Interceptors。WebWork的拦截器,WebWork截获Action请求,在Action请求执行之前或之后调用拦截器方法。这样,可以用插拔的方式将功能注入到Action中。WebWrok框架的很多功能都是以拦截器的形式提供出来。例如参数组装,验证,国际化,文件上传等。

     

     

    探索webwork初始化内容

    init

    load-on-startup=1,这样在启动tomcat时就会初始化前端控制器,接下来就该看看这个控制器在初始化的时候干了些什么?

    // FilterDispatcher
    
    /**
    * WebWork的主过滤器,可处理四种不同的职责:
    *    1、执行action
    *    2、清理ActionContext
    *    3、提供静态内容
    *    4、在请求生命周期中启动xwork的Ioc容器
    */
    public class FilterDispatcher implements Filter, WebWorkStatics {
    
    /**
    * 1、初始化packages参数
    * 2、真正操作其实都是在DispatcherUtils下的init方法里
    */
    public void init(FilterConfig filterConfig) throws ServletException {
            this.filterConfig = filterConfig;
            String param = filterConfig.getInitParameter("packages");
            String packages = "com.opensymphony.webwork.static template com.opensymphony.webwork.interceptor.debugging";
            if (param != null) {
                packages = param + " " + packages;
            }
            this.pathPrefixes = parse(packages);
            DispatcherUtils.initialize(filterConfig.getServletContext());
        }

    initialize静态方法中创建了DispatcherUtils对象实例,在创建实例时将ServletContext作为参数传入DispatcherUtils构造函数中,又直接在DispatcherUtils构造函数里调用了其init方法,所有有价值的操作都是在这里完成的:

    /**
    * 一个实用程序类,FilterDispatcher将其大部分任务委托给它。
    * 它是一个静态单例对象。
    */
    public class DispatcherUtils {   
        
      protected void init(ServletContext servletContext) {
        boolean reloadi18n = Boolean.valueOf((String) Configuration.get(WebWorkConstants.WEBWORK_I18N_RELOAD)).booleanValue();
        LocalizedTextUtil.setReloadBundles(reloadi18n);
    
        // initialize Ioc容器
        /**
        * 初始化Ioc容器
        *  webwork自带的有自己的Ioc容器(2.2.2版本之后 已被弃用),如果你想使用
        *  spring的Ioc容器可以在配置文件webwork.properties文件中申明
        *  webwork.objectFactory = spring,并且在web.xml中配置spring的监听
        *  器ContextLoaderListener
        */
        ObjectFactory objectFactory = null;
        if (Configuration.isSet(WebWorkConstants.WEBWORK_OBJECTFACTORY)) {
            String className = (String) Configuration.get(WebWorkConstants.WEBWORK_OBJECTFACTORY);
            if (className.equals("spring")) {
                // note: this class name needs to be in string form so we don't put hard
                //       dependencies on spring, since it isn't technically required.
                className = "com.opensymphony.webwork.spring.WebWorkSpringObjectFactory";
            } else if (className.equals("plexus")) {
                // note: this class name needs to be in string form so we don't put hard
                //       dependencies on spring, since it isn't technically required.
                className = "com.opensymphony.webwork.plexus.PlexusObjectFactory";
            }
    
            try {
                Class clazz = ClassLoaderUtil.loadClass(className, DispatcherUtils.class);
                objectFactory = (ObjectFactory) clazz.newInstance();
                if (objectFactory instanceof ObjectFactoryInitializable) {
                    ((ObjectFactoryInitializable) objectFactory).init(servletContext);
                }
                ObjectFactory.setObjectFactory(objectFactory);
            } catch (Exception e) {
                LOG.error("Could not load ObjectFactory named " + className + ". Using default ObjectFactory.", e);
            }
        }
    
        
        // Intialize 对象类型确定器
        /**
        * Configuration类:
        *   处理所有WebWork2配置属性(读取的是webwork.properties/webwork.xml,在
        *   WebWorkConstants类中含有一些默认配置项)。此类的实现是可插入的(默
        *   认实现是DefaultConfiguration)。这使开发人员能够自定义如何设置和检索
        *   WebWork2属性。 例如,开发人员可能希望在委派给WebWork之前检查单独的属
        *   性库。
        */
        if (Configuration.isSet(WebWorkConstants.WEBWORK_OBJECTTYPEDETERMINER)) {
            String className = (String) Configuration.get(WebWorkConstants.WEBWORK_OBJECTTYPEDETERMINER);
            if (className.equals("tiger")) {
                // note: this class name needs to be in string form so we don't put hard
                //       dependencies on xwork-tiger, since it isn't technically required.
                className = "com.opensymphony.xwork.util.GenericsObjectTypeDeterminer";
            }
            else if (className.equals("notiger")) {
                className = "com.opensymphony.xwork.util.DefaultObjectTypeDeterminer";
            }
    
            try {
                Class clazz = ClassLoaderUtil.loadClass(className, DispatcherUtils.class);
                ObjectTypeDeterminer objectTypeDeterminer = (ObjectTypeDeterminer) clazz.newInstance();
                ObjectTypeDeterminerFactory.setInstance(objectTypeDeterminer);
            } catch (Exception e) {
                LOG.error("Could not load ObjectTypeDeterminer named " + className + ". Using default DefaultObjectTypeDeterminer.", e);
            }
        }
    
        /**
        * 如果在webwork.properties中配置了
        * DevMode=true
        * 则表示是开发模式(即启用热加载),配置为false则代表是产品模式。
        * 开发模式被激活时能明细提高开发效率,它会提供更多日志信息。
        *   1、每次修改都会重新加载资源文件,所以当对资源文件进行修改后,下一次请求时就能反映出相应的变化
        *   2、每次修改会重新加载xml配置文件等,这样方便调试或修改配置文件而不用重新部署web服务器
        */
        if ("true".equals(Configuration.get(WebWorkConstants.WEBWORK_DEVMODE))) {
            devMode = true;
            Configuration.set(WebWorkConstants.WEBWORK_I18N_RELOAD, "true");
            Configuration.set(WebWorkConstants.WEBWORK_CONFIGURATION_XML_RELOAD, "true");
        }
    
        //check for configuration reloading
        /**
        * 热加载开启后,修改代码重新加载配置
        */
        if (Configuration.isSet(WebWorkConstants.WEBWORK_CONFIGURATION_XML_RELOAD) &&
                "true".equalsIgnoreCase(Configuration.getString(WebWorkConstants.WEBWORK_CONFIGURATION_XML_RELOAD))) {
            FileManager.setReloadingConfigs(true);
        }
    
        if (Configuration.isSet(WebWorkConstants.WEBWORK_CONTINUATIONS_PACKAGE)) {
            String pkg = Configuration.getString(WebWorkConstants.WEBWORK_CONTINUATIONS_PACKAGE);
            ObjectFactory.setContinuationPackage(pkg);
        }
    
        // test wether param-access workaround needs to be enabled
        /**
        * 测试是否需要启用param-access解决方法
        * 如果检测到WebLogic服务器。 启用WebWork参数访问解决方案。否则使用webwork默认解决方案
       */
        if (servletContext.getServerInfo().indexOf("WebLogic") >= 0) {
            LOG.info("WebLogic server detected. Enabling WebWork parameter access work-around.");
            paramsWorkaroundEnabled = true;
        } else if (Configuration.isSet(WebWorkConstants.WEBWORK_DISPATCHER_PARAMETERSWORKAROUND)) {
            paramsWorkaroundEnabled = "true".equals(Configuration.get(WebWorkConstants.WEBWORK_DISPATCHER_PARAMETERSWORKAROUND));
        } else {
            LOG.debug("Parameter access work-around disabled.");
        }
        
        // inform startup listeners 通知一些要启动的监听器
        if (Configuration.isSet(WebWorkConstants.WEBWORK_DISPATCHER_START_UP_LISTENER)) {
            String[] startupListenerClassNames = Configuration.getString(WebWorkConstants.WEBWORK_DISPATCHER_START_UP_LISTENER).split(",");
            for (int a=0; a<startupListenerClassNames.length; a++) {
                     /**
                     * 默认是只有StartUpListen,这个监听器监听着webwork的启动
                     * 当webwork启动后:
                     *   在这里由于我们没有在webwork.properties中指定任何实现了
                     *   StarUpListener接口的监听器,所以在webwork启动时我们并
                     *   没做什么东西。如果你有什么任务需要在webwork启动时执行,
                     *   可以写一个监听器,在监听器里处理你的业务逻辑。
                     */
                String startupListenerClassName = startupListenerClassNames[a].trim();
                try {
                    StartUpListener startUpListener = (StartUpListener) objectFactory.buildBean(startupListenerClassName, Collections.EMPTY_MAP);
                    if (LOG.isDebugEnabled()) {
                        LOG.debug("notifying start up listener ["+startUpListener+"]");
                    }
                    startUpListener.startup();
                }
                catch(Exception e) { // we might also get ClassCastException
                    LOG.warn("shutdown listener ["+startupListenerClassName+"] failed to be initialized, it will be ignored", e);
                }
            }
        }
    
      }

    上面有一些细节,我并不是太理解,而且源码中也没给出任何注释,部分源码也没有公布,所以我有些地方无法理解,但是总体看下来在前端控制器中主要做了3大步骤和一个扩展步骤:

    1、启动Ioc容器(Spring或webwork自己的Ioc容器)

    2、读取webwork所有配置文件信息,初始化一些默认的配置

    3、启动热加载

    4、监听webwork启动 (扩展步骤)

     

    这里对比一下struts1的初始化:

     由于webwork使用FilterDispatcher作为控制器,所以就不在对Servlet依赖,在这里也并没有对web.xml做处理的步骤了(在struts1中是有的,在initServlet方法里),这是使用FilterDispatcher的好处。

     其次在webwork使用了Ioc容器去管理bean,这一点在struts1中是没有的,struts1使用的是servlet,都是交给servlet容器去管理,对于依赖则是硬编码的方式做的,对比这点,这是webwork改进的地方。

     热加载貌似在struts1的init中并没有显示的说明,但是如果修改struts1中的资源,也会重新加载项目。【对热加载研究的不够深入】

     

     

    doFilter

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
            ServletContext servletContext = filterConfig.getServletContext();
    
            /**
            * 在请求到来时先对该请求做一些预处理:
            *   1、设置请求的编码
            *   2、设置时区
            */
            DispatcherUtils du = DispatcherUtils.getInstance();
            du.prepare(request, response);
            try {
                /**
                 * 首先判断该请求是不是一个multipart/form-data?
                 *   是:就将其包装成MultiPartRequestWrapper
                 *   不是:包装成webwork的wrap对象
                 */
                request = du.wrapRequest(request, servletContext);
            } catch (IOException e) {
                String message = "Could not wrap servlet request with MultipartRequestWrapper!";
                LOG.error(message, e);
                throw new ServletException(message, e);
            }
            
            /**
            * 对应webwork原理图,下面两行代码就是FilterDispatcher根据请求的
            * URL解析出对应的action名称,然后去咨询ActionMapper这个action是
            * 需要被执行(即mapping是否为null,null就不执行)
            */
            ActionMapper mapper = ActionMapperFactory.getMapper();
            ActionMapping mapping = mapper.getMapping(request);
    
            /**
            * 为null:看看当前请求的是不是静态资源(css,js,png等)?
            *   是:静态资源就将该静态资源读取的response里
            *   不是:请求的可能是jsp文件等,直接通过
            */
            if (mapping == null) {
                // there is no action in this request, should we look for a static resource?
                String resourcePath = RequestUtils.getServletPath(request);
    
                if ("".equals(resourcePath) && null != request.getPathInfo()) {
                    resourcePath = request.getPathInfo();
                }
    
                if ("true".equals(Configuration.get(WebWorkConstants.WEBWORK_SERVE_STATIC_CONTENT)) 
                        && resourcePath.startsWith("/webwork")) {
                    String name = resourcePath.substring("/webwork".length());
                    findStaticResource(name, response);
                } else {
                    // this is a normal request, let it pass through
                    chain.doFilter(request, response);
                }
                // WW did its job here
                return;
            }
    
            /**
            *  不为null:
            *    先设置容器(创建一个交互的场景)
            *      1、拿到session
            *      2、配置一些组件
            *     接下来的重点在serviceAction方法里
            */
            Object o = null;
            try {
    
                setupContainer(request);
                o = beforeActionInvocation(request, servletContext);
    
                du.serviceAction(request, response, servletContext, mapping);
            } finally {
                afterActionInvocation(request, servletContext, o);
                ActionContextCleanUp.cleanUp(req);
            }
        }
    /**
         * 加载Action并执行它。此方法首先从给定参数创建context map,
         * 然后从给定的action的名称和命名空间去加载AcrionProxy。
         * 之后,执行action并通过response对象返回。
         * 如果没有找到对应的action,将通过DispatcherUtils#sendError方法
         * 使用404返回代码发送回用户。
         */
        public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, ActionMapping mapping) throws ServletException {
            /**
            *  关于值堆栈的context map,它是一个放置值堆上下文数据的对象。
            *  通过符号“#”再加上对象的名称,可以访问这些数据(只可以访问)。
            *  一些JavaServlet相关的数据都放在这个容器中。这个对webwork的
            *  标签库特别有用,这样我们就可以直接通过表达式语言去访问request,
            *  attribute,session,application里的数据。
            *  例如:
            *    拿上下文对象:<ww:property value="%{#request}"/>。
            *    拿Action中属性:<ww:property value="%{welcomeFormAction.welStr}"/>
            */
            Map extraContext = createContextMap(request, response, mapping, context);
    
            // 如果存在先前的值堆栈,则创建新副本并将其传递给新Action使用
            OgnlValueStack stack = (OgnlValueStack) request.getAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY);
            if (stack != null) {
                extraContext.put(ActionContext.VALUE_STACK, new OgnlValueStack(stack));
            }
    
            try {
                // 获取基本的东西
                String namespace = mapping.getNamespace();
                String name = mapping.getName();
                String method = mapping.getMethod();
    
                String id = request.getParameter(XWorkContinuationConfig.CONTINUE_PARAM);
                if (id != null) {
                    // remove the continue key from the params - we don't want to bother setting
                    // on the value stack since we know it won't work. Besides, this breaks devMode!
                    Map params = (Map) extraContext.get(ActionContext.PARAMETERS);
                    params.remove(XWorkContinuationConfig.CONTINUE_PARAM);
    
                    // and now put the key in the context to be picked up later by XWork
                    extraContext.put(XWorkContinuationConfig.CONTINUE_KEY, id);
                }
                // 反射创建代理
                ActionProxy proxy = ActionProxyFactory.getFactory().createActionProxy(namespace, name, extraContext, true, false);
                // 调用某个方法:拿值/设置值/execute
                proxy.setMethod(method);
                request.setAttribute(ServletActionContext.WEBWORK_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();
                }
    
                // If there was a previous value stack then set it back onto the request
                if (stack != null) {
                    request.setAttribute(ServletActionContext.WEBWORK_VALUESTACK_KEY, stack);
                }
            } catch (ConfigurationException e) {
                LOG.error("Could not find action", e);
                sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
            } catch (Exception e) {
                throw new ServletException(e);
            }
        }

    结合webwork的流程图,在doFilter方法里的大致内容都很容易理解了。接下来看一下webwork的原理。

     

     

     

     

     

    WebWork原理

    原理图解析

     

    浅灰色方框

    分别代表了客户端的一次Http请求,和服务器端运算结束之后的一次响应。

     

    浅红色方框:

    表示一次Action请求所要经过的Servlet filters(Servlet 过滤器)。我们可以看到最后一个filter就是我们前面介绍的WebWork的前端控制器。

     

    蓝色方框:

    这是WebWork框架的核心部分。

       一次请求到了WebWork的前端控制器,它首先会根据请求的URL解析出对应的action 名称,然后去咨询ActionMapper这个action是否需要被执行。

     如果ActionMapper决定这个action需要被执行,前端控制器就把工作委派给ActionProxy。接着她们会咨询WebWork的配置管理器,并读取在web.xml文件中定义的配置信息。接下来ActionProxy会创建ActionInvocation对象。

     ActionInvocation是Xwork原理的(Command模式)实现部分。它会调用这个Action已定义的拦截器(before方法),Action方法,Result方法。

     最后,看上面流程的图的方向,它会再执行拦截器(after方法),再回到Servlet Filter部分,最后结束并传给用户一个结果响应。

     

     靛色方框:

    这是拦截器部分,在上面的拦截器章节我们已经有了详细的介绍。

     

    黄色方框:

    这是我们在开发Web应用时,需要自己开发的程序。其中包括:Action类,页面模板,配置文件xwork.xml。

     

     

     

     

    ValueStack(值堆栈)和EL(表达式语言)

    1、ValueStack其实就是一个放置Java对象得堆栈而已,唯一特别的是可以使用EL来获得值堆栈中对象属性的数据,并可以为值堆栈的对象属性赋值

    2、EL,全称Express Language,即表达式语言,它是简单的对象导航语言。有字符串(例如:方法名)和特殊字符组成(例如用 .  表示调用对象的属性方法)。通过EL,我们可以存、取对象数据,而且还可以直接访问类的静态数据,调用静态方法。

    3、WebWork的ValueStack底层有第三方开源项目OGNL实现。所有EL也都遵循OGNL的规范。我们在开发中,几乎不需要知道OGNL的细节。

    4、WebWork为每一次请求构建一个ValueStack,并将所有相关的数据对象(例如:Action对象、Model对象等)放到ValueStck中。再将ValueStack暴露给视图页面,这样页面就可以直接访问后台处理生成的数据

     

    ValueStack功能测试

    新建两个用于测试的实体类:Employee和Address

    package valueStack;
    
    public class Address {
        private String country;
        private String city;
        private String street;
        
        // getter/setter method
    }

     

    新建junit测试

    package valueStack;
    
    public class Employee {
        private String name;
        private Address address;
    
        // getter/setter method and update getAddress like this
        public Address getAddress() {
            if (address == null) {
                address = new Address();
            }
            return address;
        }
    
    }

    setUp方法会在每一个测试方法执行之前执行。在setUp方法中首先创建两个对象:valueStack对象和employee对象,然后将employee对象入栈。这样employee对象就在值堆栈的最上端。

     

    测试方法Get:

    在测试方法testCouldGetDataFormObjectInOgnlValueStackByEL中,先为值栈里的employee对象设置数据。

    第一个验证断言:

    assertEquals("MaoMao", valueStack.findValue("name"));

    解释为:我们期望使用表达式语言“name”去ValueStack中查找某个的对象的name属性值为“MaoMao”。valueStack.findValue("name")语句会调用ValueStack中对象的getName方法(即employeed对象的getName方法),并返回这个方法返回的数据。

    第二个验证断言:

    assertEquals("China", valueStack.findValue("address.country"));

    解释为:期望表达式语言“address.country”取得的数据是对象的address属性对象的country属性,即取得雇员对象的地址所在的国家。深入下去,也就是调用employee对象的getAddress().getCountry()方法。

     

    测试方法Set:

    valueStack.setValue("name", "keke");

    它通过表达式语言“name”将“keke”赋值给ValueStack中某个对象的name属性里,即调用栈里对象employee对象的setName方法将后面的值作为方法的参数。同理,第二个验证断言调用栈中某个对象的getAddress().setCountry()方法,把后面的数据作为setCountry方法的参数。

     

     

    ValueStack综合描述

    1、所有存取操作的目标对象都是已放入ValueStack中的对象。所以在使用之前,必须要先将对象入栈。例如上面在setUp方法中的语句:valueStack.push(employee)

    2、每一次WebWork请求,在创建Action对象之前都会先生成一个ValueStack对象,再将Action对象入栈。这样我们就可以通过表达式语言来直接存取action对象的数据,所以在WebWork中,action具有数据模型的功能。

    3、在对ValueStack进行存取操作时,我么弄得操作指令(表达式语言)并不知到它是对哪个对象进行操作。例如,我们在获取员工姓名时,我们给的操作指令是“name”,这时,并不知道ValueStack里面的对象一定就是employee。ValueStack会从上而下,遍历栈里面的对象,并试图调用当前遍历对象的getName方法,当它找到了这个方法,并执行之后,就将执行得到的数据返回。

    例如:新建Teacher类:

    package valueStack;
    
    public class Teacher {
        private String name;
    
        // getter/setter
    }

    然后在setUp方法中:

    public class OgnlValueStackTest extends TestCase {
        private OgnlValueStack valueStack;
        private Employee employee;
        private Teacher teacher;
    
        @Override
        protected void setUp() throws Exception {
            valueStack = new OgnlValueStack();
            employee = new Employee();
            teacher = new Teacher();
            valueStack.push(employee);
            valueStack.push(teacher);
        }
    
        public void testCouldGetDataFormObjectInOgnlValueStackByEL() {
            employee.setName("MaoMao");
            Address address = new Address();
            address.setCountry("China");
            employee.setAddress(address);
            
            teacher.setName("liyang");
    
            assertEquals("MaoMao", valueStack.findValue("name"));
            assertEquals("China", valueStack.findValue("address.country"));
        }

    由于teacher对象是在employee后面放入的,且含有getName方法,所以在测试断言1时就会提示测试失败。原因:堆栈取出的真实值为liyang,而比较值是“MaoMao”。

    4、关于值堆栈的context map,它是一个放置值堆上下文数据的对象。通过符号“#”再加上对象的名称,可以访问这些数据(只可以访问)。一些JavaServlet相关的数据都放在这个容器中。这个对webwork的标签库特别有用,这样我们就可以直接通过表达式语言去访问request,attribute,session,application里的数据。例如:用property标签库打印出所有请求参数的数据:

    <ww:property value="%{#request}" />

    5、其他。“top”是ValueStack里面的关键字,通过它可以找到ValueStack中最上面的那个对象。可以试着打印一下valueStack.findValue("top")。表达式语言除了可以调用基于JavaBean规范的get/set方法之外,还可以调用一般的Java方法(这是需要使用方法的全名,并传入需要的数据),也可以直接访问Java类的静态字段和静态方法。

     

     

     

    Interceptor(拦截器)

    1、一个拦截器就是在xwork.xml文件中定义的一个无状态Java类,它至少要实现XWork的com.opensymphony.xwork.interceptor.Interceptor接口。

    public interface Interceptor extends Serializable {
        void destroy();
     
        void init();
     
        String intercept(ActionInvocation invocation) throws Exception;
    }

    2、实现Interceptor接口的拦截器,代码部分在intercept方法中实现。在intercept方法中,可以直接返回一个Result字符串,这样整个执行直接“短路”,这时Action的execute方法也不会执行(一般很少会这么用)。所以,一般都会在这个方法里调用参数对象invocation的invoke方法,并返回这个方法执行的结果。这样会持续执行后面的拦截器方法以及Action的execute方法等。

    3、大部分的时候,拦截器直接继承WebWork的抽象类com.opensymphony.xwork.interceptor.AroundInterceptor就可以了。这时,需要实现它的before和after方法。Before方法会在Action执行之前调用,after方法在Action执行之后调用。

    4、拦截器的执行顺序。我们可将多个拦截器放一起组装成一个拦截器栈。这样拦截器会按照栈的顺序由上而下执行before方法,所有before方法执行结束,再执行Action的方法,执行Result的方法,再返回执行结果,最后再从下而上执行拦截器的after方法。

    5、拦截器的过滤功能。我们通常会在应用中使用一个通用的定义多个拦截器的拦截器栈。但有些Action方法在调用的时候,不需要要其中的部分拦截器。这时,我们就可以使用拦截器过滤功能。如果拦截器要拥有过滤功能,必须实现抽象类com.opensymphony.xwork.interceptor.MethodFilterInterceptor。这样,拦截器在定义的时候或者在Action引用拦截器栈的时候,我们就可以指定哪些Action方法是需要过滤的,哪些Action是不需要过滤的。

     

     

    WebWork提供的拦截器介绍

    1、  自动为Action设置Http请求数据的拦截器(Parameters Interceptor)。这个拦截器非常方便实用,但完全自动组装对象数据,很可能会带来安全问题。如果Action不需要设置数据,那么这个Action只要实现com.opensymphony.xwork.interceptor.NoParameters接口即可。如果是Action中部分数据需要自动设置,部分数据不允许设置,这样可以实现接口com.opensymphony.xwork.interceptor.ParameterNameAware,可以在这个接口的acceptableParameterName(String parameterName)方法中,定义我们可以接受哪些方法,如果允许只要让这个方法返回True就可以了。

    2、  过虑参数功能的拦截器(Parameter Filter Interceptor)。它可以全局阻止非法或不允许Action访问的参数。可以很好的和上面的组装参数的拦截器一起使用。

    3、  Action设置静态数据的拦截器(Static Parameters Interceptor)。它可以将Action定义的静态<param/>参数,设置到Action中。

    4、  数据验证拦截器(Validation Interceptor)。定义之后,会调用验证文件或实现验证接口com.opensymphony.xwork.Validateable的所有验证。

    5、  验证流程处理拦截器(Workflow Interceptor)。它和上面的拦截器一起使用,处理验证的流程。如果验证通过则继续前进,如果发现有验证错误消息,直接转到Action中定义的输入结果(input)页面。

    6、  类型转换错误处理拦截器()。它首先去取得类型转换的错误消息(主要是由设置Http请求参数的拦截器产生),如果取到错误消息,它会将错误消息传递给实现接口com.opensymphony.xwork.ValidationAware的Action,这样我们可以将这些错误消息暴露到页面中。

    7、  Action链拦截器(Chaining Interceptor)。它是用来拷贝前一个Action的属性数据到当前Action中。它要求前一个Action必须是chain Result(<result type="chain">),这样才能进行Action的数据拷贝。

    8、  防止页面重复提交(或页面重复刷新)拦截器。Token Interceptor和Token Session Interceptor都是防止重复提交的拦截器。不同点是后者在Session存贮了最近一次请求的结果数据。

    9、  文件上传的拦截器(File Upload Interceptor)。实现文件上传的功能。如果有人曾经手工写过文件上传程序,那一定会惊叹于这个拦截器。我们可以在这个拦截器中设定上传文件的大小和类型限制。记得需要第三方的文件上传库的支持,只要在webwork.properties中配置过,并拷贝相应的jar包就可以了。

    10、进度条等待拦截器(Execute and Wait Interceptor)。当Action的执行需要很长实际的时候,我们可以使用这个进度条等待的拦截器。它会将Action放到后台执行,而在前端显示进度条或等待消息提示的页面。

     

     

     

    实例

    本文附带了一个简单的实例案例,详细可参见码云 struts

     

     

     

    接下来要读

    struts2

     

    前进时,请别遗忘了身后的脚印。
  • 相关阅读:
    FusionCharts 2D柱状图和折线图的组合图调试错误
    FusionCharts 2D柱状图和折线图的组合图
    Action写法心得
    SSH2三大框架SQL查询
    JUnit4测试出错(一)
    Java兔子问题
    FusionCharts重写单系列图
    Java中的Calendar方法
    Java的Random总结
    rtsp协议详解
  • 原文地址:https://www.cnblogs.com/liudaihuablogs/p/13462116.html
Copyright © 2011-2022 走看看