如何深入理解struts2工作流程及其源码细节?

2026-04-02 15:241阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计1666个文字,预计阅读时间需要7分钟。

如何深入理解struts2工作流程及其源码细节?

Struts2架构图:请求首先通过FilterchainFilter,主要包含ActionContextCleanUp,它主要清理当前线程的ActionContext。

Struts2架构图请求首先通过FilterchainFilter主要包括ActionContextCleanUp它主要清理当前线程的ActionC

Struts2架构图

  

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

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

Struts2部分类介绍

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

ActionMapper

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

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

ConfigurationProviderStruts2中的配置文件主要是尤其实现类XmlConfigurationProvider及其子类StrutsXmlConfigurationProvider来解析

Struts2请求流程

1、客户端发送请求

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

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

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

5、ActionProxy根据ActionMapping和ConfigurationManager找到需要调用的Action类

6、ActionProxy创建一个ActionInvocation的实例

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

8、Action执行完毕ActionInvocation创建Result并返回当然如果要在返回之前做些什么可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现不知道其它还有什么方式

Struts2(2.1.2)部分源码阅读

    从org.apache.struts2.dispatcher.FilterDispatcher开始

    //创建Dispatcher此类是一个Delegate它是真正完成根据url解析读取对应Action的地方     public void init(FilterConfig filterConfig) throws ServletException {         try {             this.filterConfig  filterConfig;             initLogging();             dispatcher  createDispatcher(filterConfig);             dispatcher.init();             dispatcher.getContainer().inject(this);             //读取初始参数pakages调用parse()解析成类似/org/apache/struts2/static,/template的数组             String param  filterConfig.getInitParameter("packages");             String packages  "org.apache.struts2.static template org.apache.struts2.interceptor.debugging";             if (param ! null) {                 packages  param  " "  packages;             }             this.pathPrefixes  parse(packages);         } finally {             ActionContext.setContext(null);         }     }

     顺着流程我们看Disptcher的init方法。init方法里就是初始读取一些配置文件等先看init_DefaultProperties主要是读取properties配置文件。

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

    打开DefaultPropertiesProvider

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

    再来看init_TraditionalXmlConfigurations方法这个是读取struts-default.xml和Struts.xml的方法。 

    private void init_TraditionalXmlConfigurations() {         //首先读取web.xml中的config初始参数值         //如果没有配置就使用默认的"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*");         //依次解析配置文件xwork.xml单独解析         for (String file : files) {             if (file.endsWith(".xml")) {                 if ("xwork.xml".equals(file)) {                     configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));                 } else {                     configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));                 }             } else {                 throw new IllegalArgumentException("Invalid configuration file name");             }         }     }

    对于其它配置文件只用StrutsXmlConfigurationProvider此类继承XmlConfigurationProvider而XmlConfigurationProvider又实现ConfigurationProvider接口。类XmlConfigurationProvider负责配置文件的读取和解析addAction()方法负责读取标签并将数据保存在ActionConfig中addResultTypes()方法负责将标签转化为ResultTypeConfig对象loadInterceptors()方法负责将标签转化为InterceptorConfi对象loadInterceptorStack()方法负责将标签转化为InterceptorStackConfig对象loadInterceptorStacks()方法负责将标签转化成InterceptorStackConfig对象。而上面的方法最终会被addPackage()方法调用将所读取到的数据汇集到PackageConfig对象中。来看XmlConfigurationProvider的源代码详细的我自己也就大体浏览了一下各位可以自己研读。

    protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {         PackageConfig.Builder newPackage  buildPackageContext(packageElement);         if (newPackage.isNeedsRefresh()) {             return newPackage.build();         }         .         addResultTypes(newPackage, packageElement);         loadInterceptors(newPackage, packageElement);         loadDefaultInterceptorRef(newPackage, packageElement);         loadDefaultClassRef(newPackage, packageElement);         loadGlobalResults(newPackage, packageElement);         loadGobalExceptionMappings(newPackage, packageElement);         NodeList actionList  packageElement.getElementsByTagName("action");         for (int i  0; i < actionList.getLength(); i) {             Element actionElement  (Element) actionList.item(i);             addAction(actionElement, newPackage);         }         loadDefaultActionRef(newPackage, packageElement);         PackageConfig cfg  newPackage.build();         configuration.addPackageConfig(cfg.getName(), cfg);         return cfg;     }

    这儿发现一个配置上的小技巧我的xwork2.0.*是没有的但是看源码是看到xwork2.1.*是可以的。继续看XmlConfigurationProvider的源代码

如何深入理解struts2工作流程及其源码细节?

    private List loadConfigurationFiles(String fileName, Element includeElement) {         List docs  new ArrayList();         if (!includedFileNames.contains(fileName)) {                                  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();                         //解析每个action配置是对于include文件可以使用通配符*来进行配置                         //如Struts.xml中可配置成                         if (nodeName.equals("include")) {                             String includeFileName  child.getAttribute("file");                             if(includeFileName.indexOf(*) ! -1 ) {                                 ClassPathFinder wildcardFinder  new ClassPathFinder();                                 wildcardFinder.setPattern(includeFileName);                                 Vector wildcardMatches  wildcardFinder.findMatches();                                 for (String match : wildcardMatches) {                                     docs.addAll(loadConfigurationFiles(match, child));                                 }                             }                             else {                                                                  docs.addAll(loadConfigurationFiles(includeFileName, child));                                 }                         }                 }                 }                 docs.add(doc);                 loadedFileUrls.add(url.toString());             }         }         return docs;     }

    init_CustomConfigurationProviders方式初始自定义的Provider配置类全名和实现ConfigurationProvider接口用逗号隔开即可。 

    private void init_CustomConfigurationProviders() {         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);                 }                              }         }     }

    好了现在再回到FilterDispatcher每次发送一个RequestFilterDispatcher都会调用doFilter方法。

    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 {             ValueStack stack  dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();             ActionContext ctx  new ActionContext(stack.getContext());             ActionContext.setContext(ctx);                          UtilTimerStack.push(timerKey);             //根据content type来使用不同的Request封装可以参见Dispatcher的wrapRequest             request  prepareDispatcherAndWrapRequest(request, response);             ActionMapping mapping;             try {                 //根据url取得对应的Action的配置信息--ActionMappingactionMapper是通过Container的inject注入的                 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)  request.getPathInfo()) {                     resourcePath  request.getPathInfo();                 }                 if (serveStatic  resourcePath.substring("/struts".length());                     findStaticResource(name, request, response);                 } else {                     chain.doFilter(request, response);                 }                 return;             }             //正式开始Action的方法了             dispatcher.serviceAction(request, response, servletContext, mapping);         } finally {             try {                 ActionContextCleanUp.cleanUp(req);             } finally {                 UtilTimerStack.pop(timerKey);             }         }     }

    Dispatcher类的serviceAction方法

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping) throws ServletException {         Map 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);         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();             ActionProxy proxy  config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(                     namespace, name, method, extraContext, true, false);             request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());             // if the ActionMapping says to go straight to a result, do it!             if (mapping.getResult() ! null) {                 Result result  mapping.getResult();                 result.execute(proxy.getInvocation());             } else {                 proxy.execute();             }             // If there was a previous value stack then set it back onto the request             if (stack ! null) {                 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);             }         } catch (ConfigurationException e) {             LOG.error("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);         }     }

    第一句createContextMap()方法该方法主要把Application、Session、Request的key value值拷贝到Map中并放在HashMap中可以参见createContextMap方法

    public Map createContextMap(HttpServletRequest request, HttpServletResponse response,             ActionMapping mapping, ServletContext context) {         // request map wrapping the www.ognl.org/我在网上也找到另外一篇www.javaeye.com/topic/254684和www.javaeye.com/topic/223612。完了来看下面这段小测试程序其它的Ognl的测试可以自己添加。

public class TestOgnl {          private User user;     private Map context;          Before     public void setUp() throws Exception {          }     Test     public void ognlGetValue() throws Exception {     reset();     Assert.assertEquals("myyate", Ognl.getValue("name", user));     Assert.assertEquals("cares", Ognl.getValue("dept.name", user));     Assert.assertEquals("myyate", Ognl.getValue("name", context, user));     Assert.assertEquals("contextmap", Ognl.getValue("#name", context, user));     Assert.assertEquals("parker", Ognl.getValue("#pen", context, user));     }          Test     public void ognlSetValue() throws Exception {     reset();     Ognl.setValue("name", user, "myyateC");     Assert.assertEquals("myyateC", Ognl.getValue("name", user));          Ognl.setValue("dept.name", user, "caresC");     Assert.assertEquals("caresC", Ognl.getValue("dept.name", user));          Assert.assertEquals("contextmap", Ognl.getValue("#name", context, user));     Ognl.setValue("#name", context, user, "contextmapC");     Assert.assertEquals("contextmapC", Ognl.getValue("#name", context, user));          Assert.assertEquals("parker", Ognl.getValue("#pen", context, user));     Ognl.setValue("#name", context, user, "parkerC");     Assert.assertEquals("parkerC", Ognl.getValue("#name", context, user));     }          public static void main(String[] args) throws Exception {     JUnitCore.runClasses(TestOgnl.class);     }          private void reset() {     user  new User("myyate", new Dept("cares"));     context  new OgnlContext();     context.put("pen", "parker");     context.put("name", "contextmap");     } } class User {     public User(String name, Dept dept) {     this.name  name;     this.dept  dept;     }     String name;     private Dept dept;     public Dept getDept() {         return dept;     }     public String getName() {         return name;     }     public void setDept(Dept dept) {         this.dept  dept;     }     public void setName(String name) {         this.name  name;     } } class Dept {     public Dept(String name) {     this.name  name;     }     private String name;     public String getName() {         return name;     }     public void setName(String name) {         this.name  name;     } }

  

本文共计1666个文字,预计阅读时间需要7分钟。

如何深入理解struts2工作流程及其源码细节?

Struts2架构图:请求首先通过FilterchainFilter,主要包含ActionContextCleanUp,它主要清理当前线程的ActionContext。

Struts2架构图请求首先通过FilterchainFilter主要包括ActionContextCleanUp它主要清理当前线程的ActionC

Struts2架构图

  

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

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

Struts2部分类介绍

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

ActionMapper

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

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

ConfigurationProviderStruts2中的配置文件主要是尤其实现类XmlConfigurationProvider及其子类StrutsXmlConfigurationProvider来解析

Struts2请求流程

1、客户端发送请求

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

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

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

5、ActionProxy根据ActionMapping和ConfigurationManager找到需要调用的Action类

6、ActionProxy创建一个ActionInvocation的实例

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

8、Action执行完毕ActionInvocation创建Result并返回当然如果要在返回之前做些什么可以实现PreResultListener。添加PreResultListener可以在Interceptor中实现不知道其它还有什么方式

Struts2(2.1.2)部分源码阅读

    从org.apache.struts2.dispatcher.FilterDispatcher开始

    //创建Dispatcher此类是一个Delegate它是真正完成根据url解析读取对应Action的地方     public void init(FilterConfig filterConfig) throws ServletException {         try {             this.filterConfig  filterConfig;             initLogging();             dispatcher  createDispatcher(filterConfig);             dispatcher.init();             dispatcher.getContainer().inject(this);             //读取初始参数pakages调用parse()解析成类似/org/apache/struts2/static,/template的数组             String param  filterConfig.getInitParameter("packages");             String packages  "org.apache.struts2.static template org.apache.struts2.interceptor.debugging";             if (param ! null) {                 packages  param  " "  packages;             }             this.pathPrefixes  parse(packages);         } finally {             ActionContext.setContext(null);         }     }

     顺着流程我们看Disptcher的init方法。init方法里就是初始读取一些配置文件等先看init_DefaultProperties主要是读取properties配置文件。

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

    打开DefaultPropertiesProvider

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

    再来看init_TraditionalXmlConfigurations方法这个是读取struts-default.xml和Struts.xml的方法。 

    private void init_TraditionalXmlConfigurations() {         //首先读取web.xml中的config初始参数值         //如果没有配置就使用默认的"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*");         //依次解析配置文件xwork.xml单独解析         for (String file : files) {             if (file.endsWith(".xml")) {                 if ("xwork.xml".equals(file)) {                     configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));                 } else {                     configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));                 }             } else {                 throw new IllegalArgumentException("Invalid configuration file name");             }         }     }

    对于其它配置文件只用StrutsXmlConfigurationProvider此类继承XmlConfigurationProvider而XmlConfigurationProvider又实现ConfigurationProvider接口。类XmlConfigurationProvider负责配置文件的读取和解析addAction()方法负责读取标签并将数据保存在ActionConfig中addResultTypes()方法负责将标签转化为ResultTypeConfig对象loadInterceptors()方法负责将标签转化为InterceptorConfi对象loadInterceptorStack()方法负责将标签转化为InterceptorStackConfig对象loadInterceptorStacks()方法负责将标签转化成InterceptorStackConfig对象。而上面的方法最终会被addPackage()方法调用将所读取到的数据汇集到PackageConfig对象中。来看XmlConfigurationProvider的源代码详细的我自己也就大体浏览了一下各位可以自己研读。

    protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {         PackageConfig.Builder newPackage  buildPackageContext(packageElement);         if (newPackage.isNeedsRefresh()) {             return newPackage.build();         }         .         addResultTypes(newPackage, packageElement);         loadInterceptors(newPackage, packageElement);         loadDefaultInterceptorRef(newPackage, packageElement);         loadDefaultClassRef(newPackage, packageElement);         loadGlobalResults(newPackage, packageElement);         loadGobalExceptionMappings(newPackage, packageElement);         NodeList actionList  packageElement.getElementsByTagName("action");         for (int i  0; i < actionList.getLength(); i) {             Element actionElement  (Element) actionList.item(i);             addAction(actionElement, newPackage);         }         loadDefaultActionRef(newPackage, packageElement);         PackageConfig cfg  newPackage.build();         configuration.addPackageConfig(cfg.getName(), cfg);         return cfg;     }

    这儿发现一个配置上的小技巧我的xwork2.0.*是没有的但是看源码是看到xwork2.1.*是可以的。继续看XmlConfigurationProvider的源代码

如何深入理解struts2工作流程及其源码细节?

    private List loadConfigurationFiles(String fileName, Element includeElement) {         List docs  new ArrayList();         if (!includedFileNames.contains(fileName)) {                                  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();                         //解析每个action配置是对于include文件可以使用通配符*来进行配置                         //如Struts.xml中可配置成                         if (nodeName.equals("include")) {                             String includeFileName  child.getAttribute("file");                             if(includeFileName.indexOf(*) ! -1 ) {                                 ClassPathFinder wildcardFinder  new ClassPathFinder();                                 wildcardFinder.setPattern(includeFileName);                                 Vector wildcardMatches  wildcardFinder.findMatches();                                 for (String match : wildcardMatches) {                                     docs.addAll(loadConfigurationFiles(match, child));                                 }                             }                             else {                                                                  docs.addAll(loadConfigurationFiles(includeFileName, child));                                 }                         }                 }                 }                 docs.add(doc);                 loadedFileUrls.add(url.toString());             }         }         return docs;     }

    init_CustomConfigurationProviders方式初始自定义的Provider配置类全名和实现ConfigurationProvider接口用逗号隔开即可。 

    private void init_CustomConfigurationProviders() {         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);                 }                              }         }     }

    好了现在再回到FilterDispatcher每次发送一个RequestFilterDispatcher都会调用doFilter方法。

    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 {             ValueStack stack  dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();             ActionContext ctx  new ActionContext(stack.getContext());             ActionContext.setContext(ctx);                          UtilTimerStack.push(timerKey);             //根据content type来使用不同的Request封装可以参见Dispatcher的wrapRequest             request  prepareDispatcherAndWrapRequest(request, response);             ActionMapping mapping;             try {                 //根据url取得对应的Action的配置信息--ActionMappingactionMapper是通过Container的inject注入的                 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)  request.getPathInfo()) {                     resourcePath  request.getPathInfo();                 }                 if (serveStatic  resourcePath.substring("/struts".length());                     findStaticResource(name, request, response);                 } else {                     chain.doFilter(request, response);                 }                 return;             }             //正式开始Action的方法了             dispatcher.serviceAction(request, response, servletContext, mapping);         } finally {             try {                 ActionContextCleanUp.cleanUp(req);             } finally {                 UtilTimerStack.pop(timerKey);             }         }     }

    Dispatcher类的serviceAction方法

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping) throws ServletException {         Map 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);         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();             ActionProxy proxy  config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(                     namespace, name, method, extraContext, true, false);             request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());             // if the ActionMapping says to go straight to a result, do it!             if (mapping.getResult() ! null) {                 Result result  mapping.getResult();                 result.execute(proxy.getInvocation());             } else {                 proxy.execute();             }             // If there was a previous value stack then set it back onto the request             if (stack ! null) {                 request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);             }         } catch (ConfigurationException e) {             LOG.error("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);         }     }

    第一句createContextMap()方法该方法主要把Application、Session、Request的key value值拷贝到Map中并放在HashMap中可以参见createContextMap方法

    public Map createContextMap(HttpServletRequest request, HttpServletResponse response,             ActionMapping mapping, ServletContext context) {         // request map wrapping the www.ognl.org/我在网上也找到另外一篇www.javaeye.com/topic/254684和www.javaeye.com/topic/223612。完了来看下面这段小测试程序其它的Ognl的测试可以自己添加。

public class TestOgnl {          private User user;     private Map context;          Before     public void setUp() throws Exception {          }     Test     public void ognlGetValue() throws Exception {     reset();     Assert.assertEquals("myyate", Ognl.getValue("name", user));     Assert.assertEquals("cares", Ognl.getValue("dept.name", user));     Assert.assertEquals("myyate", Ognl.getValue("name", context, user));     Assert.assertEquals("contextmap", Ognl.getValue("#name", context, user));     Assert.assertEquals("parker", Ognl.getValue("#pen", context, user));     }          Test     public void ognlSetValue() throws Exception {     reset();     Ognl.setValue("name", user, "myyateC");     Assert.assertEquals("myyateC", Ognl.getValue("name", user));          Ognl.setValue("dept.name", user, "caresC");     Assert.assertEquals("caresC", Ognl.getValue("dept.name", user));          Assert.assertEquals("contextmap", Ognl.getValue("#name", context, user));     Ognl.setValue("#name", context, user, "contextmapC");     Assert.assertEquals("contextmapC", Ognl.getValue("#name", context, user));          Assert.assertEquals("parker", Ognl.getValue("#pen", context, user));     Ognl.setValue("#name", context, user, "parkerC");     Assert.assertEquals("parkerC", Ognl.getValue("#name", context, user));     }          public static void main(String[] args) throws Exception {     JUnitCore.runClasses(TestOgnl.class);     }          private void reset() {     user  new User("myyate", new Dept("cares"));     context  new OgnlContext();     context.put("pen", "parker");     context.put("name", "contextmap");     } } class User {     public User(String name, Dept dept) {     this.name  name;     this.dept  dept;     }     String name;     private Dept dept;     public Dept getDept() {         return dept;     }     public String getName() {         return name;     }     public void setDept(Dept dept) {         this.dept  dept;     }     public void setName(String name) {         this.name  name;     } } class Dept {     public Dept(String name) {     this.name  name;     }     private String name;     public String getName() {         return name;     }     public void setName(String name) {         this.name  name;     } }