struts2源代码分析

1. Struts2架构图

请求首先通过Filter chainFilter主要包括ActionContextCleanUp,它主要清理当前线程的ActionContextDispatcherFilterDispatcher主要通过AcionMapper来决定需要调用哪个Action

        ActionMapper取得了ActionMapping后,在DispatcherserviceAction方法里创建ActionProxyActionProxy创建ActionInvocation,然后ActionInvocation调用Interceptors,执行Action本身,创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener

 

2. Struts2部分类介绍

这部分从Struts2参考文档中翻译就可以了。

ActionMapper

        ActionMapper其实是HttpServletRequestAction调用请求的一个映射,它屏蔽了Action对于Requestjava Servlet类的依赖。Struts2中它的默认实现类是DefaultActionMapperActionMapper很大的用处可以根据自己的需要来设计url格式,它自己也有Restful的实现,具体可以参考文档的docs\actionmapper.html

ActionProxy&ActionInvocation

        Action的一个代理,由ActionProxyFactory创建,它本身不包括Action实例,默认实现DefaultActionProxy是由ActionInvocation持有Action实例。ActionProxy作用是如何取得Action,无论是本地还是远程。而ActionInvocation的作用是如何执行Action,拦截器的功能就是在ActionInvocation中实现的。

ConfigurationProvider&Configuration

        ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其实现类XmlConfigurationProvider及其子类StrutsXmlConfigurationProvider来解析。

 

3. Struts2请求流程

1、客户端发送请求

2、请求先通过ActionContextCleanUp-->FilterDispatcher

3FilterDispatcher通过ActionMapper来决定这个Request需要调用哪个Action

4、如果ActionMapper决定调用某个ActionFilterDispatcher把请求的处理交给ActionProxy,这儿已经转到它的Delegate--Dispatcher来执行

5ActionProxy根据ActionMappingConfigurationManager找到需要调用的Action

6ActionProxy创建一个ActionInvocation的实例

7ActionInvocation调用真正的Action,当然这涉及到相关拦截器的调用

8Action执行完毕,ActionInvocation创建Result并返回,当然,如果要在返回之前做些什么,可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现。

 

首先强调一下struts2的线程程安全,在Struts2中大量采用ThreadLocal线程局部变量的方法来保证线程的安全,Dispatcher等都是通过ThreadLocal来保存变量值,使得每个线程都有自己独立的实例变量,互不相干.

 

接下来就从Dispatcher开始看起,先看其构造函数:

 

//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方     

    public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {     

        this.servletContext = servletContext;     

        //配置在web.xml中的param参数      

        this.initParams = initParams;     

    }  

//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方  

    public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {  

        this.servletContext = servletContext;   

        //配置在web.xml中的param参数  

        this.initParams = initParams;  

    } 

 

我们再看在FilterDispatcher创建Dispatcher:

 

protected Dispatcher createDispatcher(FilterConfig filterConfig) {     

    Map<String, String> params = new HashMap<String, String>();     

    for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {     

        String name = (String) e.nextElement();     

        String value = filterConfig.getInitParameter(name);     

        params.put(name, value);     

    }     

都可以从FilterConfig中得到     

    return new Dispatcher(filterConfig.getServletContext(), params);     

protected Dispatcher createDispatcher(FilterConfig filterConfig) {  

    Map<String, String> params = new HashMap<String, String>();  

    for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {  

        String name = (String) e.nextElement();  

        String value = filterConfig.getInitParameter(name);  

        params.put(name, value);  

    }  

都可以从FilterConfig中得到  

    return new Dispatcher(filterConfig.getServletContext(), params);  

}

 

分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.

将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManagerList里面.

最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.

下面将分析这七层功夫是怎样一步步练成的.

 

首先是init_DefaultProperties()

 

创建Dispatcher之后,来看init()方法

init()方法是用来Load用户配置文件,资源文件以及默认的配置文件.

 

主要分七步走,看下面注释

 

public void init() {     

    

    if (configurationManager == null) {     

    //设置ConfigurationManagerdefaultFrameworkBeanName.     

    //这里DEFAULT_BEAN_NAMEstruts,这是xwork框架的内容,Framework可以是xwork,struts,webwork     

        configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);     

    }     

      //读取properties信息,默认的default.properties,     

    init_DefaultProperties(); // [1]     

//读取xml配置文件     

      init_TraditionalXmlConfigurations(); // [2]     

//读取用户自定义的struts.properties     

      init_LegacyStrutsProperties(); // [3]     

//自定义的configProviders     

      init_CustomConfigurationProviders(); // [5]     

//载入FilterDispatcher传进来的initParams     

      init_FilterInitParameters() ; // [6]     

//将配置文件中的bean与具体的类映射     

      init_AliasStandardObjects() ; // [7]     

           

//构建一个用于依赖注射的Container对象     

//在这里面会循环调用上面七个ConfigurationProviderregister方法     

//其中的重点就是DefaultConfiguration#reload()方法     

      Container container = init_PreloadConfiguration();     

      container.inject(this);     

      init_CheckConfigurationReloading(container);     

      init_CheckWebLogicWorkaround(container);     

    

      if (!dispatcherListeners.isEmpty()) {     

          for (DispatcherListener l : dispatcherListeners) {     

              l.dispatcherInitialized(this);     

          }     

      }     

  }  

public void init() {  

 

    if (configurationManager == null) {  

    //设置ConfigurationManagerdefaultFrameworkBeanName.  

    //这里DEFAULT_BEAN_NAMEstruts,这是xwork框架的内容,Framework可以是xwork,struts,webwork  

        configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);  

    }  

      //读取properties信息,默认的default.properties,   

    init_DefaultProperties(); // [1]  

//读取xml配置文件  

      init_TraditionalXmlConfigurations(); // [2]  

//读取用户自定义的struts.properties  

      init_LegacyStrutsProperties(); // [3]  

//自定义的configProviders  

      init_CustomConfigurationProviders(); // [5]  

//载入FilterDispatcher传进来的initParams  

      init_FilterInitParameters() ; // [6]  

//将配置文件中的bean与具体的类映射  

      init_AliasStandardObjects() ; // [7]  

        

//构建一个用于依赖注射的Container对象  

//在这里面会循环调用上面七个ConfigurationProviderregister方法  

//其中的重点就是DefaultConfiguration#reload()方法  

      Container container = init_PreloadConfiguration();  

      container.inject(this);  

      init_CheckConfigurationReloading(container);  

      init_CheckWebLogicWorkaround(container);  

 

      if (!dispatcherListeners.isEmpty()) {  

          for (DispatcherListener l : dispatcherListeners) {  

              l.dispatcherInitialized(this);  

          }  

      }  

  } 

 

分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.

将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManagerList里面.

最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.

下面将分析这七层功夫是怎样一步步练成的.

 

首先是init_DefaultProperties()

 

private void init_DefaultProperties() {     

    configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());     

}     

接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法     

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);     

}   

private void init_DefaultProperties() {  

    configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());  

}  

接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法  

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);  

 

 

//PropertiesSettings构造方法       

    //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写     

    public PropertiesSettings(String name) {     

             

        URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass());     

             

        if (settingsUrl == null) {     

            LOG.debug(name + ".properties missing");     

            settings = new LocatableProperties();     

            return;     

        }     

             

        settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));     

    

        // Load settings     

        InputStream in = null;     

        try {     

            in = settingsUrl.openStream();     

            settings.load(in);     

        } catch (IOException e) {     

            throw new StrutsException("Could not load " + name + ".properties:" + e, e);     

        } finally {     

            if(in != null) {     

                try {     

                    in.close();     

                } catch(IOException io) {     

                    LOG.warn("Unable to close input stream", io);     

                }     

            }     

        }     

    }     

         

    //loadSettings主要是将progertyvalueLocale从上面PropertiesSettings中取得并存放到LocatableProperties props     

    //这个propsregister的一个入参.     

    protected void loadSettings(LocatableProperties props, final Settings settings) {     

        // We are calling the impl methods to get around the single instance of Settings that is expected     

        for (Iterator i = settings.listImpl(); i.hasNext(); ) {     

            String name = (String) i.next();     

            props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name));     

        }     

    } 

//PropertiesSettings构造方法    

    //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写  

    public PropertiesSettings(String name) {  

          

        URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass());   

          

        if (settingsUrl == null) {  

            LOG.debug(name + ".properties missing");  

            settings = new LocatableProperties();  

            return;  

        }  

          

        settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));  

 

        // Load settings  

        InputStream in = null;  

        try {  

            in = settingsUrl.openStream();  

            settings.load(in);  

        } catch (IOException e) {  

            throw new StrutsException("Could not load " + name + ".properties:" + e, e);  

        } finally {  

            if(in != null) {  

                try {  

                    in.close();  

                } catch(IOException io) {  

                    LOG.warn("Unable to close input stream", io);  

                }  

            }  

        }  

    }  

      

    //loadSettings主要是将progertyvalueLocale从上面PropertiesSettings中取得并存放到LocatableProperties props  

    //这个propsregister的一个入参.  

    protected void loadSettings(LocatableProperties props, final Settings settings) {  

        // We are calling the impl methods to get around the single instance of Settings that is expected  

        for (Iterator i = settings.listImpl(); i.hasNext(); ) {  

            String name = (String) i.next();  

            props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name));  

        }  

    }

 

再来看第二步:init_TraditionalXmlConfigurations()

 

private void init_TraditionalXmlConfigurations() {     

 //首先读取web.xml中的config初始参数值        

    //如果没有配置就使用默认的DEFAULT_CONFIGURATION_PATHS:"struts-default.xml,struts-plugin.xml,struts.xml"        

    //这儿就可以看出为什么默认的配置文件必须取名为这三个名称了        

    //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可      

    String configPaths = initParams.get("config");     

    if (configPaths == null) {     

        configPaths = DEFAULT_CONFIGURATION_PATHS;     

    }     

    String[] files = configPaths.split("\\s*[,]\\s*");     

    for (String file : files) {     

        if (file.endsWith(".xml")) {     

            if ("xwork.xml".equals(file)) {     

    //XmlConfigurationProvider负责解析xwork.xml     

                configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));     

            } else {     

    //其它xml都是由StrutsXmlConfigurationProvider来解析     

                configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));     

            }     

        } else {     

            throw new IllegalArgumentException("Invalid configuration file name");     

        }     

    }     

}   

private void init_TraditionalXmlConfigurations() {  

 //首先读取web.xml中的config初始参数值     

    //如果没有配置就使用默认的DEFAULT_CONFIGURATION_PATHS:"struts-default.xml,struts-plugin.xml,struts.xml"     

    //这儿就可以看出为什么默认的配置文件必须取名为这三个名称了     

    //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可   

    String configPaths = initParams.get("config");  

    if (configPaths == null) {  

        configPaths = DEFAULT_CONFIGURATION_PATHS;  

    }  

    String[] files = configPaths.split("\\s*[,]\\s*");  

    for (String file : files) {  

        if (file.endsWith(".xml")) {  

            if ("xwork.xml".equals(file)) {  

    //XmlConfigurationProvider负责解析xwork.xml  

                configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));  

            } else {  

    //其它xml都是由StrutsXmlConfigurationProvider来解析  

                configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));  

            }  

        } else {  

            throw new IllegalArgumentException("Invalid configuration file name");  

        }  

    }  

 

 

对于其它配置文件只用StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProvider接口。

XmlConfigurationProvider负责配置文件的读取和解析,

首先通过init()中的loadDocuments(configFileName);利用DomHelper中的

public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) configFileName配置文件通过SAX解析方式按照DtdMappings解析成Document对象.

然后通过Providerregister()方法加载"bean""constant"属性,再通过loadPackages()加载packagepackage中的属性

addAction()方法负责读取<action>标签,并将数据保存在ActionConfig中;

addResultTypes()方法负责将<result-type>标签转化为ResultTypeConfig对象;

loadInterceptors()方法负责将<interceptor>标签转化为InterceptorConfi对象;

loadInterceptorStack()方法负责将<interceptor-ref>标签转化为InterceptorStackConfig对象;

loadInterceptorStacks()方法负责将<interceptor-stack>标签转化成InterceptorStackConfig对象。

而上面的方法最终会被addPackage()方法调用,addPackage又会被ProviderloadPackages()调用,将所读取到的数据汇集到PackageConfig对象中。

 

protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {     

     PackageConfig.Builder newPackage = buildPackageContext(packageElement);     

    

     if (newPackage.isNeedsRefresh()) {     

         return newPackage.build();     

     }     

     // add result types (and default result) to this package     

     addResultTypes(newPackage, packageElement);     

     // load the interceptors and interceptor stacks for this package     

     loadInterceptors(newPackage, packageElement);     

     // load the default interceptor reference for this package     

     loadDefaultInterceptorRef(newPackage, packageElement);     

     // load the default class ref for this package     

     loadDefaultClassRef(newPackage, packageElement);     

     // load the global result list for this package     

     loadGlobalResults(newPackage, packageElement);     

     // load the global exception handler list for this package     

     loadGobalExceptionMappings(newPackage, packageElement);     

     // get actions     

     NodeList actionList = packageElement.getElementsByTagName("action");     

     for (int i = 0; i < actionList.getLength(); i++) {     

         Element actionElement = (Element) actionList.item(i);     

         addAction(actionElement, newPackage);     

     }     

     // load the default action reference for this package     

     loadDefaultActionRef(newPackage, packageElement);     

     PackageConfig cfg = newPackage.build();     

     configuration.addPackageConfig(cfg.getName(), cfg);     

     return cfg;     

 }       

    

loadConfigurationFiles解析读取xml中的内容     

 private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {           

   ...       

//通过DomHelper调用SAX进行解析xml     

doc = DomHelper.parse(in, dtdMappings);     

...     

   Element rootElement = doc.getDocumentElement();     

   NodeList children = rootElement.getChildNodes();     

   int childSize = children.getLength();     

    

   for (int i = 0; i < childSize; i++) {     

     Node childNode = children.item(i);     

    

     if (childNode instanceof Element) {     

       Element child = (Element) childNode;     

    

       final String nodeName = child.getNodeName();     

    

       if ("include".equals(nodeName)) {     

         String includeFileName = child.getAttribute("file");     

    

      //解析每个action配置是,对于include文件可以使用通配符*来进行配置        

         //Struts.xml中可配置成<include file="actions_*.xml"/>       

         if (includeFileName.indexOf('*') != -1) {     

           ClassPathFinder wildcardFinder = new ClassPathFinder();     

           wildcardFinder.setPattern(includeFileName);     

           Vector<String> wildcardMatches = wildcardFinder.findMatches();     

           for (String match : wildcardMatches) {     

       //递归Loadfile中的<include/>     

             docs.addAll(loadConfigurationFiles(match, child));     

           }     

         } else {     

    

           docs.addAll(loadConfigurationFiles(includeFileName, child));     

         }     

       }     

     }     

   }     

   docs.add(doc);     

   loadedFileUrls.add(url.toString());     

   ...     

   return docs;     

 }   

 

首先强调一下struts2的线程程安全,在Struts2中大量采用ThreadLocal线程局部变量的方法来保证线程的安全,Dispatcher等都是通过ThreadLocal来保存变量值,使得每个线程都有自己独立的实例变量,互不相干.

 

接下来就从Dispatcher开始看起,先看其构造函数:

 

//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方     

    public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {     

        this.servletContext = servletContext;     

        //配置在web.xml中的param参数     

        this.initParams = initParams;     

    }  

//创建Dispatcher,此类是一个Delegate,它是真正完成根据url解析转向,读取对应Action的地方  

    public Dispatcher(ServletContext servletContext, Map<String, String> initParams) {  

        this.servletContext = servletContext;  

        //配置在web.xml中的param参数  

        this.initParams = initParams;  

    } 

 

我们再看在FilterDispatcher创建Dispatcher:

 

protected Dispatcher createDispatcher(FilterConfig filterConfig) {     

    Map<String, String> params = new HashMap<String, String>();     

    for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {     

        String name = (String) e.nextElement();     

        String value = filterConfig.getInitParameter(name);     

        params.put(name, value);     

    }     

都可以从FilterConfig中得到     

    return new Dispatcher(filterConfig.getServletContext(), params);     

protected Dispatcher createDispatcher(FilterConfig filterConfig) {  

    Map<String, String> params = new HashMap<String, String>();  

    for (Enumeration e = filterConfig.getInitParameterNames(); e.hasMoreElements();) {  

        String name = (String) e.nextElement();  

        String value = filterConfig.getInitParameter(name);  

        params.put(name, value);  

    }  

都可以从FilterConfig中得到  

    return new Dispatcher(filterConfig.getServletContext(), params);  

}

 

分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.

将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManagerList里面.

最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.

下面将分析这七层功夫是怎样一步步练成的.

 

首先是init_DefaultProperties()

 

创建Dispatcher之后,来看init()方法

init()方法是用来Load用户配置文件,资源文件以及默认的配置文件.

 

主要分七步走,看下面注释

 

public void init() {     

    

    if (configurationManager == null) {     

    //设置ConfigurationManagerdefaultFrameworkBeanName.     

    //这里DEFAULT_BEAN_NAMEstruts,这是xwork框架的内容,Framework可以是xwork,struts,webwork     

        configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);     

    }     

      //读取properties信息,默认的default.properties,     

    init_DefaultProperties(); // [1]     

//读取xml配置文件     

      init_TraditionalXmlConfigurations(); // [2]     

//读取用户自定义的struts.properties     

      init_LegacyStrutsProperties(); // [3]     

//自定义的configProviders     

      init_CustomConfigurationProviders(); // [5]     

//载入FilterDispatcher传进来的initParams     

      init_FilterInitParameters() ; // [6]     

//将配置文件中的bean与具体的类映射     

      init_AliasStandardObjects() ; // [7]     

           

//构建一个用于依赖注射的Container对象     

//在这里面会循环调用上面七个ConfigurationProviderregister方法     

//其中的重点就是DefaultConfiguration#reload()方法     

      Container container = init_PreloadConfiguration();     

      container.inject(this);     

      init_CheckConfigurationReloading(container);     

      init_CheckWebLogicWorkaround(container);     

    

      if (!dispatcherListeners.isEmpty()) {     

          for (DispatcherListener l : dispatcherListeners) {     

              l.dispatcherInitialized(this);     

          }     

      }     

  }  

public void init() {  

 

    if (configurationManager == null) {  

    //设置ConfigurationManagerdefaultFrameworkBeanName.  

    //这里DEFAULT_BEAN_NAMEstruts,这是xwork框架的内容,Framework可以是xwork,struts,webwork  

        configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);  

    }  

      //读取properties信息,默认的default.properties,  

    init_DefaultProperties(); // [1]  

//读取xml配置文件  

      init_TraditionalXmlConfigurations(); // [2]  

//读取用户自定义的struts.properties  

      init_LegacyStrutsProperties(); // [3]  

//自定义的configProviders  

      init_CustomConfigurationProviders(); // [5]  

//载入FilterDispatcher传进来的initParams  

      init_FilterInitParameters() ; // [6]  

//将配置文件中的bean与具体的类映射  

      init_AliasStandardObjects() ; // [7]  

        

//构建一个用于依赖注射的Container对象  

//在这里面会循环调用上面七个ConfigurationProviderregister方法  

//其中的重点就是DefaultConfiguration#reload()方法  

      Container container = init_PreloadConfiguration();  

      container.inject(this);  

      init_CheckConfigurationReloading(container);  

      init_CheckWebLogicWorkaround(container);  

 

      if (!dispatcherListeners.isEmpty()) {  

          for (DispatcherListener l : dispatcherListeners) {  

              l.dispatcherInitialized(this);  

          }  

      }  

  } 

 

分七步载入各种配置属性,都是通过ConfigurationProvider接口进行的,这个接口提供init(),destroy(),register()等方法.

将各种ConfigurationProvider初始化之后将实例添加到ConfigurationManagerList里面.

最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.

下面将分析这七层功夫是怎样一步步练成的.

 

首先是init_DefaultProperties()

 

private void init_DefaultProperties() {     

    configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());     

}     

接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法     

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);     

}   

private void init_DefaultProperties() {  

    configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());  

}  

接来看DefaultPropertiesProvider好了,DefaultPropertiesProvider实际上只是实现了register()方法  

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);  

 

 

//PropertiesSettings构造方法       

    //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写     

    public PropertiesSettings(String name) {     

             

        URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass());     

             

        if (settingsUrl == null) {     

            LOG.debug(name + ".properties missing");     

            settings = new LocatableProperties();     

            return;     

        }     

              

        settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));     

    

        // Load settings     

        InputStream in = null;     

        try {     

            in = settingsUrl.openStream();     

            settings.load(in);     

        } catch (IOException e) {     

            throw new StrutsException("Could not load " + name + ".properties:" + e, e);     

        } finally {     

            if(in != null) {     

                try {      

                    in.close();     

                } catch(IOException io) {     

                    LOG.warn("Unable to close input stream", io);     

                }     

            }     

        }     

    }     

         

    //loadSettings主要是将progertyvalueLocale从上面PropertiesSettings中取得并存放到LocatableProperties props     

    //这个propsregister的一个入参.     

    protected void loadSettings(LocatableProperties props, final Settings settings) {     

        // We are calling the impl methods to get around the single instance of Settings that is expected     

        for (Iterator i = settings.listImpl(); i.hasNext(); ) {     

            String name = (String) i.next();     

            props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name));     

        }     

    } 

//PropertiesSettings构造方法    

    //读取org/apache/struts2/default.properties的配置信息,如果项目中需要覆盖,可以在classpath里的struts.properties里覆写  

    public PropertiesSettings(String name) {  

           

        URL settingsUrl = ClassLoaderUtils.getResource(name + ".properties", getClass());  

          

        if (settingsUrl == null) {  

            LOG.debug(name + ".properties missing");  

            settings = new LocatableProperties();   

            return;  

        }  

          

        settings = new LocatableProperties(new LocationImpl(null, settingsUrl.toString()));  

 

        // Load settings  

        InputStream in = null;  

        try {  

            in = settingsUrl.openStream();  

            settings.load(in);  

        } catch (IOException e) {  

            throw new StrutsException("Could not load " + name + ".properties:" + e, e);  

        } finally {  

            if(in != null) {  

                try {  

                    in.close();  

                } catch(IOException io) {  

                    LOG.warn("Unable to close input stream", io);  

                }  

            }  

        }  

    }  

      

    //loadSettings主要是将progertyvalueLocale从上面PropertiesSettings中取得并存放到LocatableProperties props  

    //这个propsregister的一个入参.  

    protected void loadSettings(LocatableProperties props, final Settings settings) {  

        // We are calling the impl methods to get around the single instance of Settings that is expected  

        for (Iterator i = settings.listImpl(); i.hasNext(); ) {  

            String name = (String) i.next();  

            props.setProperty(name, settings.getImpl(name), settings.getLocationImpl(name));  

        }  

    }

 

再来看第二步:init_TraditionalXmlConfigurations()

 

private void init_TraditionalXmlConfigurations() {     

 //首先读取web.xml中的config初始参数值        

    //如果没有配置就使用默认的DEFAULT_CONFIGURATION_PATHS:"struts-default.xml,struts-plugin.xml,struts.xml"        

    //这儿就可以看出为什么默认的配置文件必须取名为这三个名称了        

    //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可      

    String configPaths = initParams.get("config");     

    if (configPaths == null) {     

        configPaths = DEFAULT_CONFIGURATION_PATHS;     

    }     

    String[] files = configPaths.split("\\s*[,]\\s*");     

    for (String file : files) {     

        if (file.endsWith(".xml")) {     

            if ("xwork.xml".equals(file)) {     

    //XmlConfigurationProvider负责解析xwork.xml     

                configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));     

            } else {     

    //其它xml都是由StrutsXmlConfigurationProvider来解析     

                configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));     

            }     

        } else {     

            throw new IllegalArgumentException("Invalid configuration file name");     

        }     

    }     

}   

private void init_TraditionalXmlConfigurations() {  

 //首先读取web.xml中的config初始参数值     

    //如果没有配置就使用默认的DEFAULT_CONFIGURATION_PATHS:"struts-default.xml,struts-plugin.xml,struts.xml"      

    //这儿就可以看出为什么默认的配置文件必须取名为这三个名称了     

    //如果不想使用默认的名称,直接在web.xml中配置config初始参数即可   

    String configPaths = initParams.get("config");  

    if (configPaths == null) {  

        configPaths = DEFAULT_CONFIGURATION_PATHS;  

    }  

    String[] files = configPaths.split("\\s*[,]\\s*");  

    for (String file : files) {  

        if (file.endsWith(".xml")) {  

            if ("xwork.xml".equals(file)) {  

    //XmlConfigurationProvider负责解析xwork.xml  

                configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));  

            } else {  

    //其它xml都是由StrutsXmlConfigurationProvider来解析  

                configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));  

            }  

        } else {  

            throw new IllegalArgumentException("Invalid configuration file name");  

        }  

    }  

 

 

对于其它配置文件只用StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProvider接口。

XmlConfigurationProvider负责配置文件的读取和解析,

首先通过init()中的loadDocuments(configFileName);利用DomHelper中的

public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) configFileName配置文件通过SAX解析方式按照DtdMappings解析成Document对象.

然后通过Providerregister()方法加载"bean""constant"属性,再通过loadPackages()加载packagepackage中的属性

addAction()方法负责读取<action>标签,并将数据保存在ActionConfig中;

addResultTypes()方法负责将<result-type>标签转化为ResultTypeConfig对象;

loadInterceptors()方法负责将<interceptor>标签转化为InterceptorConfi对象;

loadInterceptorStack()方法负责将<interceptor-ref>标签转化为InterceptorStackConfig对象;

loadInterceptorStacks()方法负责将<interceptor-stack>标签转化成InterceptorStackConfig对象。

而上面的方法最终会被addPackage()方法调用,addPackage又会被ProviderloadPackages()调用,将所读取到的数据汇集到PackageConfig对象中。

 

protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {     

     PackageConfig.Builder newPackage = buildPackageContext(packageElement);     

    

     if (newPackage.isNeedsRefresh()) {      

         return newPackage.build();     

     }     

     // add result types (and default result) to this package     

     addResultTypes(newPackage, packageElement);     

     // load the interceptors and interceptor stacks for this package     

     loadInterceptors(newPackage, packageElement);     

     // load the default interceptor reference for this package     

     loadDefaultInterceptorRef(newPackage, packageElement);     

     // load the default class ref for this package     

     loadDefaultClassRef(newPackage, packageElement);     

     // load the global result list for this package     

     loadGlobalResults(newPackage, packageElement);     

     // load the global exception handler list for this package     

     loadGobalExceptionMappings(newPackage, packageElement);     

     // get actions     

     NodeList actionList = packageElement.getElementsByTagName("action");     

     for (int i = 0; i < actionList.getLength(); i++) {     

         Element actionElement = (Element) actionList.item(i);     

         addAction(actionElement, newPackage);     

     }     

     // load the default action reference for this package     

     loadDefaultActionRef(newPackage, packageElement);     

     PackageConfig cfg = newPackage.build();     

     configuration.addPackageConfig(cfg.getName(), cfg);     

     return cfg;     

 }       

    

loadConfigurationFiles解析读取xml中的内容     

 private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {           

   ...       

//通过DomHelper调用SAX进行解析xml     

doc = DomHelper.parse(in, dtdMappings);     

...     

   Element rootElement = doc.getDocumentElement();     

   NodeList children = rootElement.getChildNodes();     

   int childSize = children.getLength();     

    

   for (int i = 0; i < childSize; i++) {     

     Node childNode = children.item(i);     

    

     if (childNode instanceof Element) {     

       Element child = (Element) childNode;     

    

       final String nodeName = child.getNodeName();     

    

       if ("include".equals(nodeName)) {     

         String includeFileName = child.getAttribute("file");     

    

      //解析每个action配置是,对于include文件可以使用通配符*来进行配置        

         //Struts.xml中可配置成<include file="actions_*.xml"/>       

         if (includeFileName.indexOf('*') != -1) {     

           ClassPathFinder wildcardFinder = new ClassPathFinder();     

           wildcardFinder.setPattern(includeFileName);     

           Vector<String> wildcardMatches = wildcardFinder.findMatches();     

           for (String match : wildcardMatches) {     

       //递归Loadfile中的<include/>     

             docs.addAll(loadConfigurationFiles(match, child));     

           }     

         } else {     

    

           docs.addAll(loadConfigurationFiles(includeFileName, child));     

         }     

       }     

     }     

   }     

   docs.add(doc);     

   loadedFileUrls.add(url.toString());     

   ...     

   return docs;     

 } 

接下来第三步:init_LegacyStrutsProperties()

调用的是调用的是LegacyPropertiesConfigurationProvider

通过比较前面DefaultPropertiesProvider与调用的是LegacyPropertiesConfigurationProvider.

发现DefaultPropertiesProvider继承自后者,但重写了register()方法,主要是生成PropertiesSetting的不同,前者是根据org/apache/struts2/default.properties

后者是根据struts.properties

我们展开register()中的Settings.getInstance(),最后是调用getDefaultInstance()

 

private static Settings getDefaultInstance() {     

     if (defaultImpl == null) {     

         // Create bootstrap implementation     

//不带参数的DefaultSettings(),区别与DefaultPropertiesProvider中直接带default.properties参数     

//不带参数就是默认为struts.propertes,并且加载struts.custom.properties所定义的properties文件     

         defaultImpl = new DefaultSettings();     

    

         // Create default implementation     

         try {     

    //STRUTS_CONFIGURATION:struts.configuration     

    //struts.proterties中查找struts.configuration的值,这个值必须是org.apache.struts2.config.Configuration接口的实现类     

    //所以我有个困惑就是在下面的转换当中怎么将Configuration转换成Setting类型的...     

    //这一点先放下了,有时间再研究     

             String className = get(StrutsConstants.STRUTS_CONFIGURATION);     

    

             if (!className.equals(defaultImpl.getClass().getName())) {     

                 try {     

                     // singleton instances shouldn't be built accessing request or session-specific context data     

                     defaultImpl = (Settings) ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLoader().loadClass(className), null);     

                 } catch (Exception e) {     

                     LOG.error("Settings: Could not instantiate the struts.configuration object, substituting the default implementation.", e);     

                 }     

             }     

         } catch (IllegalArgumentException ex) {     

             // ignore     

  } 

private static Settings getDefaultInstance() {  

     if (defaultImpl == null) {  

         // Create bootstrap implementation  

//不带参数的DefaultSettings(),区别与DefaultPropertiesProvider中直接带default.properties参数  

//不带参数就是默认为struts.propertes,并且加载struts.custom.properties所定义的properties文件  

         defaultImpl = new DefaultSettings();  

 

         // Create default implementation  

         try {  

    //STRUTS_CONFIGURATION:struts.configuration  

    //struts.proterties中查找struts.configuration的值,这个值必须是org.apache.struts2.config.Configuration接口的实现类  

    //所以我有个困惑就是在下面的转换当中怎么将Configuration转换成Setting类型的...  

    //这一点先放下了,有时间再研究  

             String className = get(StrutsConstants.STRUTS_CONFIGURATION);  

 

             if (!className.equals(defaultImpl.getClass().getName())) {  

                 try {  

                     // singleton instances shouldn't be built accessing request or session-specific context data  

                     defaultImpl = (Settings) ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLoader().loadClass(className), null);  

                 } catch (Exception e) {  

                     LOG.error("Settings: Could not instantiate the struts.configuration object, substituting the default implementation.", e);  

                 }  

             }  

         } catch (IllegalArgumentException ex) {  

             // ignore  

  }

 

2.1.6中去掉了第四步:init_ZeroConfiguration();

第五步是自定义的configProviders

 

private void init_CustomConfigurationProviders() {     

    //从这里可以看到可以将自定义的Provider定义在web.xmlFilterDispatcherparam:configProviders     

    String configProvs = initParams.get("configProviders");     

    if (configProvs != null) {     

        String[] classes = configProvs.split("\\s*[,]\\s*");     

        for (String cname : classes) {     

            try {     

                Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());     

                ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();     

                configurationManager.addConfigurationProvider(prov);     

            }      

...     

        }     

    }      

}   

private void init_CustomConfigurationProviders() {  

    //从这里可以看到可以将自定义的Provider定义在web.xmlFilterDispatcherparam:configProviders  

    String configProvs = initParams.get("configProviders");  

    if (configProvs != null) {  

        String[] classes = configProvs.split("\\s*[,]\\s*");  

        for (String cname : classes) {  

            try {  

                Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());  

                ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();  

                configurationManager.addConfigurationProvider(prov);  

            }   

...  

        }  

    }  

 

 

第六步:init_FilterInitParameters

 

//从这里可以看出struts.properties中的属性不仅可以在struts.xml中以constant形式定义,而且可以在FilterDispatcherparam中定义       

    private void init_FilterInitParameters() {     

        configurationManager.addConfigurationProvider(new ConfigurationProvider() {     

            public void destroy() {}     

            public void init(Configuration configuration) throws ConfigurationException {}     

            public void loadPackages() throws ConfigurationException {}     

            public boolean needsReload() { return false; }     

    

            public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {     

                props.putAll(initParams);//在这里实现滴~     

            }     

        });     

    }  

//从这里可以看出struts.properties中的属性不仅可以在struts.xml中以constant形式定义,而且可以在FilterDispatcherparam中定义    

    private void init_FilterInitParameters() {  

        configurationManager.addConfigurationProvider(new ConfigurationProvider() {  

            public void destroy() {}  

            public void init(Configuration configuration) throws ConfigurationException {}  

            public void loadPackages() throws ConfigurationException {}  

            public boolean needsReload() { return false; }  

 

            public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {  

                props.putAll(initParams);//在这里实现滴~  

            }  

        });  

    } 

 

第七步:init_AliasStandardObjects,使用BeanSelectionProvider

这是将配置文件中定义的<bean>与实际的类相映射,就是注入bean的依赖关系,这部分以后有时候再研究Container

 

接下来是看怎样调用这些ConfigurationProviders

展开init_PreloadConfiguration()

 

private Container init_PreloadConfiguration() {     

     Configuration config = configurationManager.getConfiguration();     

     Container container = config.getContainer();     

    

     boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));     

     LocalizedTextUtil.setReloadBundles(reloadi18n);     

    

     return container;     

 }     

      //再看getConfiguration()      

 public synchronized Configuration getConfiguration() {     

     if (configuration == null) {     

         setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));     

         try {     

//重点就是这个reloadContainer     

             configuration.reloadContainer(getContainerProviders());     

         } catch (ConfigurationException e) {     

             setConfiguration(null);     

             throw new ConfigurationException("Unable to load configuration.", e);     

         }      

     } else {     

         conditionalReload();     

     }     

    

     return configuration;     

 }   

private Container init_PreloadConfiguration() {  

     Configuration config = configurationManager.getConfiguration();  

     Container container = config.getContainer();  

 

     boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.STRUTS_I18N_RELOAD));  

     LocalizedTextUtil.setReloadBundles(reloadi18n);  

 

     return container;  

 }  

      //再看getConfiguration()  

 public synchronized Configuration getConfiguration() {  

     if (configuration == null) {  

         setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));  

         try {  

//重点就是这个reloadContainer  

             configuration.reloadContainer(getContainerProviders());  

         } catch (ConfigurationException e) {  

             setConfiguration(null);  

             throw new ConfigurationException("Unable to load configuration.", e);  

         }  

     } else {  

         conditionalReload();  

     }  

 

     return configuration;  

 } 

 

 

展开DefaultConfiguration中的reloadContainer

 

public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {     

      packageContexts.clear();     

      loadedFileNames.clear();     

      List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();     

    

//Struts2(xwork2)Container来完成依赖注入的功能      

//首先初始化一个ContainerBuilder,再由builder来保存接口与实现类或工厂类的对应关系     

//然后通过builder.create(boolean)方法产生container     

//container.getInstance(Class);就可以得到接口的实现实例了     

//这一部分比较复杂,后面研究完成了,会单独拿出来讲,这里先弄清楚Xwork依赖注入的实现步骤就可以了     

      ContainerProperties props = new ContainerProperties();     

      ContainerBuilder builder = new ContainerBuilder();     

      for (final ContainerProvider containerProvider : providers)     

      {     

    //循环调用ConfigurationProviderinitregister方法,明白了吧,在这里统一循环调用     

          containerProvider.init(this);     

          containerProvider.register(builder, props);     

      }     

      props.setConstants(builder);     

      //注入依赖关系,在这里并不产生实例     

      builder.factory(Configuration.class, new Factory<Configuration>() {     

          public Configuration create(Context context) throws Exception {     

              return DefaultConfiguration.this;     

          }     

      });     

    

      ActionContext oldContext = ActionContext.getContext();     

      try {     

          // Set the bootstrap container for the purposes of factory creation     

          Container bootstrap = createBootstrapContainer();     

          setContext(bootstrap);     

    //create已经注入依赖关系的Container     

          container = builder.create(false);     

          setContext(container);     

          objectFactory = container.getInstance(ObjectFactory.class);     

    

          // Process the configuration providers first     

          for (final ContainerProvider containerProvider : providers)     

          {     

              if (containerProvider instanceof PackageProvider) {     

                  container.inject(containerProvider);     

            //调用PackageProviderloadPackages()方法,这里主要是针对XmlConfigurationProviderStrutsXmlConfigurationProvider     

                  ((PackageProvider)containerProvider).loadPackages();     

                  packageProviders.add((PackageProvider)containerProvider);     

              }     

          }     

    

          // Then process any package providers from the plugins     

          Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);     

          if (packageProviderNames != null) {      

              for (String name : packageProviderNames) {     

                  PackageProvider provider = container.getInstance(PackageProvider.class, name);     

                  provider.init(this);     

                  provider.loadPackages();     

                  packageProviders.add(provider);     

              }     

          }     

    

          rebuildRuntimeConfiguration();     

      } finally {     

          if (oldContext == null) {     

              ActionContext.setContext(null);     

          }     

      }     

      return packageProviders;     

  }

Dispatcher已经在之前讲过,这就好办了。FilterDispatcherStruts2的核心控制器,首先看一下init()方法。

 

public void init(FilterConfig filterConfig) throws ServletException {      

    try {     

        this.filterConfig = filterConfig;     

        initLogging();     

     //创建dispatcher,前面都已经讲过啰     

        dispatcher = createDispatcher(filterConfig);     

        dispatcher.init();     

     //注入将FilterDispatcher中的变量通过container注入,如下面的staticResourceLoader     

        dispatcher.getContainer().inject(this);     

        //StaticContentLoaderBeanSelectionProvider中已经被注入了依赖关系:DefaultStaticContentLoader     

     //可以在struts-default.xml中的<bean>可以找到     

        staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));     

    } finally {     

        ActionContext.setContext(null);     

    }     

}  

public void init(FilterConfig filterConfig) throws ServletException {  

    try {  

        this.filterConfig = filterConfig;  

        initLogging();  

     //创建dispatcher,前面都已经讲过啰  

        dispatcher = createDispatcher(filterConfig);  

        dispatcher.init();  

     //注入将FilterDispatcher中的变量通过container注入,如下面的staticResourceLoader  

        dispatcher.getContainer().inject(this);  

        //StaticContentLoaderBeanSelectionProvider中已经被注入了依赖关系:DefaultStaticContentLoader  

     //可以在struts-default.xml中的<bean>可以找到  

        staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));  

    } finally {  

        ActionContext.setContext(null);  

    }  

 

//下面来看DefaultStaticContentLoadersetHostConfig     

    public void setHostConfig(HostConfig filterConfig) {     

          //读取初始参数pakages,调用parse(),解析成类似/org/apache/struts2/static,/template的数组        

        String param = filterConfig.getInitParameter("packages");     

           //"org.apache.struts2.static template org.apache.struts2.interceptor.debugging static"     

        String packages = getAdditionalPackages();     

        if (param != null) {     

            packages = param + " " + packages;     

        }     

        this.pathPrefixes = parse(packages);     

        initLogging(filterConfig);     

    }      

//下面来看DefaultStaticContentLoadersetHostConfig  

    public void setHostConfig(HostConfig filterConfig) {  

          //读取初始参数pakages,调用parse(),解析成类似/org/apache/struts2/static,/template的数组     

        String param = filterConfig.getInitParameter("packages");  

           //"org.apache.struts2.static template org.apache.struts2.interceptor.debugging static"  

        String packages = getAdditionalPackages();  

        if (param != null) {  

            packages = param + " " + packages;  

        }  

        this.pathPrefixes = parse(packages);  

        initLogging(filterConfig);  

    }     

 

现在回去doFilter的方法,每当有一个Request,都会调用这些FiltersdoFilter方法

 

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {     

    

    HttpServletRequest request = (HttpServletRequest) req;     

    HttpServletResponse response = (HttpServletResponse) res;     

    ServletContext servletContext = getServletContext();     

    

    String timerKey = "FilterDispatcher_doFilter: ";     

    try {     

    

        // FIXME: this should be refactored better to not duplicate work with the action invocation     

        //先看看ValueStackFactory所注入的实现类OgnlValueStackFactory     

     //new OgnlValueStack     

        ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();     

        ActionContext ctx = new ActionContext(stack.getContext());     

        ActionContext.setContext(ctx);     

    

        UtilTimerStack.push(timerKey);     

    

 //如果是multipart/form-data就用MultiPartRequestWrapper进行包装     

//MultiPartRequestWrapperStrutsRequestWrapper的子类,两者都是HttpServletRequest实现     

//此时在MultiPartRequestWrapper中就会把Files给解析出来,用于文件上传     

//所有request都会StrutsRequestWrapper进行包装,StrutsRequestWrapper是可以访问ValueStack     

//下面是参见DispatcherwrapRequest     

   // String content_type = request.getContentType();     

       //if(content_type!= null&&content_type.indexOf("multipart/form-data")!=-1){     

       //MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class);     

       //request =new MultiPartRequestWrapper(multi,request,getSaveDir(servletContext));     

       //} else {     

       //     request = new StrutsRequestWrapper(request);     

       // }     

         

        request = prepareDispatcherAndWrapRequest(request, response);     

        ActionMapping mapping;     

        try {     

         //根据url取得对应的Action的配置信息     

         //看一下注入的DefaultActionMappergetMapping()方法.Action的配置信息存储在 ActionMapping对象中     

            mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());     

        } catch (Exception ex) {     

            log.error("error getting ActionMapping", ex);     

            dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);     

            return;     

        }     

    

     //如果找不到对应的action配置,则直接返回。比如你输入***.jsp等等                                       

     //这儿有个例外,就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,当然.class文件除外。如果再没有则跳转到404       

        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 (staticResourceLoader.canHandle(resourcePath)) {     

            // DefaultStaticContentLoader:return serveStatic && (resourcePath.startsWith("/struts") || resourcePath.startsWith("/static"));     

                staticResourceLoader.findStaticResource(resourcePath, request, response);     

            } else {     

                // this is a normal request, let it pass through     

                chain.doFilter(request, response);     

            }     

            // The framework did its job here     

            return;     

        }     

        //正式开始Action的方法     

        dispatcher.serviceAction(request, response, servletContext, mapping);     

    

    } finally {     

        try {     

            ActionContextCleanUp.cleanUp(req);     

        } finally {     

            UtilTimerStack.pop(timerKey);     

        }     

    }     

}  

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {  

 

    HttpServletRequest request = (HttpServletRequest) req;  

    HttpServletResponse response = (HttpServletResponse) res;  

    ServletContext servletContext = getServletContext();  

 

    String timerKey = "FilterDispatcher_doFilter: ";  

    try {  

 

        // FIXME: this should be refactored better to not duplicate work with the action invocation  

        //先看看ValueStackFactory所注入的实现类OgnlValueStackFactory  

     //new OgnlValueStack  

        ValueStack stack = dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();  

        ActionContext ctx = new ActionContext(stack.getContext());  

        ActionContext.setContext(ctx);  

 

        UtilTimerStack.push(timerKey);  

 

 //如果是multipart/form-data就用MultiPartRequestWrapper进行包装  

//MultiPartRequestWrapperStrutsRequestWrapper的子类,两者都是HttpServletRequest实现  

//此时在MultiPartRequestWrapper中就会把Files给解析出来,用于文件上传  

//所有request都会StrutsRequestWrapper进行包装,StrutsRequestWrapper是可以访问ValueStack  

//下面是参见DispatcherwrapRequest  

   // String content_type = request.getContentType();  

       //if(content_type!= null&&content_type.indexOf("multipart/form-data")!=-1){  

       //MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class);  

       //request =new MultiPartRequestWrapper(multi,request,getSaveDir(servletContext));  

       //} else {  

       //     request = new StrutsRequestWrapper(request);  

       // }  

      

        request = prepareDispatcherAndWrapRequest(request, response);  

        ActionMapping mapping;  

        try {  

         //根据url取得对应的Action的配置信息  

         //看一下注入的DefaultActionMappergetMapping()方法.Action的配置信息存储在 ActionMapping对象中  

            mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());  

        } catch (Exception ex) {  

            log.error("error getting ActionMapping", ex);  

            dispatcher.sendError(request, response, servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);  

            return;  

        }  

 

     //如果找不到对应的action配置,则直接返回。比如你输入***.jsp等等                                   

     //这儿有个例外,就是如果path是以“/struts”开头,则到初始参数packages配置的包路径去查找对应的静态资源并输出到页面流中,当然.class文件除外。如果再没有则跳转到404    

        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 (staticResourceLoader.canHandle(resourcePath)) {  

            // DefaultStaticContentLoader:return serveStatic && (resourcePath.startsWith("/struts") || resourcePath.startsWith("/static"));  

                staticResourceLoader.findStaticResource(resourcePath, request, response);  

            } else {  

                // this is a normal request, let it pass through  

                chain.doFilter(request, response);  

            }  

            // The framework did its job here  

            return;  

        }  

        //正式开始Action的方法  

        dispatcher.serviceAction(request, response, servletContext, mapping);  

 

    } finally {  

        try {  

            ActionContextCleanUp.cleanUp(req);  

        } finally {  

            UtilTimerStack.pop(timerKey);  

        }  

    }  

 

//下面是ActionMapper接口的实现类 DefaultActionMappergetMapping()方法的源代码:      

    public ActionMapping getMapping(HttpServletRequest request,     

            ConfigurationManager configManager) {     

        ActionMapping mapping = new ActionMapping();     

        String uri = getUri(request);//得到请求路径的URI,如:testAtcion.actiontestAction.do     

    

    

        int indexOfSemicolon = uri.indexOf(";");//修正url的带;jsessionid 时找不到而且的bug     

        uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;     

    

        uri = dropExtension(uri, mapping);//删除扩展名,默认扩展名为action     

        if (uri == null) {     

            return null;     

        }     

    

        parseNameAndNamespace(uri, mapping, configManager);//匹配Actionnamenamespace     

    

        handleSpecialParameters(request, mapping);//去掉重复参数     

    

        //如果Actionname没有解析出来,直接返回     

    if (mapping.getName() == null) {     

      returnnull;     

    }     

     //下面处理形如testAction!method格式的请求路径     

    if (allowDynamicMethodCalls) {     

      // handle "name!method" convention.     

      String name = mapping.getName();     

      int exclamation = name.lastIndexOf("!");//!Action名称和方法名的分隔符     

      if (exclamation != -1) {     

        mapping.setName(name.substring(0, exclamation));//提取左边为name     

        mapping.setMethod(name.substring(exclamation + 1));//提取右边的method     

      }     

    }     

    

        return mapping;     

    }  

//下面是ActionMapper接口的实现类 DefaultActionMappergetMapping()方法的源代码:   

    public ActionMapping getMapping(HttpServletRequest request,  

            ConfigurationManager configManager) {  

        ActionMapping mapping = new ActionMapping();  

        String uri = getUri(request);//得到请求路径的URI,如:testAtcion.actiontestAction.do  

 

 

        int indexOfSemicolon = uri.indexOf(";");//修正url的带;jsessionid 时找不到而且的bug  

        uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;  

 

        uri = dropExtension(uri, mapping);//删除扩展名,默认扩展名为action  

        if (uri == null) {  

            return null;  

        }  

 

        parseNameAndNamespace(uri, mapping, configManager);//匹配Actionnamenamespace  

 

        handleSpecialParameters(request, mapping);//去掉重复参数  

 

        //如果Actionname没有解析出来,直接返回  

    if (mapping.getName() == null) {  

      returnnull;  

    }  

     //下面处理形如testAction!method格式的请求路径  

    if (allowDynamicMethodCalls) {  

      // handle "name!method" convention.  

      String name = mapping.getName();  

      int exclamation = name.lastIndexOf("!");//!Action名称和方法名的分隔符  

      if (exclamation != -1) {  

        mapping.setName(name.substring(0, exclamation));//提取左边为name  

        mapping.setMethod(name.substring(exclamation + 1));//提取右边的method  

      }  

    }  

 

        return mapping;  

    } 

 

从代码中看出,getMapping()方法返回ActionMapping类型的对象,该对象包含三个参数:Actionnamenamespace和要调用的方法method

  如果getMapping()方法返回ActionMapping对象为null,则FilterDispatcher认为用户请求不是Action,自然另当别论,FilterDispatcher会做一件非常有意思的事:如果请求以/struts开头,会自动查找在web.xml文件中配置的 packages初始化参数,就像下面这样:

 

 <filter>    

        <filter-name>struts2</filter-name>    

        <filter-class>    

          org.apache.struts2.dispatcher.FilterDispatcher     

        </filter-class>    

        <init-param>    

          <param-name>packages</param-name>    

          <param-value>com.lizanhong.action</param-value>    

        </init-param>    

    </filter>   

 <filter> 

        <filter-name>struts2</filter-name> 

        <filter-class> 

          org.apache.struts2.dispatcher.FilterDispatcher  

        </filter-class> 

        <init-param> 

          <param-name>packages</param-name> 

          <param-value>com.lizanhong.action</param-value> 

        </init-param> 

    </filter> 

 

 

FilterDispatcher会将com.lizanhong.action包下的文件当作静态资源处理,即直接在页面上显示文件内容,不过会忽略扩展名为class的文件。比如在com.lizanhong.action包下有一个aaa.txt的文本文件,其内容为“中华人民共和国”,访问 http://localhost:8081/Struts2Demo/struts/aaa.txt时会输出txt中的内容

   FilterDispatcher.findStaticResource()方法

 

protectedvoid findStaticResource(String name, HttpServletRequest request, HttpServletResponse response) throws IOException {     

    if (!name.endsWith(".class")) {//忽略class文件     

     //遍历packages参数     

      for (String pathPrefix : pathPrefixes) {     

        InputStream is = findInputStream(name, pathPrefix);//读取请求文件流     

        if (is != null) {     

          ...     

          // set the content-type header     

          String contentType = getContentType(name);//读取内容类型     

          if (contentType != null) {     

            response.setContentType(contentType);//重新设置内容类型     

          }     

         ...     

          try {     

           //将读取到的文件流以每次复制4096个字节的方式循环输出     

            copy(is, response.getOutputStream());     

          } finally {     

            is.close();     

          }     

          return;     

        }     

      }     

    }     

  }   

protectedvoid findStaticResource(String name, HttpServletRequest request, HttpServletResponse response) throws IOException {  

    if (!name.endsWith(".class")) {//忽略class文件  

     //遍历packages参数  

      for (String pathPrefix : pathPrefixes) {  

        InputStream is = findInputStream(name, pathPrefix);//读取请求文件流  

        if (is != null) {  

          ...  

          // set the content-type header  

          String contentType = getContentType(name);//读取内容类型  

          if (contentType != null) {   

            response.setContentType(contentType);//重新设置内容类型  

          }  

         ...  

          try {  

           //将读取到的文件流以每次复制4096个字节的方式循环输出  

            copy(is, response.getOutputStream());  

          } finally {  

            is.close();  

          }  

          return;  

        }  

      }  

    }  

  

 

 

如果用户请求的资源不是以/struts开头——可能是.jsp文件,也可能是.html文件,则通过过滤器链继续往下传送,直到到达请求的资源为止。

如果getMapping()方法返回有效的ActionMapping对象,则被认为正在请求某个Action,将调用 Dispatcher.serviceAction(request, response, servletContext, mapping)方法,该方法是处理Action的关键所在。

下面就来看serviceAction,这又回到全局变量dispatcher中了

 

//Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.     

public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,     

                              ActionMapping mapping) throws ServletException {     

        //createContextMap方法主要把ApplicationSessionRequestkey value值拷贝到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();     

            String name = mapping.getName();     

            String method = mapping.getMethod();     

    

            Configuration config = configurationManager.getConfiguration();     

            //创建一个Action的代理对象,ActionProxyFactory是创建ActionProxy的工厂     

            //参考实现类:DefaultActionProxyDefaultActionProxyFactory     

            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!     

            //如果是Result,则直接转向,关于Result,ActionProxy,ActionInvocation下一讲中再分析     

            if (mapping.getResult() != null) {     

                Result result = mapping.getResult();     

                result.execute(proxy.getInvocation());     

            } else {     

                //执行Action     

                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) {     

                LOG.error("Could not find action or result", 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);     

        }     

    }

下面开始讲一下主菜ActionProxy.在这之前最好先去了解一下动态Proxy的基本知识.

ActionProxyAction的一个代理类,也就是说Action的调用是通过ActionProxy实现的,其实就是调用了ActionProxy.execute()方法,而该方法又调用了ActionInvocation.invoke()方法。归根到底,最后调用的是DefaultActionInvocation.invokeAction()方法。

DefaultActionInvocation()->init()->createAction()

最后通过调用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()

这里的步骤是先由ActionProxyFactory创建ActionInvocationActionProxy.

 

public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {     

         

    ActionInvocation inv = new DefaultActionInvocation(extraContext, true);     

    container.inject(inv);     

    return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);     

}   

public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {  

      

    ActionInvocation inv = new DefaultActionInvocation(extraContext, true);  

    container.inject(inv);  

    return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);  

}  

 

 下面先看DefaultActionInvocationinit方法

 

public void init(ActionProxy proxy) {     

    this.proxy = proxy;     

    Map<String, Object> contextMap = createContextMap();     

    

    // Setting this so that other classes, like object factories, can use the ActionProxy and other     

    // contextual information to operate     

    ActionContext actionContext = ActionContext.getContext();     

    

    if (actionContext != null) {     

        actionContext.setActionInvocation(this);     

    }     

    //创建Action,struts2中每一个Request都会创建一个新的Action     

    createAction(contextMap);     

    

    if (pushAction) {     

        stack.push(action);     

        contextMap.put("action", action);     

    }     

    

    invocationContext = new ActionContext(contextMap);     

    invocationContext.setName(proxy.getActionName());     

    

    // get a new List so we don't get problems with the iterator if someone changes the list     

    List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());     

    interceptors = interceptorList.iterator();     

}     

         

protected void createAction(Map<String, Object> contextMap) {     

    // load action     

    String timerKey = "actionCreate: " + proxy.getActionName();     

    try {     

        UtilTimerStack.push(timerKey);     

        //默认为SpringObjectFactory:struts.objectFactory=spring.这里非常巧妙,struts.properties中可以重写这个属性     

        //在前面BeanSelectionProvider中通过配置文件为ObjectFactory设置实现类     

        //这里以Spring为例,这里会调到SpringObjectFactorybuildBean方法,可以通过ApplicationContextgetBean()方法得到SpringBean     

        action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);     

    } catch (InstantiationException e) {     

        throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());     

    } catch (IllegalAccessException e) {     

        throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());     

    } catch (Exception e) {     

       ...     

    } finally {     

        UtilTimerStack.pop(timerKey);     

    }     

    

    if (actionEventListener != null) {     

        action = actionEventListener.prepare(action, stack);     

    }     

}     

//SpringObjectFactory     

public Object buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception {     

    Object o = null;     

    try {     

        //SpringObjectFactory会通过web.xml中的context-param:contextConfigLocation自动注入ClassPathXmlApplicationContext     

        o = appContext.getBean(beanName);     

    } catch (NoSuchBeanDefinitionException e) {     

        Class beanClazz = getClassInstance(beanName);     

        o = buildBean(beanClazz, extraContext);     

    }     

    if (injectInternal) {     

        injectInternalBeans(o);     

    }     

    return o;     

}   

public void init(ActionProxy proxy) {  

    this.proxy = proxy;  

    Map<String, Object> contextMap = createContextMap();  

 

    // Setting this so that other classes, like object factories, can use the ActionProxy and other  

    // contextual information to operate  

    ActionContext actionContext = ActionContext.getContext();  

 

    if (actionContext != null) {  

        actionContext.setActionInvocation(this);  

    }  

    //创建Action,struts2中每一个Request都会创建一个新的Action  

    createAction(contextMap);  

 

    if (pushAction) {  

        stack.push(action);  

        contextMap.put("action", action);  

    }  

 

    invocationContext = new ActionContext(contextMap);  

    invocationContext.setName(proxy.getActionName());  

 

    // get a new List so we don't get problems with the iterator if someone changes the list  

    List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());  

    interceptors = interceptorList.iterator();  

}  

      

protected void createAction(Map<String, Object> contextMap) {  

    // load action  

    String timerKey = "actionCreate: " + proxy.getActionName();  

    try {  

        UtilTimerStack.push(timerKey);  

        //默认为SpringObjectFactory:struts.objectFactory=spring.这里非常巧妙,struts.properties中可以重写这个属性  

        //在前面BeanSelectionProvider中通过配置文件为ObjectFactory设置实现类  

        //这里以Spring为例,这里会调到SpringObjectFactorybuildBean方法,可以通过ApplicationContextgetBean()方法得到SpringBean  

        action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);  

    } catch (InstantiationException e) {  

        throw new XWorkException("Unable to intantiate Action!", e, proxy.getConfig());  

    } catch (IllegalAccessException e) {  

        throw new XWorkException("Illegal access to constructor, is it public?", e, proxy.getConfig());  

    } catch (Exception e) {  

       ...  

    } finally {  

        UtilTimerStack.pop(timerKey);  

    }  

 

    if (actionEventListener != null) {  

        action = actionEventListener.prepare(action, stack);   

    }  

}  

//SpringObjectFactory  

public Object buildBean(String beanName, Map<String, Object> extraContext, boolean injectInternal) throws Exception {  

    Object o = null;  

    try {  

        //SpringObjectFactory会通过web.xml中的context-param:contextConfigLocation自动注入ClassPathXmlApplicationContext  

        o = appContext.getBean(beanName);  

    } catch (NoSuchBeanDefinitionException e) {  

        Class beanClazz = getClassInstance(beanName);  

        o = buildBean(beanClazz, extraContext);  

    }  

    if (injectInternal) {  

        injectInternalBeans(o);  

    }  

    return o;  

 

 

//接下来看看DefaultActionInvocation invoke方法     

public String invoke() throws Exception {     

    String profileKey = "invoke: ";     

    try {     

        UtilTimerStack.push(profileKey);     

        

        if (executed) {     

            throw new IllegalStateException("Action has already executed");     

        }     

        //递归执行interceptor     

        if (interceptors.hasNext()) {     

            //interceptorsInterceptorMapping实际上是像一个像FilterChain一样的Interceptor     

            //通过调用Invocation.invoke()实现递归牡循环     

            final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();     

            String interceptorMsg = "interceptor: " + interceptor.getName();     

            UtilTimerStack.push(interceptorMsg);     

            try {       

                 //在每个Interceptor的方法中都会return invocation.invoke()            

                 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);     

                }     

            finally {     

                UtilTimerStack.pop(interceptorMsg);     

            }     

        } else {       

            //当所有interceptor都执行完,最后执行Action,invokeActionOnly会调用invokeAction()方法     

            resultCode = invokeActionOnly();     

        }     

    

        // this is needed because the result will be executed, then control will return to the Interceptor, which will     

        // return above and flow through again       

        //Result返回之前调用preResultListeners      

        //通过executed控制,只执行一次      

        if (!executed) {     

            if (preResultListeners != null) {      

                for (Object preResultListener : preResultListeners) {      

                    PreResultListener listener = (PreResultListener) preResultListener;     

                                                                      

                    String _profileKey = "preResultListener: ";      

                    try {                                            

                        UtilTimerStack.push(_profileKey);                                   

                        listener.beforeResult(this, resultCode);     

                    }                                                

                    finally {                                        

                        UtilTimerStack.pop(_profileKey);             

                    }                                                

                }                                                    

            }                                                        

                                                                     

            // now execute the result, if we're supposed to          

            //执行Result                                             

            if (proxy.getExecuteResult()) {                           

                executeResult();                                     

            }                                                        

                                                                     

            executed = true;                                         

        }                                                            

                                                                     

        return resultCode;                                            

    }                                                                

    finally {                                                        

        UtilTimerStack.pop(profileKey);                              

    }                                                                 

}      

    

//invokeAction     

protected String invokeAction(Object action,ActionConfig actionConfig)throws Exception{     

    String methodName = proxy.getMethod();     

    

    String timerKey = "invokeAction: " + proxy.getActionName();     

    try {     

        UtilTimerStack.push(timerKey);     

    

        boolean methodCalled = false;     

        Object methodResult = null;     

        Method method = null;     

        try {     

            //java反射机制得到要执行的方法     

            method = getAction().getClass().getMethod(methodName, new Class[0]);     

        } catch (NoSuchMethodException e) {     

            // hmm -- OK, try doXxx instead     

            //如果没有对应的方法,则使用do+Xxxx来再次获得方法        

            try {     

                String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);     

                method = getAction().getClass().getMethod(altMethodName, new Class[0]);     

            } catch (NoSuchMethodException e1) {     

                // well, give the unknown handler a shot     

                if (unknownHandlerManager.hasUnknownHandlers()) {     

                    try {     

                        methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);     

                        methodCalled = true;     

                    } catch (NoSuchMethodException e2) {     

                        // throw the original one     

                        throw e;     

                    }     

                } else {     

                    throw e;     

                }     

            }     

        }     

        //执行Method     

        if (!methodCalled) {     

            methodResult = method.invoke(action, new Object[0]);     

        }     

        //从这里可以看出可以Action的方法可以返回String去匹配Result,也可以直接返回Result     

        if (methodResult instanceof Result) {     

            this.explicitResult = (Result) methodResult;     

    

            // Wire the result automatically     

            container.inject(explicitResult);     

            return null;     

        } else {     

            return (String) methodResult;     

        }     

    } catch (NoSuchMethodException e) {     

        throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");     

    } catch (InvocationTargetException e) {     

        // We try to return the source exception.      

        Throwable t = e.getTargetException();     

    

        if (actionEventListener != null) {     

            String result = actionEventListener.handleException(t, getStack());     

            if (result != null) {     

                return result;     

            }     

        }     

        if (t instanceof Exception) {     

            throw (Exception) t;     

        } else {     

            throw e;     

        }     

    } finally {     

        UtilTimerStack.pop(timerKey);     

    }     

}   

//接下来看看DefaultActionInvocation invoke方法  

public String invoke() throws Exception {  

    String profileKey = "invoke: ";  

    try {  

        UtilTimerStack.push(profileKey);  

     

        if (executed) {  

            throw new IllegalStateException("Action has already executed");  

        }  

        //递归执行interceptor  

        if (interceptors.hasNext()) {  

            //interceptorsInterceptorMapping实际上是像一个像FilterChain一样的Interceptor  

            //通过调用Invocation.invoke()实现递归牡循环  

            final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();  

            String interceptorMsg = "interceptor: " + interceptor.getName();  

            UtilTimerStack.push(interceptorMsg);  

            try {    

                 //在每个Interceptor的方法中都会return invocation.invoke()         

                 resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);  

                }  

            finally {  

                UtilTimerStack.pop(interceptorMsg);  

            }  

        } else {    

            //当所有interceptor都执行完,最后执行Action,invokeActionOnly会调用invokeAction()方法  

            resultCode = invokeActionOnly();  

        }  

 

        // this is needed because the result will be executed, then control will return to the Interceptor, which will  

        // return above and flow through again    

        //Result返回之前调用preResultListeners   

        //通过executed控制,只执行一次   

        if (!executed) {  

            if (preResultListeners != null) {   

                for (Object preResultListener : preResultListeners) {   

                    PreResultListener listener = (PreResultListener) preResultListener;  

                                                                   

                    String _profileKey = "preResultListener: ";   

                    try {                                         

                        UtilTimerStack.push(_profileKey);                                

                        listener.beforeResult(this, resultCode);  

                    }                                             

                    finally {                                     

                        UtilTimerStack.pop(_profileKey);          

                    }                                             

                }                                                 

            }                                                     

                                                                   

            // now execute the result, if we're supposed to       

            //执行Result                                          

            if (proxy.getExecuteResult()) {                       

                executeResult();                                  

            }                                                     

                                                                  

            executed = true;                                       

        }                                                         

                                                                  

        return resultCode;                                        

    }                                                             

    finally {                                                     

        UtilTimerStack.pop(profileKey);                           

    }                                                              

}   

 

//invokeAction  

protected String invokeAction(Object action,ActionConfig actionConfig)throws Exception{  

    String methodName = proxy.getMethod();  

 

    String timerKey = "invokeAction: " + proxy.getActionName();  

    try {  

        UtilTimerStack.push(timerKey);  

 

        boolean methodCalled = false;  

        Object methodResult = null;  

        Method method = null;  

        try {  

            //java反射机制得到要执行的方法  

            method = getAction().getClass().getMethod(methodName, new Class[0]);  

        } catch (NoSuchMethodException e) {  

            // hmm -- OK, try doXxx instead  

            //如果没有对应的方法,则使用do+Xxxx来再次获得方法     

            try {  

                String altMethodName = "do" + methodName.substring(0, 1).toUpperCase() + methodName.substring(1);  

                method = getAction().getClass().getMethod(altMethodName, new Class[0]);  

            } catch (NoSuchMethodException e1) {  

                // well, give the unknown handler a shot  

                if (unknownHandlerManager.hasUnknownHandlers()) {  

                    try {  

                        methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);  

                        methodCalled = true;  

                    } catch (NoSuchMethodException e2) {  

                        // throw the original one  

                        throw e;  

                    }  

                } else {  

                    throw e;  

                }  

            }  

        }  

        //执行Method  

        if (!methodCalled) {  

            methodResult = method.invoke(action, new Object[0]);  

        }  

        //从这里可以看出可以Action的方法可以返回String去匹配Result,也可以直接返回Result  

        if (methodResult instanceof Result) {  

            this.explicitResult = (Result) methodResult;  

 

            // Wire the result automatically  

            container.inject(explicitResult);  

            return null;  

        } else {  

            return (String) methodResult;  

        }  

    } catch (NoSuchMethodException e) {  

        throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");  

    } catch (InvocationTargetException e) {  

        // We try to return the source exception.  

        Throwable t = e.getTargetException();  

 

        if (actionEventListener != null) {  

            String result = actionEventListener.handleException(t, getStack());  

            if (result != null) {  

                return result;  

            }  

        }  

        if (t instanceof Exception) {  

            throw (Exception) t;  

        } else {  

            throw e;  

        }  

    } finally {  

        UtilTimerStack.pop(timerKey);  

    }  

}  

 

action执行完了,还要根据ResultConfig返回到view,也就是在invoke方法中调用executeResult方法。

 

private void executeResult() throws Exception {     

    //根据ResultConfig创建Result      

    result = createResult();     

    

    String timerKey = "executeResult: " + getResultCode();     

    try {     

        UtilTimerStack.push(timerKey);     

        if (result != null) {     

        //开始执行Result,     

        //可以参考Result的实现,如用了比较多的ServletDispatcherResult,ServletActionRedirectResult,ServletRedirectResult      

            result.execute(this);     

        } else if (resultCode != null && !Action.NONE.equals(resultCode)) {     

            throw new ConfigurationException("No result defined for action " + getAction().getClass().getName()     

                    + " and result " + getResultCode(), proxy.getConfig());     

        } else {     

            if (LOG.isDebugEnabled()) {     

                LOG.debug("No result returned for action " + getAction().getClass().getName() + " at " + proxy.getConfig().getLocation());     

            }     

        }     

    } finally {     

        UtilTimerStack.pop(timerKey);     

    }     

}               

    

public Result createResult() throws Exception {     

    //如果Action中直接返回的Result类型,invokeAction()保存在explicitResult     

    if (explicitResult != null) {                                

        Result ret = explicitResult;                             

        explicitResult = null;                                   

                                                                 

        return ret;                                              

    }     

    //返回的是String则从config中得到当前ActionResults列表     

    ActionConfig config = proxy.getConfig();                     

    Map<String, ResultConfig> results = config.getResults();     

                                                                 

    ResultConfig resultConfig = null;                            

                                                                 

    synchronized (config) {                                      

        try {      

            //通过返回的String来匹配resultConfig       

            resultConfig = results.get(resultCode);              

        } catch (NullPointerException e) {                       

            // swallow                                           

        }                                                         

        if (resultConfig == null) {                              

            // If no result is found for the given resultCode, try to get a wildcard '*' match.     

            //如果找不到对应nameResultConfig,则使用name*Result        

            //说明可以用*通配所有的Result                                   

            resultConfig = results.get("*");     

        }                                        

    }                                            

                                                  

    if (resultConfig != null) {                  

        try {     

            //创建Result      

            return objectFactory.buildResult(resultConfig, invocationContext.getContextMap());     

        } catch (Exception e) {     

            LOG.error("There was an exception while instantiating the result of type " + resultConfig.getClassName(), e);     

            throw new XWorkException(e, resultConfig);     

        }      

    } else if (resultCode != null && !Action.NONE.equals(resultCode) && unknownHandlerManager.hasUnknownHandlers()) {     

        return unknownHandlerManager.handleUnknownResult(invocationContext, proxy.getActionName(), proxy.getConfig(), resultCode);     

    }                 

    return null;     

}        

    

public Result buildResult(ResultConfig resultConfig, Map<String, Object> extraContext) throws Exception {     

    String resultClassName = resultConfig.getClassName();     

    Result result = null;                                     

                                                              

    if (resultClassName != null) {     

        //buildBean中会用反射机制Class.newInstance来创建bean      

        result = (Result) buildBean(resultClassName, extraContext);     

        Map<String, String> params = resultConfig.getParams();          

        if (params != null) {                                           

            for (Map.Entry<String, String> paramEntry : params.entrySet()) {     

                try {     

                     //reflectionProvider参见OgnlReflectionProvider     

              //resultConfig.getParams()就是result配置文件里所配置的参数<param></param>      

                     //setProperties方法最终调用的是Ognl类的setValue方法        

              //这句其实就是把param名值设置到根对象result     

                    reflectionProvider.setProperty(paramEntry.getKey(), paramEntry.getValue(), result, extraContext, true);     

                } catch (ReflectionException ex) {      

                    if (LOG.isErrorEnabled())           

                        LOG.error("Unable to set parameter [#0] in result of type [#1]", ex,     

                                paramEntry.getKey(), resultConfig.getClassName());     

                    if (result instanceof ReflectionExceptionHandler) {                

                        ((ReflectionExceptionHandler) result).handle(ex);              

                    }     

                }         

            }             

        }                 

    }                     

                          

    return result;        

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值