zoukankan      html  css  js  c++  java
  • Struct2

    Struct2

     

    推荐文章:

    官网

    struts2:

    https://www.cnblogs.com/mfmdaoyou/p/7189578.html

    IBM:

    https://www.ibm.com/developerworks/cn/java/j-lo-struct2/index.html?mhq=struts2%E5%8E%9F%E7%90%86&mhsrc=ibmsearch_a

    WebWork:

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

    刚开始出来找工作时,自己很菜,那时候在网上看了一系列的视频,去学习struts2、spring、spring mvc,但是也就只是会用,没有一点深入,现在在我打实了servlet基础后,打算深入学习一下struts,虽然说这东西可能算是有些过时了,但是经典必然有其经典之处,希望自己能有较多收获。

     

    发展历程

    工作流程

    流程

    源码分析

    实例

    接下来要读

     

    webwork

      

    struts2(2.1.3之前的版本使用的是FilterDispatcher,在其之后开始使用StrutsPrepareAndExecuteFilter或者StrutsPrepareFilter和StrutsExecuteFilterif。除此之外,还使用ActionContextCleanUp过滤器)

     

     

     

    发展历程

    一门技术的出现绝非偶然,肯定是原来的东西让人使用起来不方便。学习了Servlet之后,我并没有开始继续探索JSP技术,原因大概就是在我眼中JSP就是View视图,MVC思想已经存在了我的脑海里,所以我不直接去将大量的Java代码嵌入JSP中,但是在开始Struts时我还真的又沿着这条历史发展线路,回头看了一下JSP技术。

    JSP技术是在Servlet之后产生的,原因就是使用Servlet的PrintWriter一直向页面输出HTML标签过于麻烦,难受,才有了JSP。JSP最终是要被编译成Servlet才能处理用户请求,因此JSP拥有Servlet的所有功能和特性。既然JSP拥有了Servlet的所有功能和特性,这样你就可以在一个JSP中即处理业务逻辑又能显示到页面,不将Servlet和JSP划分开也行。那样的话JSP中视图和处理业务(操作bean)的代码将混杂在一起,某个JSP可能并不做展示用,而仅仅是操作javaBean,基于JSP的Web应用程序有时混合了数据库代码,页面设计代码和控制流代码,很难维护,这点你要回顾一下JSP笔记中的MVC发展线。oracle将MVC思想引入到java中,在web开发层面使层与层分开,各司其职,逻辑会更清晰。

    struts2并不是是在MVC思想蔓延后的第一个mvc框架,但是它有许多优秀的理念值得学习。在学习struts2之前,你至少要深入的了解一下struts1,webwork是帮助你学习struts2不可缺少的一部分,你也该去了解一下。

     

    工作流程

     

    流程

     初始的请求通过一条标准的过滤器链,到达 servlet 容器 ( 比如 tomcat 容器,WebSphere 容器 )。

     过滤器链包括可选的 ActionContextCleanUp 过滤器,用于系统整合技术,如 SiteMesh 插件。

     接着调用 FilterDispatcher,FilterDispatcher 查找 ActionMapper,以确定这个请求是否需要调用某个 Action。

     如果 ActionMapper 确定需要调用某个 Action,FilterDispatcher 将控制权交给 ActionProxy。

     ActionProxy 依照框架的配置文件(struts.xml),找到需要调用的 Action 类。

     ActionProxy 创建一个 ActionInvocation 的实例。ActionInvocation 先调用相关的拦截器 (Action 调用之前的部分),最后调用 Action。

     一旦 Action 调用返回结果,ActionInvocation 根据 struts.xml 配置文件,查找对应的转发路径。返回结果通常是(但不总是,也可能是另外的一个 Action 链)JSP 技术或者 FreeMarker 的模版技术的网页呈现。Struts2 的标签和其他视图层组件,帮助呈现我们所需要的显示结果。在此,我想说清楚一些,最终的显示结果一定是 HTML 标签。标签库技术和其他视图层技术只是为了动态生成 HTML 标签。

     接着按照相反次序执行拦截器链 ( 执行 Action 调用之后的部分 )。最后,响应通过滤器链返回(过滤器技术执行流程与拦截器一样,都是先执行前面部分,后执行后面部)。如果过滤器链中存在 ActionContextCleanUp,FilterDispatcher 不会清理线程局部的 ActionContext。如果不存在 ActionContextCleanUp 过滤器,FilterDispatcher 会清除所有线程局部变量。

     

    理解

    客户端初始化一个指向Servlet容器(例如Tomcat)的请求request

    这个请求经过一系列的过滤器(Filter)。这些过滤器中有一个叫做ActionContextCleanUp的可选过滤器,这个过滤器对于Struts2和其他框架的集成很有帮助,例如:SiteMesh Plugin。

    接着StrutsPrepareAndExecuteFilter被调用,StrutsPrepareAndExecuteFilter询问ActionMapper来决定这个请求是否需要调用某个Action;

    如果ActionMapper决定需要调用某个Action,StrutsPrepareAndExecuteFilter把请求的处理交给ActionProxy;

    ActionProxy通过ConfigurationManager询问框架的配置文件,找到需要调用的Action类;

    ActionProxy创建一个ActionInvocation的实例。

    ActionInvocation实例使用命名模式来调用,在调用Action的过程前后,涉及到相关拦截器(Intercepter)的调用。

    一旦Action执行完毕,ActionInvocation负责根据struts.xml中的配置找到对应的返回结果

     

     

    源码分析

    struts2.3.4版本分析。关于StrutsPrepareAndExecuteFilter的功能说明:处理Struts调度过程的准备和执行阶段。下面是该过滤器大致代码:

    /**
    * 处理Struts调度过程的准备和执行阶段。
    * 当你没有其他需要访问操作上下文信息(如SiteMesh)的筛选器时,
    * 这个过滤器比较好用。
    */
    public class StrutsPrepareAndExecuteFilter implements StrutsStatics, Filter {
        protected PrepareOperations prepare;
        protected ExecuteOperations execute;
        protected List<Pattern> excludedPatterns = null;
    
        public void init(FilterConfig filterConfig) throws ServletException {
            /**
            * 创建一个专门用于初始化操作的对象
            */
            InitOperations init = new InitOperations();
            try {
                // 包装filterconfig的主机配置
                FilterHostConfig config = new FilterHostConfig(filterConfig);
                /**
                * 从web.xml的filter配置中读取param-name=loggerFactory的信息
                * ,初始化日志
                */
                init.initLogging(config);
                /**
                * 初始化转发器。读取web.xml内所有的配置信息filter,filter-mapping
                * 在这里filter-mapping通常配置的是拦截所有,也就是说让转发器处理
                * 所有请求。
                */
                Dispatcher dispatcher = init.initDispatcher(config);
                /**
                * 使用带过滤器配置的加载程序初始化静态内容
                */
                init.initStaticContentLoader(config, dispatcher);
                
                /**
                * 使用dispatcher和servletcontext创建两个全局对象,备用
                *   记住:prepare和execute里面最开始时装的内容是一样的,
                *   都是dispatcher和servletContext
                */
                prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
                execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
                this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
    
                postInit(dispatcher, filterConfig);
            } finally {
                // 清掉ActionContext
                init.cleanup();
            }
    
        }
    
        //  no content in postInit method
    
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
    
            try {
                // 给dispatcher设置Encoding
                prepare.setEncodingAndLocale(request, response);
                
                /**
                * 创建操作上下文并初始化本地线程
                *   1、每一个请求过来都会创建一个Action上下文,Action上下文中包
                *      含一些Servlet的对象,比如:request,session等等。
                *   2、初始化一个值堆栈(Action不仅作为控制器,还被当作一个实体)
                *      值堆栈中存储的是该实体的相关属性
                *   3、将Action上下文也放入值堆栈中
                */
                prepare.createActionContext(request, response);
                
                // 将调度程序分配给调度程序线程本地
                prepare.assignDispatcherToThread();
                
                /**
                * 如果只在web.xml中配置了filter-mapping.url-pattern=/*,那么
                * StrutsPrepareAndExecuteFilter会拦截所有的请求(也就是下面只走else)
                * 但是如果你在struts.xml中配置了struts.action.excludePattern,那么
                * 当一个请求匹配上了excludePattern中的正则表达式,就不会在让
                * StrutsPrepareAndExecuteFilter去处理这个请求了。我尝试在
                * struts.xml配置了如下项:
                *     <constant name="struts.action.excludePattern" value="/*.jsp"></constant>
                * debug启动后,在如下if判断里的isUrlExcluded处打个断点
                * 然后访问项目:8080/Struts2Base/index.jsp,在诡异的一幕出现了,
                * 你会发现它明明执行了isUrlExcluded方法中的return true,但是
                * 最后又执行了这个方法里最后的return false.这简直让我难以理解。其实
                * 我在struts.xml配置excludePattern只是为了帮助这个Filter省力
                * 而已,因为我看到它会在else里处理对静态文件的请求(先去找有没
                * 有Action,在去看看是不是静态资源),所以我配置了excludePattern,
                * 对于静态文件让它直接去找,这样就不必在去找一遍Action了。然而我
                * 得到的是诡异的true-false.
                *
                * 当一个请求过来,它会先去检查这个请求是否与“排除列表”中的某个
                * 后缀匹配,如果匹配就直接通过(chain.doFilter),不匹配就进入else。
                */
            if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                chain.doFilter(request, response);
            } else {
              /**
              * 包装request请求,以访它是一个multipart/form-data请求。
              * 从Mapping列表中找有没有该request请求的Mapping,如果
              * 有,就交给这个这个Action去执行。没有的话,看看它访问的是不是静态
              * 资源,如果是就不处理,交给后面的chain.访问的也不是静态资源,
              * 那就不鸟这个请求。
              */
                request = prepare.wrapRequest(request);
                ActionMapping mapping = prepare.findActionMapping(request, response, true);
                if (mapping == null) {
                    boolean handled = execute.executeStaticResourceRequest(request, response);
                    if (!handled) {
                        chain.doFilter(request, response);
                    }
                } else {
                               // 更核心的在这里
                    execute.executeAction(request, response, mapping);
                }
            }
            } finally {
                // 清理一下这个请求(无状态的)
                prepare.cleanupRequest(request);
            }
        }
        // destroy method...
    }
    // 在上面execute.executeAction->serviceAction
    
        /**
         * Load Action类用于映射和调用相应的Action方法,或直接转到Result。
         * 此方法首先从给定参数创建操作上下文,然后从给定操作名称和命名空
         * 间加载ActionProxy。之后,执行Action方法并通过响应对象输出通道。
         * 未找到的操作将使用404返回代码通过Dispatcher#sendError方法发回给用户。
         * 抛出ServletException报告所有其他错误。
         */
        public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                                  ActionMapping mapping) throws ServletException {
            // 创建ContextMap和值栈这些东西基本上和WebWork是大差不差的,不罗嗦了。
            Map<String, Object> extraContext = createContextMap(request, response, mapping, context);
    
            // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
            ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
            boolean nullStack = stack == null;
            if (nullStack) {
                ActionContext ctx = ActionContext.getContext();
                if (ctx != null) {
                    stack = ctx.getValueStack();
                }
            }
            if (stack != null) {
                extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
            }
    
            String timerKey = "Handling request from Dispatcher";
            try {
                // 获取Action信息,通过反射创建代理,设置被调用的方法
                UtilTimerStack.push(timerKey);
                String namespace = mapping.getNamespace();
                String name = mapping.getName();
                String method = mapping.getMethod();
    
                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();
                }
    
                // If there was a previous value stack then set it back onto the request
                if (!nullStack) {
                    request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
                }
            } catch (ConfigurationException e) {
                // WW-2874 Only log error if in devMode
                if(devMode) {
                    String reqStr = request.getRequestURI();
                    if (request.getQueryString() != null) {
                        reqStr = reqStr + "?" + request.getQueryString();
                    }
                    LOG.error("Could not find action or result
    " + reqStr, e);
                }
                else {
                        if (LOG.isWarnEnabled()) {
                    LOG.warn("Could not find action or result", e);
                        }
                }
                sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
            } catch (Exception e) {
                sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
            } finally {
                UtilTimerStack.pop(timerKey);
            }
        }

    如果你认真看过WebWork的源码并分析过后,你会发现为什么说Struts2是包装了WebWork核心的外壳了,Strut拥有强大的社区,WebWork有先进的技术,强强联手。

    对照着架构蓝图,阅读起代码来,相对好理解很多。要补充的是在Webwork中一些关于

    # webwork.properties
    webwork.devMode=true
    webwork.action.extension=action,do

    等设置是放在webwork.properties里的,在struts2中将这些设置用constant标签包装了一下,放在了struts.xml里。

    # struts.xml
    <struts>
    
        <constant name="struts.devMode" value="true" />
        
        <constant name="struts.action.excludePattern" value="/*.jsp"></constant>
    
        <package name="basicstruts2" extends="struts-default">
            <action name="index">
                <result>/index.jsp</result>
            </action>

    更多关于值栈等内容,可以参见webwork,想深入学习struts2就在回头去探索webwork吧!

     

    实例

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

    官网上也有不少实例,比较推荐官网上,自己动手去搭建的。

     

    接下来要读

    spring技术

     

    前进时,请别遗忘了身后的脚印。
  • 相关阅读:
    团队项目-项目进度
    团队项目-Recycle项目文档
    团队项目-Recycle需求规格说明书
    团队项目-初步构想
    关于 Localhost:8080/hello
    Java EE课程作业 (Seventh)-- Async
    Java EE 课程作业 (Sixth)-- Filter分析
    Java EE 课程作业(Fifth)-- XML:定义 用途 工作原理及未来xml
    Java EE 课程作业(Fourth)-- Session
    Java EE 课程作业(third)- 关于 Java EE的思维导图
  • 原文地址:https://www.cnblogs.com/liudaihuablogs/p/13462183.html
Copyright © 2011-2022 走看看