zoukankan      html  css  js  c++  java
  • Struts2源码浅析初始化(转载)

    本文参考自:http://blog.csdn.net/java2000_wl/article/details/7572828

    如果有侵犯您知识产权的地方,烦请通知本人,本人将即刻停止侵权行为

    --------begin

       在web.xml中配置的StrutsPrepareAndExecuteFilter类是struts2的入口类,它实现了Filter接口 ,它的init

    方法为初始化入口,下面是它的init方法:

     1     public void init(FilterConfig filterConfig) throws ServletException {
     2         InitOperations init = new InitOperations();
     3         try {
     4             filterHostConfig类对FilterConfig类进行了封装,使用了装饰模式
     5             FilterHostConfig config = new FilterHostConfig(filterConfig);
     6             初始化日志工厂:如果在web.xml中没有配置logFactory参数,则使用
     7             com.opensymphony.xwork2.util.logging.commons.CommonsLoggerFactory()
     8             如果没有该类,则使用JdkLoggerFactory,详情请参考LoggerFactory类
     9             init.initLogging(config);
    10             创建DisPatcher,初始化DisPatcher,注册加载各种配置文件的加载器,执行加载器,
    11             创建容器,解析XML文件
    12             Dispatcher dispatcher = init.initDispatcher(config);
    13             init.initStaticContentLoader(config, dispatcher);
    14             预处理类 请求处理时才会真正用到  
    15             1.主要负责在每次请求 创建ActionContext 清除ActionContext  
    16             2.当接收到一个请求时 通过uri查找 ActionConfig 创建ActionMapping  
    17             prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
    18             处理请求            
    19             execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
    20             this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
    21 
    22             postInit(dispatcher, filterConfig);
    23         } finally {
    24             init.cleanup();
    25         }
    26 
    27     }

          InitOperations 类似与一个Delegate(代理) 主要负责实例化Dispatcher ,再把初始化操作转交给

    Dispatcher自己的init方法处理:初始化Dispatcher类:

    1  /**
    2      * Creates and initializes the dispatcher
    3      */
    4     public Dispatcher initDispatcher( HostConfig filterConfig ) {
    5         Dispatcher dispatcher = createDispatcher(filterConfig);创建dispacher类
    6         dispatcher.init();dispatcher的初始化,在这里运行转移到dispatcher类,进行注册xml文件的加载器和运行加载器
    7         return dispatcher;
    8     }

    创建Dispatcher类:

    将web.xml文件中配置的启动参数独取出来,其中参数已经被初始化到HostConfig类中,将启动参数封装到

    dispather类的一个属性中:params中,便于统一管理

     1   /**
     2      * Create a {@link Dispatcher}
     3      */
     4     private Dispatcher createDispatcher( HostConfig filterConfig ) {
     5         Map<String, String> params = new HashMap<String, String>();
     6         for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
     7             String name = (String) e.next();
     8             String value = filterConfig.getInitParameter(name);
     9             params.put(name, value);
    10         }
    11         return new Dispatcher(filterConfig.getServletContext(), params);
    12     }

    Dispatcher的init方法,主要完成了以下几个方面的功能:

    1、注册各种struts2配置文件的加载器

    2、执行加载器

    3、解析各种配置文件:主要包括XML和properties两种配置,struts2向用户预留了加载其他种类配置文件的类的接

    口,在后面我们将会进行介绍

    4、创建容器,实现容器的依赖注入功能

     1  /**
     2      * Load configurations, including both XML and zero-configuration strategies,
     3      * and update optional settings, including whether to reload configurations and resource files.
     4      */
     5     public void init() {
     6         如果当前的configurationManager对象为空,则创建一个默认的ConfigurationManager对象
     7         if (configurationManager == null) {
     8             configurationManager = new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
     9         }
    10 
    11         try {
    12             init_DefaultProperties(); // [1]
    13             init_TraditionalXmlConfigurations(); // [2]
    14             init_LegacyStrutsProperties(); // [3]
    15             init_CustomConfigurationProviders(); // [5]用户自定义的加载器的实现
    16             init_FilterInitParameters() ; // [6]
    17             init_AliasStandardObjects() ; // [7]
    18 
    19             Container container = init_PreloadConfiguration();
    20             container.inject(this);
    21             init_CheckConfigurationReloading(container);
    22             init_CheckWebLogicWorkaround(container);
    23 
    24             if (!dispatcherListeners.isEmpty()) {
    25                 for (DispatcherListener l : dispatcherListeners) {
    26                     l.dispatcherInitialized(this);
    27                 }
    28             }
    29         } catch (Exception ex) {
    30             if (LOG.isErrorEnabled())
    31                 LOG.error("Dispatcher initialization failed", ex);
    32             throw new StrutsException(ex);
    33         }
    34     }

    初始化各种形式加载器,保存到ConfigurationManager#containerProviders Map集合中 没有真正执行加载 解析逻辑

    对上述几个步骤初始化的文件进行介绍:

    [1]:初始化stuts2默认的properties文件加载器(default.properties)

    [2]:初始化XML文件文件加载器

    [3]:初始化的properties文件加载器(struts.properties),该文件是用户自定义的文件

    [4]:初始化用户自定义配置加载器将我们自定义的加载器  保存到containerProviders集合中 ,web.xml中的

    configProviders参数  多个用","分开  配置器必须是ConfigurationProvider接口的实例

    [5]:初始化由web.xml传入的运行参数,最终保存到Container中

    [6]:初始化默认容器内置对象加载器

          ConfigurationManager和Configuration是Struts2的初始化元素,它们两者之间的关系,好比是xwork中 

    ActionProxy和ActionInvocation直接的关系,其中ConfigurationManager是整个配置元素进行操作的代理接口类,

    而真  正进行所有配置元素初始化进行调度的是Configuration对象。

     1、default.properties文件,属性加载器

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

    2、struts相关文件加载器,主要包括三种文件"struts-default.xml,struts-plugin.xml,struts.xml

     1  private void init_TraditionalXmlConfigurations() {
     2         String configPaths = initParams.get("config");从webx.xml中的配置的启动参数config读取文件,如果           没有配置,则使用的DAFAULT_CONFIGURATIO_PATHS静态常量配置的值。
     3         if (configPaths == null) {
     4             configPaths = DEFAULT_CONFIGURATION_PATHS;
     5         }文件名称使用,隔开,\s是java正则表达式的,表示匹配空白符号,匹配所有的空白符号,包括tab符
     6         String[] files = configPaths.split("\\s*[,]\\s*");
     7         for (String file : files) {
     8             if (file.endsWith(".xml")) {
     9                 if ("xwork.xml".equals(file)) {
    10                     configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));
    11                 } else {
    12                     configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));
    13                 }
    14             } else {
    15                 throw new IllegalArgumentException("Invalid configuration file name");
    16             }
    17         }
    18     }

    3、用户自定义的properties文件加载器:default.properties文件,详细情况参考DefaultSettings类的构造器

    1  private void init_LegacyStrutsProperties() {
    2         configurationManager.addConfigurationProvider(new LegacyPropertiesConfigurationProvider());
    3     }

    4、用户自定义的文件加载器,主要是用户可能扩充读取其他配置文件的类,用户需要实现自定义的加载器,但该加载器

    必须要实现ConfigurationProvider接口,并在web.xml的启动参数进行配置,并且配置的名称必须是configProviders

    如果有多个,使用逗号(,)隔开。

     1  private void init_CustomConfigurationProviders() {
     2         String configProvs = initParams.get("configProviders");
     3         if (configProvs != null) {
     4             String[] classes = configProvs.split("\\s*[,]\\s*");
     5             for (String cname : classes) {
     6                 try {
     7                     Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());类加载器
     8                     ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();实例化
     9                     configurationManager.addConfigurationProvider(prov);
    10                 } catch (InstantiationException e) {
    11                     throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
    12                 } catch (IllegalAccessException e) {
    13                     throw new ConfigurationException("Unable to access provider: "+cname, e);
    14                 } catch (ClassNotFoundException e) {
    15                     throw new ConfigurationException("Unable to locate provider class: "+cname, e);
    16                 }
    17             }
    18         }
    19     }

    5、初始化web.xml中配置的启动参数,配置的主要是在properties文件中配置的参数,或者,在struts2的xml文件中

    constant节点配置的属性和值,在这里使用了动态接口编程的思想,而没有单独的为初始化参数写加载器,只是实现了

    ConfigurationProvider类的匿名接口类。

     1     private void init_FilterInitParameters() {动态接口编程
     2         configurationManager.addConfigurationProvider(new ConfigurationProvider() {
     3             public void destroy() {}
     4             public void init(Configuration configuration) throws ConfigurationException {}
     5             public void loadPackages() throws ConfigurationException {}
     6             public boolean needsReload() { return false; }
     7 
     8             public void register(ContainerBuilder builder, LocatableProperties props) throws ConfigurationException {
     9                 props.putAll(initParams);
    10             }
    11         });
    12     }

    6、初始化默认容器的内置对象:在这里,之前通过加载struts2配置文件中,可能用户在自定义的xml配置文件中对

    系统的bean节点进行了重新配置,在这里可能需要重新的初始化,在这里主要是初始化系统的内置对象。

    1 private void init_AliasStandardObjects() {
    2         configurationManager.addConfigurationProvider(new BeanSelectionProvider());
    3     }

    加载器初始化已经完成

    init_PreloadConfiguration 方法中调用了  ConfigurationManager的getConfiguration 方法

    1  private Container init_PreloadConfiguration() {
    2         Configuration config = configurationManager.getConfiguration();
    3         Container container = config.getContainer();
    4 
    5         boolean reloadi18n = Boolean.valueOf(container.getInstance(String.class, StrutsConstants.S          TRUTS_I18N_RELOAD));
    6         LocalizedTextUtil.setReloadBundles(reloadi18n);
    7 
    8         return container;
    9     }

    getConfiguration方法:在这里,关键的方法在于reloadContainer方法它是我们初始化进行操作的地方

     1     /**
     2      * Get the current XWork configuration object.  By default an instance of DefaultConfiguration will be returned
     3      *
     4      * @see com.opensymphony.xwork2.config.impl.DefaultConfiguration
     5      */
     6     public synchronized Configuration getConfiguration() {
     7         if (configuration == null) {
     8             setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
     9             try {
    10                 configuration.reloadContainer(getContainerProviders());
    11             } catch (ConfigurationException e) {
    12                 setConfiguration(null);
    13                 throw new ConfigurationException("Unable to load configuration.", e);
    14             }
    15         } else {
    16             conditionalReload();
    17         }
    18 
    19         return configuration;
    20     }

    reloadContainer方法

     1     /**
     2      * Calls the ConfigurationProviderFactory.getConfig() to tell it to reload the configuration and then calls
     3      * buildRuntimeConfiguration().
     4      *
     5      * @throws ConfigurationException
     6      */
     7     public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
     8         packageContexts.clear();
     9         loadedFileNames.clear();配置文件加载器
    10         List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
    11         props是用来保存struts2的常量信息,主要是在properties文件中配置的,和在xml的constant节点配置的
    12         ContainerProperties props = new ContainerProperties();
    13         ContainerBuilder builder = new ContainerBuilder();容器构建器
    14         for (final ContainerProvider containerProvider : providers)
    15         {
    16             containerProvider.init(this);//初始化配置文件加载器
    17             containerProvider.register(builder, props);注册配置文件加载器,进行各种文件的读取,并
    18         }   保存到builder和props两个变量中
    19         props.setConstants(builder);进行constant常量的注册,将常量保存到ContainerBuilder#
    20         factorys中,
    21         builder.factory(Configuration.class, new Factory<Configuration>() {
    22             public Configuration create(Context context) throws Exception {
    23                 return DefaultConfiguration.this;
    24             }将自身放入到factorys中
    25         });
    26 
    27         ActionContext oldContext = ActionContext.getContext();
    28         try {创建容器,并创建struts2的一些核心类
    29             // Set the bootstrap container for the purposes of factory creation
    30             Container bootstrap = createBootstrapContainer();
    31             setContext(bootstrap);
    32             container = builder.create(false);创建容器
    33             setContext(container);
    34             objectFactory = container.getInstance(ObjectFactory.class);
    35             解析struts2的配置文件,解析package节点下的信息,并把不到
    36             // Process the configuration providers first
    37             for (final ContainerProvider containerProvider : providers)
    38             {
    39                 if (containerProvider instanceof PackageProvider) {
    40                     container.inject(containerProvider);依赖注入
    41                     ((PackageProvider)containerProvider).loadPackages();
    42                     packageProviders.add((PackageProvider)containerProvider);
    43                 }
    44             }
    45             解析XML的package节点,保存到packageContexts的MAP中
    46             // Then process any package providers from the plugins
    47             Set<String> packageProviderNames = container.getInstanceNames(PackageProvider.class);
    48             if (packageProviderNames != null) {
    49                 for (String name : packageProviderNames) {
    50                     PackageProvider provider = container.getInstance(PackageProvider.class, name);
    51                     provider.init(this);
    52                     provider.loadPackages();
    53                     packageProviders.add(provider);
    54                 }
    55             }
    56 
    57             rebuildRuntimeConfiguration();
    58         } finally {
    59             if (oldContext == null) {
    60                 ActionContext.setContext(null);
    61             }
    62         }
    63         return packageProviders;
    64     }

     StrutsXmlConfigurationProvider的init方法 具体在父类XmlConfigurationProvider中实现,

     特别需要注意StrutsXmlConfigurationProvider的构造器

     1  /**
     2      * Constructs the configuration provider
     3      *
     4      * @param filename The filename to look for
     5      * @param errorIfMissing If we should throw an exception if the file can't be found
     6      * @param ctx Our ServletContext
     7      */
     8     public StrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
     9         super(filename, errorIfMissing);特别需要注意这句话,它还调用了父亲的构造器
    10         this.servletContext = ctx;
    11         this.filename = filename;
    12         reloadKey = "configurationReload-"+filename;
    13         Map<String,String> dtdMappings = new HashMap<String,String>(getDtdMappings());
    14         dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.0//EN", "struts-2.0.dtd");
    15         dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.1//EN", "struts-2.1.dtd");
    16         dtdMappings.put("-//Apache Software Foundation//DTD Struts Configuration 2.1.7//EN", "struts-2.1.7.dtd");
    17         setDtdMappings(dtdMappings);
    18         File file = new File(filename);
    19         if (file.getParent() != null) {
    20             this.baseDir = file.getParentFile();
    21         }
    22     }

    init方法主要有下面三个功能:

    1. configFileName ->struts.xml  
    2. 递归处理include节点  
    3. 生成Document 集合
    1 public void init(Configuration configuration) {
    2         this.configuration = configuration;
    3         this.includedFileNames = configuration.getLoadedFileNames();
    4         loadDocuments(configFileName);
    5}

    loadConfigurationFiles方法递归处理include节点 最终生成Document集合,注意在这里使用的递归方法,在struts2

    初始化操作中,大量的使用了递归方法的调用

      1     private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
      2         List<Document> docs = new ArrayList<Document>();
      3         List<Document> finalDocs = new ArrayList<Document>();
      4         if (!includedFileNames.contains(fileName)) {放置include的中引用的文件重复插入
      5             if (LOG.isDebugEnabled()) {
      6                 LOG.debug("Loading action configurations from: " + fileName);
      7             }
      8 
      9             includedFileNames.add(fileName);
     10 
     11             Iterator<URL> urls = null;
     12             InputStream is = null;
     13 
     14             IOException ioException = null;
     15             try {
     16                 urls = getConfigurationUrls(fileName);
     17             } catch (IOException ex) {
     18                 ioException = ex;
     19             }
     20 
     21             if (urls == null || !urls.hasNext()) {
     22                 if (errorIfMissing) {
     23                     throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
     24                 } else {
     25                     LOG.info("Unable to locate configuration files of the name "
     26                             + fileName + ", skipping");
     27                     return docs;
     28                 }
     29             }
     30 
     31             URL url = null;
     32             while (urls.hasNext()) {
     33                 try {
     34                     url = urls.next();
     35                     is = FileManager.loadFile(url);
     36 
     37                     InputSource in = new InputSource(is);
     38 
     39                     in.setSystemId(url.toString());
     40                     生成document文件
     41                     docs.add(DomHelper.parse(in, dtdMappings));
     42                 } catch (XWorkException e) {
     43                     if (includeElement != null) {
     44                         throw new ConfigurationException("Unable to load " + url, e, includeElement);
     45                     } else {
     46                         throw new ConfigurationException("Unable to load " + url, e);
     47                     }
     48                 } catch (Exception e) {
     49                     final String s = "Caught exception while loading file " + fileName;
     50                     throw new ConfigurationException(s, e, includeElement);
     51                 } finally {
     52                     if (is != null) {
     53                         try {
     54                             is.close();
     55                         } catch (IOException e) {
     56                             LOG.error("Unable to close input stream", e);
     57                         }
     58                     }
     59                 }
     60             }
     61 
     62             //sort the documents, according to the "order" attribute
     63             Collections.sort(docs, new Comparator<Document>() {
     64                 public int compare(Document doc1, Document doc2) {
     65                     return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));
     66                 }
     67             });
     68 
     69             for (Document doc : docs) {
     70                 Element rootElement = doc.getDocumentElement();
     71                 NodeList children = rootElement.getChildNodes();
     72                 int childSize = children.getLength();
     73 
     74                 for (int i = 0; i < childSize; i++) {
     75                     Node childNode = children.item(i);
     76 
     77                     if (childNode instanceof Element) {
     78                         Element child = (Element) childNode;
     79 
     80                         final String nodeName = child.getNodeName();
     81                         处理include节点的内容,可以使用通配符
     82                         if ("include".equals(nodeName)) {
     83                             String includeFileName = child.getAttribute("file");
     84                             if (includeFileName.indexOf('*') != -1) {
     85                                 // handleWildCardIncludes(includeFileName, docs, child);
     86                                 ClassPathFinder wildcardFinder = new ClassPathFinder();
     87                                 wildcardFinder.setPattern(includeFileName);
     88                                 Vector<String> wildcardMatches = wildcardFinder.findMatches();
     89                                 for (String match : wildcardMatches) {
     90                                     finalDocs.addAll(loadConfigurationFiles(match, child));
     91                                 }   递归调用
     92                             } else {
     93                                finalDocs.addAll(loadConfigurationFiles(includeFileName, child));                                         递归调用
     94                             }
     95                         }
     96                     }
     97                 }
     98                 finalDocs.add(doc);
     99                 loadedFileUrls.add(url.toString());
    100             }
    101 
    102             if (LOG.isDebugEnabled()) {
    103                 LOG.debug("Loaded action configuration from: " + fileName);
    104             }
    105         }
    106         return finalDocs;
    107     }

    StrutsXmlConfigurationProvider的register方法 主要在父类 XmlConfigurationProvider中实现

    1.遍历init方法中生成的Document 集合  解析xml文件中定义的bean,constant常量节点 不会处理package节点

    2.解析bean节点的值 包装成LocatableFactory对象  注册到ContainerBuilder中factories map集合中

    3.解析constant节点的值 保存到ContainerProperties 对象中 

    XmlConfigurationProvider 的register  这里只解析 bean , constant节点

     1  public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
     2         LOG.info("Parsing configuration file [" + configFileName + "]");
     3         Map<String, Node> loadedBeans = new HashMap<String, Node>();
     4         for (Document doc : documents) {
     5             Element rootElement = doc.getDocumentElement();
     6             NodeList children = rootElement.getChildNodes();
     7             int childSize = children.getLength();
     8 
     9             for (int i = 0; i < childSize; i++) {
    10                 Node childNode = children.item(i);
    11 
    12                 if (childNode instanceof Element) {
    13                     Element child = (Element) childNode;
    14 
    15                     final String nodeName = child.getNodeName();
    16                     解析bean节点
    17                     if ("bean".equals(nodeName)) {
    18                         String type = child.getAttribute("type");
    19                         String name = child.getAttribute("name");
    20                         String impl = child.getAttribute("class");
    21                         String onlyStatic = child.getAttribute("static");
    22                         String scopeStr = child.getAttribute("scope");
    23                         boolean optional = "true".equals(child.getAttribute("optional"));
    24                         Scope scope = Scope.SINGLETON;
    25                         if ("default".equals(scopeStr)) {
    26                             scope = Scope.DEFAULT;
    27                         } else if ("request".equals(scopeStr)) {
    28                             scope = Scope.REQUEST;
    29                         } else if ("session".equals(scopeStr)) {
    30                             scope = Scope.SESSION;
    31                         } else if ("singleton".equals(scopeStr)) {
    32                             scope = Scope.SINGLETON;
    33                         } else if ("thread".equals(scopeStr)) {
    34                             scope = Scope.THREAD;
    35                         }
    36 
    37                         if (StringUtils.isEmpty(name)) {
    38                             name = Container.DEFAULT_NAME;
    39                         }
    40 
    41                         try {
    42                             Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
    43                             Class ctype = cimpl;在开发程序的过程,应该多使用公共类,是代码简洁易懂
    44                             if (StringUtils.isNotEmpty(type)) {
    45                                 ctype = ClassLoaderUtil.loadClass(type, getClass());
    46                             }
    47                             if ("true".equals(onlyStatic)) {
    48                                 // Force loading of class to detect no class def found exceptions
    49                                 cimpl.getDeclaredClasses();
    50                                 containerBuilder.injectStatics(cimpl);
    51                             } else {beanName + class构成唯一性约束
    52                                 if (containerBuilder.contains(ctype, name)) {使用loadedBeans检测使用重复配置bean节点信息
    53                                     Location loc = LocationUtils.getLocation(loadedBeans.get(ctype.getName() + name));
    54                                     if (throwExceptionOnDuplicateBeans) {
    55                                         throw new ConfigurationException("Bean type " + ctype + " with the name " +
    56                                                 name + " has already been loaded by " + loc, child);
    57                                     }
    58                                 }
    59 
    60                                 // Force loading of class to detect no class def found exceptions
    61                                 cimpl.getDeclaredConstructors();
    62 
    63                                 if (LOG.isDebugEnabled()) {
    64                                     LOG.debug("Loaded type:" + type + " name:" + name + " impl:" + impl);
    65                                 }
                                       将bean定义保存到containerBuilder#factorys中,此刻,并未真正的实现bean
    节点
    66
    containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope); 67 } 68 loadedBeans.put(ctype.getName() + name, child);向loadedBeans中插入数据 69 } catch (Throwable ex) { 70 if (!optional) { 71 throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, childNode); 72 } else { 73 LOG.debug("Unable to load optional class: " + ex); 74 } 75 } 76 } else if ("constant".equals(nodeName)) {加载constant节点,保存到props变量中 77 String name = child.getAttribute("name"); 78 String value = child.getAttribute("value"); 79 props.setProperty(name, value, childNode); 80 } else if (nodeName.equals("unknown-handler-stack")) { 81 List<UnknownHandlerConfig> unknownHandlerStack = new ArrayList<UnknownHandlerConfig>(); 82 NodeList unknownHandlers = child.getElementsByTagName("unknown-handler-ref"); 83 int unknownHandlersSize = unknownHandlers.getLength(); 84 85 for (int k = 0; k < unknownHandlersSize; k++) { 86 Element unknownHandler = (Element) unknownHandlers.item(k); 87 unknownHandlerStack.add(new UnknownHandlerConfig(unknownHandler.getAttribute("name"))); 88 } 89 90 if (!unknownHandlerStack.isEmpty()) 91 configuration.setUnknownHandlerStack(unknownHandlerStack); 92 } 93 } 94 } 95 } 96 }

    XmlConfigurationProvider的loadPackages方法  解析package节点下的所有子节点interceptor ,ResultType

    等保存到DefaultConfiguration packageContexts map集合中,

    关于loadPackages方法,可以参考之前的一篇文章:

    Struts源码学习-XmlConfigurationProvider的loadPackages()方法递归调用

     1     public void loadPackages() throws ConfigurationException {
     2         List<Element> reloads = new ArrayList<Element>();
     3         for (Document doc : documents) {
     4             Element rootElement = doc.getDocumentElement();
     5             NodeList children = rootElement.getChildNodes();
     6             int childSize = children.getLength();
     7 
     8             for (int i = 0; i < childSize; i++) {
     9                 Node childNode = children.item(i);
    10  
    11                 if (childNode instanceof Element) {
    12                     Element child = (Element) childNode;
    13 
    14                     final String nodeName = child.getNodeName();
    15                     解析package节点,包装成PackageConfig对象
    16                     if ("package".equals(nodeName)) {
    17                         PackageConfig cfg = addPackage(child);
    18                         if (cfg.isNeedsRefresh()) {
    19                             reloads.add(child);将所有要重新加载的放置到reloads这个list中,因为在初始化
    20                         }   package的时候,它extends的包可能还没有加载出来,需要reload,重新最终去构建
    21                     }       它们的父子关系,详细情况请敞开上面的链接
    22                 } 
    23             }
    24             loadExtraConfiguration(doc);空实现no op
    25         }
    26 
    27         if (reloads.size() > 0) {
    28             reloadRequiredPackages(reloads);
    29         }
    30 
    31         for (Document doc : documents) {
    32             loadExtraConfiguration(doc);no op空实现,便于以后的扩展
    33         }
    34 
    35         documents.clear();
    36         configuration = null;
    37     }

     addPackage(),解析package节点下的所有子节点;

     1   /**
     2      * Create a PackageConfig from an XML element representing it.
     3      */
     4     protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
     5         PackageConfig.Builder newPackage = buildPackageContext(packageElement);
     6 
     7         if (newPackage.isNeedsRefresh()) {
     8             return newPackage.build();
     9         }
    10 
    11         if (LOG.isDebugEnabled()) {
    12             LOG.debug("Loaded " + newPackage);
    13         }
    14         处理所有的resultType,包括自定义的和在struts-default.xml文件中定义的,这就是为什么我们建立xml文件的
    15         // add result types (and default result) to this package时候一般需要继承struts-default.xml
    16         addResultTypes(newPackage, packageElement);
    17         interceptors节点
    18         // load the interceptors and interceptor stacks for this package
    19         loadInterceptors(newPackage, packageElement);
    20         默认的拦截器节点
    21         // load the default interceptor reference for this package
    22         loadDefaultInterceptorRef(newPackage, packageElement);
    23         假如没有发现对应的action,在dafault-class-ref节点
    24         // load the default class ref for this package
    25         loadDefaultClassRef(newPackage, packageElement);
    26         全局的result节点,在这里,我们将找到,如果不配置result的name,那么它的名字将是Action.SUCCESS,
    27         // load the global result list for this package也就是success
    28         loadGlobalResults(newPackage, packageElement);
    29         全局的异常处理
    30         // load the global exception handler list for this package
    31         loadGobalExceptionMappings(newPackage, packageElement);
    32 
    33         // get actions
    34         NodeList actionList = packageElement.getElementsByTagName("action");
    35         处理action节点的信息
    36         for (int i = 0; i < actionList.getLength(); i++) {
    37             Element actionElement = (Element) actionList.item(i);
    38             addAction(actionElement, newPackage);
    39         }
    40         默认的本包内部的action
    41         // load the default action reference for this package
    42         loadDefaultActionRef(newPackage, packageElement);
    43         构建packkage
    44         PackageConfig cfg = newPackage.build();
    45         configuration.addPackageConfig(cfg.getName(), cfg);保存到Map<String,PackageConfig>即
    46         return cfg; packageContexts中,
    47     }

    这里我们看下:result-type,我们平时使用的result-type,默认为分发即dispatcher,我们在这里看看这是为什么

     1 <result-types>
     2             <result-type name="chain" class="com.opensymphony.xwork2.ActionChainResult"/>
     3             <result-type name="dispatcher" class="org.apache.struts2.dispatcher.ServletDispatcherResult" default="true"/>
     4             <result-type name="freemarker" class="org.apache.struts2.views.freemarker.FreemarkerResult"/>
     5             <result-type name="httpheader" class="org.apache.struts2.dispatcher.HttpHeaderResult"/>
     6             <result-type name="redirect" class="org.apache.struts2.dispatcher.ServletRedirectResult"/>
     7             <result-type name="redirectAction" class="org.apache.struts2.dispatcher.ServletActionRedirectResult"/>
     8             <result-type name="stream" class="org.apache.struts2.dispatcher.StreamResult"/>
     9             <result-type name="velocity" class="org.apache.struts2.dispatcher.VelocityResult"/>
    10             <result-type name="xslt" class="org.apache.struts2.views.xslt.XSLTResult"/>
    11             <result-type name="plainText" class="org.apache.struts2.dispatcher.PlainTextResult" />
    12         </result-types>

    addResultType(),所在,在我们使用在action中配置result节点的时候,不执行name,则为succcess,不指定

    type,则为dispatcher.

     1  protected void addResultTypes(PackageConfig.Builder packageContext, Element element) {
     2         NodeList resultTypeList = element.getElementsByTagName("result-type");
     3 
     4         for (int i = 0; i < resultTypeList.getLength(); i++) {
     5             Element resultTypeElement = (Element) resultTypeList.item(i);
     6             String name = resultTypeElement.getAttribute("name");
     7             String className = resultTypeElement.getAttribute("class");
     8             String def = resultTypeElement.getAttribute("default");
     9 
    10             Location loc = DomHelper.getLocationObject(resultTypeElement);
    11 
    12             Class clazz = verifyResultType(className, loc);
    13             if (clazz != null) {
    14                 String paramName = null;
    15                 try {
    16                     paramName = (String) clazz.getField("DEFAULT_PARAM").get(null);
    17                 }
    18                 catch (Throwable t) {
    19                     // if we get here, the result type doesn't have a default param defined.
    20                 }
    21                 ResultTypeConfig.Builder resultType = new ResultTypeConfig.Builder(name, className).defaultResultParam(paramName)
    22                         .location(DomHelper.getLocationObject(resultTypeElement));
    23 
    24                 Map<String, String> params = XmlHelper.getParams(resultTypeElement);
    25 
    26                 if (!params.isEmpty()) {
    27                     resultType.addParams(params);
    28                 }
    29                 packageContext.addResultTypeConfig(resultType.build());
    30 
    31                 // set the default result type
    32                 if ("true".equals(def)) {设置默认的result-type,在这里为dispatcher
    33                     packageContext.defaultResultType(name);
    34                 }
    35             }
    36         }
    37     }

          最后整理解析的ActionConfig Map集合[DefaultConfiguration#packageContexts]  最终已Map<nameSpace,Map<actionName, ActionConfig>>形式存储

     1  /**
     2      * This builds the internal runtime configuration used by Xwork for finding and configuring Actions from the
     3      * programmatic configuration data structures. All of the old runtime configuration will be discarded and rebuilt.
     4      *
     5      * <p>
     6      * It basically flattens the data structures to make the information easier to access.  It will take
     7      * an {@link ActionConfig} and combine its data with all inherited dast.  For example, if the {@link ActionConfig}
     8      * is in a package that contains a global result and it also contains a result, the resulting {@link ActionConfig}
     9      * will have two results.
    10      */
    11     protected synchronized RuntimeConfiguration buildRuntimeConfiguration() throws ConfigurationException {
    12         Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<String, Map<String, ActionConfig>>();
    13         Map<String, String> namespaceConfigs = new LinkedHashMap<String, String>();
    14 
    15         for (PackageConfig packageConfig : packageContexts.values()) {
    16 
    17             if (!packageConfig.isAbstract()) {
    18                 String namespace = packageConfig.getNamespace();
    19                 Map<String, ActionConfig> configs = namespaceActionConfigs.get(namespace);
    20 
    21                 if (configs == null) {
    22                     configs = new LinkedHashMap<String, ActionConfig>();
    23                 }
    24 
    25                 Map<String, ActionConfig> actionConfigs = packageConfig.getAllActionConfigs();
    26 
    27                 for (Object o : actionConfigs.keySet()) {
    28                     String actionName = (String) o;
    29                     ActionConfig baseConfig = actionConfigs.get(actionName);这里设置action的默认拦截                       30                     configs.put(actionName, buildFullActionConfig(packageConfig, baseConfig));
    31                 }
    32 
    33 
    34 
    35                 namespaceActionConfigs.put(namespace, configs);
    36                 if (packageConfig.getFullDefaultActionRef() != null) {
    37                     namespaceConfigs.put(namespace, packageConfig.getFullDefaultActionRef());
    38                 }
    39             }
    40         }
    41 
    42         return new RuntimeConfigurationImpl(namespaceActionConfigs, namespaceConfigs);
    43     }

    请参考文章:Struts2源码学习-DefaultConfiguration的RuntimeConfigurationImpl方法(运行期改造)

    解析完成后,  最终会保存到DefaultConfiguration  runtimeConfiguration变量中。

  • 相关阅读:
    十一:jinja2模板传参
    Python基础—流程控制
    Python字符串格式化输出
    Python基本数据类型--列表、元组、字典、集合
    Python基本数据类型之字符串、数字、布尔
    Python用户输入和代码注释
    Python中变量和常量的理解
    Python程序的执行方式
    Python初识
    python初识
  • 原文地址:https://www.cnblogs.com/caroline/p/3002572.html
Copyright © 2011-2022 走看看