`
xltgod
  • 浏览: 16641 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

探究Struts2运行机制:StrutsPrepareAndExecuteFilter 源码剖析

 
阅读更多
一、概述

     Struts2的核心是一个Filter,Action可以脱离web容器,那么是什么让http请求和action关联在一起的,下面我们深入源码来分析下Struts2是如何工作的。
FilterDispatcher API 写道
Deprecated. Since Struts 2.1.3, use StrutsPrepareAndExecuteFilter instead or StrutsPrepareFilter and StrutsExecuteFilter if needing using the ActionContextCleanUp filter in addition to this one



     鉴于常规情况官方推荐使用StrutsPrepareAndExecuteFilter替代FilterDispatcher,我们此文 将剖析StrutsPrepareAndExecuteFilter,其在工程中作为一个Filter配置在web.xml中,配置如下:
Xml代码  收藏代码

    < filter >  
        < filter-name > struts2 </ filter-name >  
        < filter-class > org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter </ filter-class >  
    </ filter >  
    < filter-mapping >  
        < filter-name > struts2 </ filter-name >  
        < url-pattern > /* </ url-pattern >  
    </ filter-mapping >  

[xml] view plaincopy

    <filter> 
        <filter-name>struts2</filter-name> 
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class> 
    </filter> 
    <filter-mapping> 
        <filter-name>struts2</filter-name> 
        <url-pattern>/*</url-pattern> 
    </filter-mapping> 



二、源码属性方法简介

    下面我们研究下StrutsPrepareAndExecuteFilter源码,类的主要信息如下:


属性摘要
protected  List <Pattern > excludedPatterns
          
protected  ExecuteOperations execute
          
protected  PrepareOperations prepare
          



    StrutsPrepareAndExecuteFilter与普通的Filter并无区别,方法除继承自Filter外,仅有一个回调方法,第三部分我 们将按照Filter方法调用顺序,由init—>doFilter—>destroy顺序地分析源码。
方法摘要
void destroy ()
           继承自Filter,用于资源释放
void doFilter (ServletRequest  req, ServletResponse  res, FilterChain  chain) 
           继承自Filter,执行方法
void init (FilterConfig  filterConfig) 
           继承自Filter,初始化参数
protected  void postInit (Dispatcher  dispatcher, FilterConfig  filterConfig)
          Callback for post initialization(一个空的方法,用于方法回调初始化)



三、源码剖析   



    1、init方法

         init是Filter第一个运行的方法,我们看下struts2的核心Filter在调用init方法初始化时做哪些工作:
Java代码  收藏代码

      public   void  init(FilterConfig filterConfig)  throws  ServletException { 
            InitOperations init = new  InitOperations(); 
            try  { 
    //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中  
                FilterHostConfig config = new  FilterHostConfig(filterConfig); 
    // 初始化struts内部日志  
               init.initLogging(config); 
    //<strong>创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源</strong>  
                Dispatcher dispatcher = init.initDispatcher(config); 
                init.initStaticContentLoader(config, dispatcher); 
    //初始化类属性:prepare 、execute   
                prepare = new  PrepareOperations(filterConfig.getServletContext(), dispatcher); 
                execute = new  ExecuteOperations(filterConfig.getServletContext(), dispatcher); 
                this .excludedPatterns = init.buildExcludedPatternsList(dispatcher); 
    //回调空的postInit方法  
                postInit(dispatcher, filterConfig); 
            } finally  { 
                init.cleanup(); 
            } 
     } 

[java] view plaincopy

     public void init(FilterConfig filterConfig) throws ServletException { 
            InitOperations init = new InitOperations(); 
            try { 
    //封装filterConfig,其中有个主要方法getInitParameterNames将参数名字以String格式存储在List中 
                FilterHostConfig config = new FilterHostConfig(filterConfig); 
    // 初始化struts内部日志 
               init.initLogging(config); 
    //<strong>创建dispatcher ,并初始化,这部分下面我们重点分析,初始化时加载那些资源</strong> 
     
                Dispatcher dispatcher = init.initDispatcher(config); 
                init.initStaticContentLoader(config, dispatcher); 
    //初始化类属性:prepare 、execute  
                prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher); 
                execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher); 
                this.excludedPatterns = init.buildExcludedPatternsList(dispatcher); 
    //回调空的postInit方法 
                postInit(dispatcher, filterConfig); 
            } finally { 
                init.cleanup(); 
            } 
     } 



   首先看下FilterHostConfig ,源码如下:


Java代码  收藏代码

    public   class  FilterHostConfig  implements  HostConfig { 
     
        private  FilterConfig config; 
        /** 
         *构造函数   
         */      
        public  FilterHostConfig(FilterConfig config) { 
            this .config = config; 
        } 
        /** 
         *  根据init-param配置的param-name获取param-value的值 
         */    
        public  String getInitParameter(String key) { 
            return  config.getInitParameter(key); 
        } 
           /** 
             *  返回初始化参数名的List 
         */   
        public  Iterator<String> getInitParameterNames() { 
            return  MakeIterator.convert(config.getInitParameterNames()); 
        } 
     
        public  ServletContext getServletContext() { 
            return  config.getServletContext(); 
        } 
    } 

[java] view plaincopy

    public class FilterHostConfig implements HostConfig { 
     
        private FilterConfig config; 
        /**
         *构造函数  
         */     
        public FilterHostConfig(FilterConfig config) { 
            this.config = config; 
        } 
        /**
         *  根据init-param配置的param-name获取param-value的值
         */   
        public String getInitParameter(String key) { 
            return config.getInitParameter(key); 
        } 
           /**
             *  返回初始化参数名的List
         */  
        public Iterator<String> getInitParameterNames() { 
            return MakeIterator.convert(config.getInitParameterNames()); 
        } 
     
        public ServletContext getServletContext() { 
            return config.getServletContext(); 
        } 
    } 

   只有短短的几行代码,getInitParameterNames是这个类的核心,将Filter初始化参数名称有枚举类型转为Iterator。此类的主要作为是对filterConfig 封装。





    重点来了,创建并初始化Dispatcher     
Java代码  收藏代码

    public  Dispatcher initDispatcher( HostConfig filterConfig ) { 
           Dispatcher dispatcher = createDispatcher(filterConfig); 
           dispatcher.init(); 
           return  dispatcher; 
       } 

[java] view plaincopy

    public Dispatcher initDispatcher( HostConfig filterConfig ) { 
           Dispatcher dispatcher = createDispatcher(filterConfig); 
           dispatcher.init(); 
           return dispatcher; 
       } 

     创建Dispatcher,会读取 filterConfig 中的配置信息,将配置信息解析出来,封装成为一个Map,然后根绝servlet上下文和参数Map构造Dispatcher :
Java代码  收藏代码

    private  Dispatcher createDispatcher( HostConfig filterConfig ) { 
            Map<String, String> params = new  HashMap<String, String>(); 
            for  ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) { 
                String name = (String) e.next(); 
                String value = filterConfig.getInitParameter(name); 
                params.put(name, value); 
            } 
            return   new  Dispatcher(filterConfig.getServletContext(), params); 
        } 

[java] view plaincopy

    private Dispatcher createDispatcher( HostConfig filterConfig ) { 
            Map<String, String> params = new HashMap<String, String>(); 
            for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) { 
                String name = (String) e.next(); 
                String value = filterConfig.getInitParameter(name); 
                params.put(name, value); 
            } 
            return new Dispatcher(filterConfig.getServletContext(), params); 
        } 

  Dispatcher初始化,加载struts2的相关配置文件,将按照顺序逐一加载:default.properties,struts-default.xml,struts-plugin.xml,struts.xml,……


Java代码  收藏代码

    /** 
    *初始化过程中依次加载如下配置文件 
    */  
    public   void  init() { 
     
            if  (configurationManager ==  null ) { 
                configurationManager = new  ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); 
            } 
     
            try  { 
                //加载org/apache/struts2/default.properties  
                init_DefaultProperties(); // [1]  
               //加载struts-default.xml,struts-plugin.xml,struts.xml  
                init_TraditionalXmlConfigurations(); // [2]  
                init_LegacyStrutsProperties(); // [3]  
               //用户自己实现的ConfigurationProviders类              
            init_CustomConfigurationProviders(); // [5]  
                //Filter的初始化参数  
            init_FilterInitParameters() ; // [6]  
                init_AliasStandardObjects() ; // [7]  
     
                Container container = init_PreloadConfiguration(); 
                container.inject(this ); 
                init_CheckConfigurationReloading(container); 
                init_CheckWebLogicWorkaround(container); 
     
                if  (!dispatcherListeners.isEmpty()) { 
                    for  (DispatcherListener l : dispatcherListeners) { 
                        l.dispatcherInitialized(this ); 
                    } 
                } 
            } catch  (Exception ex) { 
                if  (LOG.isErrorEnabled()) 
                    LOG.error("Dispatcher initialization failed" , ex); 
                throw   new  StrutsException(ex); 
            } 
        } 

[java] view plaincopy

    /**
    *初始化过程中依次加载如下配置文件
    */ 
    public void init() { 
     
            if (configurationManager == null) { 
                configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME); 
            } 
     
            try { 
                //加载org/apache/struts2/default.properties 
                init_DefaultProperties(); // [1] 
               //加载struts-default.xml,struts-plugin.xml,struts.xml 
                init_TraditionalXmlConfigurations(); // [2] 
                init_LegacyStrutsProperties(); // [3] 
               //用户自己实现的ConfigurationProviders类             
            init_CustomConfigurationProviders(); // [5] 
                //Filter的初始化参数 
            init_FilterInitParameters() ; // [6] 
                init_AliasStandardObjects() ; // [7] 
     
                Container container = init_PreloadConfiguration(); 
                container.inject(this); 
                init_CheckConfigurationReloading(container); 
                init_CheckWebLogicWorkaround(container); 
     
                if (!dispatcherListeners.isEmpty()) { 
                    for (DispatcherListener l : dispatcherListeners) { 
                        l.dispatcherInitialized(this); 
                    } 
                } 
            } catch (Exception ex) { 
                if (LOG.isErrorEnabled()) 
                    LOG.error("Dispatcher initialization failed", ex); 
                throw new StrutsException(ex); 
            } 
        } 



   初始化default.properties,具体的初始化操作在DefaultPropertiesProvider类中

 
Java代码  收藏代码

    private   void  init_DefaultProperties() { 
           configurationManager.addConfigurationProvider(new  DefaultPropertiesProvider()); 
       } 

[java] view plaincopy

    private void init_DefaultProperties() { 
           configurationManager.addConfigurationProvider(new DefaultPropertiesProvider()); 
       } 

   

   下面我们看下DefaultPropertiesProvider类源码:


Java代码  收藏代码

    public   void  register(ContainerBuilder builder, LocatableProperties props) 
                throws  ConfigurationException { 
             
            Settings defaultSettings = null ; 
            try  { 
                defaultSettings = new  PropertiesSettings( "org/apache/struts2/default" ); 
            } catch  (Exception e) { 
                throw   new  ConfigurationException( "Could not find or error in org/apache/struts2/default.properties" , e); 
            } 
             
            loadSettings(props, defaultSettings); 
        } 

[java] view plaincopy

    public void register(ContainerBuilder builder, LocatableProperties props) 
                throws ConfigurationException { 
             
            Settings defaultSettings = null; 
            try { 
                defaultSettings = new PropertiesSettings("org/apache/struts2/default"); 
            } catch (Exception e) { 
                throw new ConfigurationException("Could not find or error in org/apache/struts2/default.properties", e); 
            } 
             
            loadSettings(props, defaultSettings); 
        } 



   其他的我们再次省略,大家可以浏览下各个初始化操作都加载了那些文件


3、doFilter方法

     doFilter是过滤器的执行方法,它拦截提交的HttpServletRequest请求,HttpServletResponse响应,作为strtus2的核心拦截器,在doFilter里面到底做了哪些工作,我们将逐行解读其源码,源码如下:


Java代码  收藏代码

      public   void  doFilter(ServletRequest req, ServletResponse res, FilterChain chain)  throws  IOException, ServletException { 
         //父类向子类转:强转为http请求、响应  
         HttpServletRequest request = (HttpServletRequest) req; 
         HttpServletResponse response = (HttpServletResponse) res; 
     
         try  { 
             //设置编码和国际化  
             prepare.setEncodingAndLocale(request, response); 
              //创建Action上下文(重点)  
             prepare.createActionContext(request, response); 
             prepare.assignDispatcherToThread(); 
    if  ( excludedPatterns !=  null  && prepare.isUrlExcluded(request, excludedPatterns)) { 
        chain.doFilter(request, response); 
    } else  { 
        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); 
         } 
     } 

[java] view plaincopy

     public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 
         //父类向子类转:强转为http请求、响应 
         HttpServletRequest request = (HttpServletRequest) req; 
         HttpServletResponse response = (HttpServletResponse) res; 
     
         try { 
             //设置编码和国际化 
             prepare.setEncodingAndLocale(request, response); 
              //创建Action上下文(重点) 
             prepare.createActionContext(request, response); 
             prepare.assignDispatcherToThread(); 
    if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) { 
        chain.doFilter(request, response); 
    } else { 
        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); 
         } 
     } 



    setEncodingAndLocale调用了dispatcher方法的prepare方法:


Java代码  收藏代码

    /** 
         * Sets the request encoding and locale on the response 
         */  
        public   void  setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) { 
            dispatcher.prepare(request, response); 
        } 

[java] view plaincopy

    /**
         * Sets the request encoding and locale on the response
         */ 
        public void setEncodingAndLocale(HttpServletRequest request, HttpServletResponse response) { 
            dispatcher.prepare(request, response); 
        } 



   下面我们看下prepare方法,这个方法很简单只是设置了encoding 、locale ,做的只是一些辅助的工作:
Java代码  收藏代码

    public   void  prepare(HttpServletRequest request, HttpServletResponse response) { 
            String encoding = null ; 
            if  (defaultEncoding !=  null ) { 
                encoding = defaultEncoding; 
            } 
     
            Locale locale = null ; 
            if  (defaultLocale !=  null ) { 
                locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale()); 
            } 
     
            if  (encoding !=  null ) { 
                try  { 
                    request.setCharacterEncoding(encoding); 
                } catch  (Exception e) { 
                    LOG.error("Error setting character encoding to '"  + encoding +  "' - ignoring." , e); 
                } 
            } 
     
            if  (locale !=  null ) { 
                response.setLocale(locale); 
            } 
     
            if  (paramsWorkaroundEnabled) { 
                request.getParameter("foo" );  // simply read any parameter (existing or not) to "prime" the request  
            } 
        } 

[java] view plaincopy

    public void prepare(HttpServletRequest request, HttpServletResponse response) { 
            String encoding = null; 
            if (defaultEncoding != null) { 
                encoding = defaultEncoding; 
            } 
     
            Locale locale = null; 
            if (defaultLocale != null) { 
                locale = LocalizedTextUtil.localeFromString(defaultLocale, request.getLocale()); 
            } 
     
            if (encoding != null) { 
                try { 
                    request.setCharacterEncoding(encoding); 
                } catch (Exception e) { 
                    LOG.error("Error setting character encoding to '" + encoding + "' - ignoring.", e); 
                } 
            } 
     
            if (locale != null) { 
                response.setLocale(locale); 
            } 
     
            if (paramsWorkaroundEnabled) { 
                request.getParameter("foo"); // simply read any parameter (existing or not) to "prime" the request 
            } 
        } 



   Action上下文创建(重点)

       ActionContext是一个容器,这个容易主要存储request、session、application、parameters等相关信 息.ActionContext是一个线程的本地变量,这意味着不同的action之间不会共享ActionContext,所以也不用考虑线程安全问 题。其实质是一个Map,key是标示request、session、……的字符串,值是其对应的对象:
Java代码  收藏代码

    static  ThreadLocal actionContext =  new  ThreadLocal(); 
    Map<String, Object> context; 

[java] view plaincopy

    static ThreadLocal actionContext = new ThreadLocal(); 
    Map<String, Object> context; 


   下面我们看下如何创建action上下文的,代码如下:


Java代码  收藏代码

    /** 
    *创建Action上下文,初始化thread local 
    */  
    public  ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) { 
        ActionContext ctx; 
        Integer counter = 1 ; 
        Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER); 
        if  (oldCounter !=  null ) { 
            counter = oldCounter + 1 ; 
        } 
        //注意此处是从ThreadLocal中获取此ActionContext变量  
        ActionContext oldContext = ActionContext.getContext(); 
        if  (oldContext !=  null ) { 
            // detected existing context, so we are probably in a forward  
            ctx = new  ActionContext( new  HashMap<String, Object>(oldContext.getContextMap())); 
        } else  { 
            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class ).createValueStack(); 
            stack.getContext().putAll(dispatcher.createContextMap(request, response, null , servletContext)); 
            //stack.getContext()返回的是一个Map<String,Object>,根据此Map构造一个ActionContext  
            ctx = new  ActionContext(stack.getContext()); 
        } 
        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter); 
        //将ActionContext存如ThreadLocal  
        ActionContext.setContext(ctx); 
        return  ctx; 
    } 

[java] view plaincopy

    /**
    *创建Action上下文,初始化thread local
    */ 
    public ActionContext createActionContext(HttpServletRequest request, HttpServletResponse response) { 
        ActionContext ctx; 
        Integer counter = 1; 
        Integer oldCounter = (Integer) request.getAttribute(CLEANUP_RECURSION_COUNTER); 
        if (oldCounter != null) { 
            counter = oldCounter + 1; 
        } 
        //注意此处是从ThreadLocal中获取此ActionContext变量 
        ActionContext oldContext = ActionContext.getContext(); 
        if (oldContext != null) { 
            // detected existing context, so we are probably in a forward 
            ctx = new ActionContext(new HashMap<String, Object>(oldContext.getContextMap())); 
        } else { 
            ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack(); 
            stack.getContext().putAll(dispatcher.createContextMap(request, response, null, servletContext)); 
            //stack.getContext()返回的是一个Map<String,Object>,根据此Map构造一个ActionContext 
            ctx = new ActionContext(stack.getContext()); 
        } 
        request.setAttribute(CLEANUP_RECURSION_COUNTER, counter); 
        //将ActionContext存如ThreadLocal 
        ActionContext.setContext(ctx); 
        return ctx; 
    } 



    上面代码中dispatcher.createContextMap,如何封装相关参数:


Java代码  收藏代码

    public  Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response, 
                ActionMapping mapping, ServletContext context) { 
     
            // request map wrapping the http request objects  
            Map requestMap = new  RequestMap(request); 
     
            // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately  
            Map params = new  HashMap(request.getParameterMap()); 
     
            // session map wrapping the http session  
            Map session = new  SessionMap(request); 
     
            // application map wrapping the ServletContext  
            Map application = new  ApplicationMap(context); 
                    //requestMap、params、session等Map封装成为一个上下文Map,逐个调用了map.put(Map p).  
            Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context); 
     
            if  (mapping !=  null ) { 
                extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); 
            } 
            return  extraContext; 
    } 

[java] view plaincopy

    public Map<String,Object> createContextMap(HttpServletRequest request, HttpServletResponse response, 
                ActionMapping mapping, ServletContext context) { 
     
            // request map wrapping the http request objects 
            Map requestMap = new RequestMap(request); 
     
            // parameters map wrapping the http parameters.  ActionMapping parameters are now handled and applied separately 
            Map params = new HashMap(request.getParameterMap()); 
     
            // session map wrapping the http session 
            Map session = new SessionMap(request); 
     
            // application map wrapping the ServletContext 
            Map application = new ApplicationMap(context); 
                    //requestMap、params、session等Map封装成为一个上下文Map,逐个调用了map.put(Map p). 
            Map<String,Object> extraContext = createContextMap(requestMap, params, session, application, request, response, context); 
     
            if (mapping != null) { 
                extraContext.put(ServletActionContext.ACTION_MAPPING, mapping); 
            } 
            return extraContext; 
    } 



我们简单看下RequestMap,其他的省略。RequestMap类实现了抽象Map,故其本身是一个Map,主要方法实现:
Java代码  收藏代码

    //map的get实现  
    public  Object get(Object key) { 
        return  request.getAttribute(key.toString()); 
    } 
    //map的put实现  
    public  Object put(Object key, Object value) { 
        Object oldValue = get(key); 
        entries = null ; 
        request.setAttribute(key.toString(), value); 
        return  oldValue; 
    } 

[java] view plaincopy

    //map的get实现 
    public Object get(Object key) { 
        return request.getAttribute(key.toString()); 
    } 
    //map的put实现 
    public Object put(Object key, Object value) { 
        Object oldValue = get(key); 
        entries = null; 
        request.setAttribute(key.toString(), value); 
        return oldValue; 
    } 



   下面是源码展示了如何执行Action控制器:


Java代码  收藏代码

    public   void  executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)  throws  ServletException { 
        dispatcher.serviceAction(request, response, servletContext, mapping); 
    } 
     
        public   void  serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, 
                                  ActionMapping mapping) throws  ServletException { 
                    //封装执行的上下文环境,主要讲相关信息存储入map  
            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  { 
                UtilTimerStack.push(timerKey); 
                //获取命名空间  
                String namespace = mapping.getNamespace(); 
                //获取action配置的name属性  
                String name = mapping.getName(); 
                //获取action配置的method属性  
                String method = mapping.getMethod(); 
     
                Configuration config = configurationManager.getConfiguration(); 
                //根据执行上下文参数,命名空间,名称等创建用户自定义Action的代理对象  
                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!  
                        //执行execute方法,并转向结果  
                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/n"  + reqStr, e); 
                } 
                else  { 
                    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); 
            } 
        } 

[java] view plaincopy

    public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException { 
        dispatcher.serviceAction(request, response, servletContext, mapping); 
    } 
     
        public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context, 
                                  ActionMapping mapping) throws ServletException { 
                    //封装执行的上下文环境,主要讲相关信息存储入map 
            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 { 
                UtilTimerStack.push(timerKey); 
                //获取命名空间 
                String namespace = mapping.getNamespace(); 
                //获取action配置的name属性 
                String name = mapping.getName(); 
                //获取action配置的method属性 
                String method = mapping.getMethod(); 
     
                Configuration config = configurationManager.getConfiguration(); 
                //根据执行上下文参数,命名空间,名称等创建用户自定义Action的代理对象 
                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! 
                        //执行execute方法,并转向结果 
                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/n" + reqStr, e); 
                } 
                else { 
                    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); 
            } 
        } 



   文中对如何解析Struts.xml,如何将URL与action映射匹配为分析,有需要的我后续补全,因为 StrutsXmlConfigurationProvider继承XmlConfigurationProvider,并在register方法回调父 类的register,有兴趣的可以深入阅读下下XmlConfigurationProvider源码:


Java代码  收藏代码

    public   void  register(ContainerBuilder containerBuilder, LocatableProperties props)  throws  ConfigurationException { 
           if  (servletContext !=  null  && !containerBuilder.contains(ServletContext. class )) { 
               containerBuilder.factory(ServletContext.class ,  new  Factory<ServletContext>() { 
                   public  ServletContext create(Context context)  throws  Exception { 
                       return  servletContext; 
                   } 
               }); 
           } 
           //调用父类的register,关键点所在  
           super .register(containerBuilder, props); 
       } 

[java] view plaincopy

    public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException { 
           if (servletContext != null && !containerBuilder.contains(ServletContext.class)) { 
               containerBuilder.factory(ServletContext.class, new Factory<ServletContext>() { 
                   public ServletContext create(Context context) throws Exception { 
                       return servletContext; 
                   } 
               }); 
           } 
           //调用父类的register,关键点所在 
           super.register(containerBuilder, props); 
       } 





     struts2-core-2.2.1.jar包中struts-2.1.7.dtd对于Action的定义如下:
Xml代码  收藏代码

    <!ELEMENT action (param|result|interceptor-ref|exception-mapping)* >  
    <!ATTLIST action 
        name CDATA #REQUIRED 
        class CDATA #IMPLIED 
        method CDATA #IMPLIED 
        converter CDATA #IMPLIED 
    >  

[xml] view plaincopy

    <!ELEMENT action (param|result|interceptor-ref|exception-mapping)*> 
    <!ATTLIST action 
        name CDATA #REQUIRED 
        class CDATA #IMPLIED 
        method CDATA #IMPLIED 
        converter CDATA #IMPLIED 
    > 

    从上述DTD中可见Action元素可以含有name 、class 、method 、converter 属性。



   XmlConfigurationProvider解析struts.xml配置的Action元素:
Java代码  收藏代码

    protected   void  addAction(Element actionElement, PackageConfig.Builder packageContext)  throws  ConfigurationException { 
         String name = actionElement.getAttribute("name" ); 
         String className = actionElement.getAttribute("class" ); 
         String methodName = actionElement.getAttribute("method" ); 
         Location location = DomHelper.getLocationObject(actionElement); 
     
         if  (location ==  null ) { 
             LOG.warn("location null for "  + className); 
         } 
         //methodName should be null if it's not set  
         methodName = (methodName.trim().length() > 0 ) ? methodName.trim() :  null ; 
     
         // if there isnt a class name specified for an <action/> then try to  
         // use the default-class-ref from the <package/>  
         if  (StringUtils.isEmpty(className)) { 
             // if there is a package default-class-ref use that, otherwise use action support  
            /* if (StringUtils.isNotEmpty(packageContext.getDefaultClassRef())) { 
                 className = packageContext.getDefaultClassRef(); 
             } else { 
                 className = ActionSupport.class.getName(); 
             }*/  
     
         } else  { 
             if  (!verifyAction(className, name, location)) { 
                 if  (LOG.isErrorEnabled()) 
                     LOG.error("Unable to verify action [#0] with class [#1], from [#2]" , name, className, location.toString()); 
                 return ; 
             } 
         } 
     
     
     
         Map<String, ResultConfig> results; 
         try  { 
             results = buildResults(actionElement, packageContext); 
         } catch  (ConfigurationException e) { 
             throw   new  ConfigurationException( "Error building results for action "  + name +  " in namespace "  + packageContext.getNamespace(), e, actionElement); 
         } 
     
         List<InterceptorMapping> interceptorList = buildInterceptorList(actionElement, packageContext); 
     
         List<ExceptionMappingConfig> exceptionMappings = buildExceptionMappings(actionElement, packageContext); 
     
         ActionConfig actionConfig = new  ActionConfig.Builder(packageContext.getName(), name, className) 
                 .methodName(methodName) 
                 .addResultConfigs(results) 
                 .addInterceptors(interceptorList) 
                 .addExceptionMappings(exceptionMappings) 
                 .addParams(XmlHelper.getParams(actionElement)) 
                 .location(location) 
                 .build(); 
         packageContext.addActionConfig(name, actionConfig); 
     
         if  (LOG.isDebugEnabled()) { 
             LOG.debug("Loaded "  + (StringUtils.isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace() +  "/" ) :  "" ) + name +  " in '"  + packageContext.getName() +  "' package:"  + actionConfig); 
         } 
     } 

[java] view plaincopy

    protected void addAction(Element actionElement, PackageConfig.Builder packageContext) throws ConfigurationException { 
         String name = actionElement.getAttribute("name"); 
         String className = actionElement.getAttribute("class"); 
         String methodName = actionElement.getAttribute("method"); 
         Location location = DomHelper.getLocationObject(actionElement); 
     
         if (location == null) { 
             LOG.warn("location null for " + className); 
         } 
         //methodName should be null if it's not set 
         methodName = (methodName.trim().length() > 0) ? methodName.trim() : null; 
     
         // if there isnt a class name specified for an <action/> then try to 
         // use the default-class-ref from the <package/> 
         if (StringUtils.isEmpty(className)) { 
             // if there is a package default-class-ref use that, otherwise use action support 
            /* if (StringUtils.isNotEmpty(packageContext.getDefaultClassRef())) {
                 className = packageContext.getDefaultClassRef();
             } else {
                 className = ActionSupport.class.getName();
             }*/ 
     
         } else { 
             if (!verifyAction(className, name, location)) { 
                 if (LOG.isErrorEnabled()) 
                     LOG.error("Unable to verify action [#0] with class [#1], from [#2]", name, className, location.toString()); 
                 return; 
             } 
         } 
     
     
     
         Map<String, ResultConfig> results; 
         try { 
             results = buildResults(actionElement, packageContext); 
         } catch (ConfigurationException e) { 
             throw new ConfigurationException("Error building results for action " + name + " in namespace " + packageContext.getNamespace(), e, actionElement); 
         } 
     
         List<InterceptorMapping> interceptorList = buildInterceptorList(actionElement, packageContext); 
     
         List<ExceptionMappingConfig> exceptionMappings = buildExceptionMappings(actionElement, packageContext); 
     
         ActionConfig actionConfig = new ActionConfig.Builder(packageContext.getName(), name, className) 
                 .methodName(methodName) 
                 .addResultConfigs(results) 
                 .addInterceptors(interceptorList) 
                 .addExceptionMappings(exceptionMappings) 
                 .addParams(XmlHelper.getParams(actionElement)) 
                 .location(location) 
                 .build(); 
         packageContext.addActionConfig(name, actionConfig); 
     
         if (LOG.isDebugEnabled()) { 
             LOG.debug("Loaded " + (StringUtils.isNotEmpty(packageContext.getNamespace()) ? (packageContext.getNamespace() + "/") : "") + name + " in '" + packageContext.getName() + "' package:" + actionConfig); 
         } 
     } 





     工作中不涉及Struts2,本周工作有个2天的空档期,稍微看了下struts2的文档,写了个demo,从源码的角度研究了下运行原理,如有分析不当请指出,我后续逐步完善更正,大家共同提高。
分享到:
评论

相关推荐

    struts2运行机制

    ### Struts2运行机制详解 #### 一、Struts2框架概述 Struts2是一个基于MVC(Model-View-Controller)设计模式的Java Web应用框架,它为开发者提供了构建可扩展、易于维护的Web应用程序的工具。Struts2不仅继承了...

    StrutsPrepareAndExecuteFilter源码剖析

    析&lt;/STRONG&gt; 9....通过分析其源码,我们可以深入了解 Struts2 的工作原理,包括请求处理流程、拦截器机制、配置加载等关键环节。这对于开发者来说,无论是排查问题还是优化性能,都有着重要的指导意义。

    struts2-core-2.0.11源码

    9. **类型转换(Type Conversion)**:Struts2提供了一套强大的类型转换机制,位于`org.apache.struts2.convention.converters`包下,可以自动将请求参数转换为Action属性的期望类型。 10. **注解支持(Annotations...

    struts2-core-2.3.7源码

    7. **Plug-in架构**: Struts2的插件机制允许扩展其功能,如添加新的拦截器、结果类型等。 在`struts2-core-2.3.7`版本中,我们可以看到以下几个关键组件: 1. **StrutsPrepareAndExecuteFilter**: 这是Struts2的...

    跟我一起学struts2之一:环境搭建篇

    &lt;filter-class&gt;org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter &lt;filter-name&gt;struts2 &lt;url-pattern&gt;/* ``` 5. **创建第一个Action**:在src/main/java目录下,创建一个Action类,...

    Struts2教程(完整)

    - **MVC框架**:Struts2属于MVC(Model-View-Controller)框架,这类框架主要包括Struts、WebWork、SpringMVC等。 - **事件驱动框架**:如JSF(JavaServer Faces)、Tapestry等。 - **Struts2的历史背景**: - *...

    struts2入门小案例源码

    1. **struts-default.xml**:Struts2的默认配置文件,定义了一些基本的行为和拦截器。通常我们不需要修改这个文件。 2. **struts.xml**:这是用户自定义的配置文件,用来定义Action、结果类型、拦截器栈等。比如,...

    struts2案例 struts2 struts2源码

    3. **OGNL表达式**:Struts2默认使用OGNL(Object-Graph Navigation Language)作为视图和模型之间的数据绑定语言,用于传递参数和获取对象属性。 4. **国际化与本地化**:通过资源包(properties文件)实现多语言...

    struts2源码最新

    7. **异常处理**:Struts2提供了全局的异常处理机制,当Action执行过程中抛出异常时,可以统一处理并跳转到特定的错误页面。 在深入研究Struts2源码时,我们可以关注以下几个关键组件: - **...

    Struts2: HelloWord

    1. **配置Struts2**: 在`web.xml`中配置Struts2的前端控制器`FilterDispatcher`或`StrutsPrepareAndExecuteFilter`。 2. **创建Action类**: 创建一个Java类,该类扩展了`ActionSupport`类,并实现了一个返回...

    struts2 源码绝对完整

    5. **Struts2配置**:Struts2的配置文件(通常为struts.xml)用于定义Action、Interceptor和Result的配置,使得开发者可以通过配置文件灵活地调整框架行为。 6. **Freemarker或Velocity模板引擎**:Struts2支持多种...

    struts2最新版源码文件

    负责创建并执行Action,`org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter`是Struts2的过滤器,它在Servlet容器中启动和管理Struts2的工作流程。 此外,`struts2-convention-plugin`提供了...

    手动搭建struts2环境

    1. **JDK**:Struts2是Java平台上的框架,因此需要安装Java Development Kit(JDK)并设置好`JAVA_HOME`环境变量。 2. **Apache Tomcat**:Tomcat是一个轻量级的Java应用服务器,用来运行我们的Struts2应用程序。...

    Struts2 src 源码下载

    5. **Freemarker和Velocity**:Struts2支持多种视图技术,包括FreeMarker和Velocity模板引擎,你可以看到它们的配置和使用方式。 6. **Tiles**:Struts2集成了Tiles框架,使得布局和复用视图变得简单。 7. **OGNL...

    完美版(适合自学)struts2

    - web.xml:这是应用的部署描述符,需要配置StrutsPrepareAndExecuteFilter,它作为过滤器初始化Struts2框架,并处理所有请求。ActionContextCleanUp过滤器则负责清理工作。 - struts.xml:这是框架的核心配置文件,...

    struts2项目部署

    - `struts.xml`:Struts2的配置文件,定义了Action、结果类型、拦截器等。 3. **lib目录**:包含所有必要的库文件,包括Struts2框架的JAR包和其他依赖库。 4. **WebContent或html目录**:存放静态资源,如HTML...

    Struts2 运行流程分析

    4. **ActionMapper**:Struts2会使用`ActionMapper`来解析请求URL,并确定相应的Action类和方法。URL中的部分可以映射到Action的名称和方法名称。 5. **ActionProxy创建**:根据ActionMapper的结果,框架会创建一个...

    struts 2 源码 导入eclipse工程

    7. **Plug-in体系结构**:Struts 2支持插件化开发,通过StrutsPrepareAndExecuteFilter和相应的插件,可以轻松地扩展框架功能。 8. **生命周期与工作流程**:当一个HTTP请求到达时,Struts 2会通过...

    struts2环境搭建

    1. Java Development Kit (JDK):Struts2运行在Java平台上,所以你需要先安装JDK并配置好`JAVA_HOME`环境变量。 2. Apache Tomcat:一个流行的Java应用服务器,用于部署和运行web应用程序。下载并解压Tomcat到你选择...

    struts2核心文件

    Struts2的核心jar包包含了框架运行所需的所有基本组件和服务。这些组件和服务包括但不限于: 1. **Action类和ActionSupport**: Action是Struts2的核心组件,负责处理用户请求并执行业务逻辑。ActionSupport是Action...

Global site tag (gtag.js) - Google Analytics