struts2架构中核心对象的探索—— Dispatcher&ConfigurationProvider

来源:互联网 发布:python ipython 编辑:程序博客网 时间:2024/05/20 00:49

首先强调一下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;      }  

我们再看在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);  }  

创建Dispatcher之后,来看init()方法,init()方法是用来Load用户配置文件,资源文件以及默认的配置文件,主要分七步走,看下面注释:

  public void init() {      if (configurationManager == null) {      //设置ConfigurationManager的defaultFrameworkBeanName.      //这里DEFAULT_BEAN_NAME为struts,这是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对象      //在这里面会循环调用上面七个ConfigurationProvider的register方法      //其中的重点就是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初始化之后将实例添加到ConfigurationManager的List里面.最后通过循环调用List里的这些destroy(),register()等方法实现对配置文件的属性进行注册和销毁等功能.
下面将分析分析7步流程:

- 1:首先是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);  }
//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主要是将progerty的value和Locale从上面PropertiesSettings中取得并存放到LocatableProperties props      //这个props是register的一个入参.      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));          }      } 

- 2:再来看第二步: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");          }      }  } 
 对于其它配置文件只用StrutsXmlConfigurationProvider,此类继承XmlConfigurationProvider,而XmlConfigurationProvider又实现ConfigurationProvider接口。 类XmlConfigurationProvider负责配置文件的读取和解析,     首先通过init()中的loadDocuments(configFileName);     利用DomHelper中的 public static Document parse(InputSource inputSource, Map<String, String> dtdMappings) 将configFileName配置文件通过SAX解析方式按照DtdMappings解析成Document对象.     然后通过Provider的register()方法加载"bean""constant"属性,     再通过loadPackages()加载packagepackage中的属性
addAction()方法负责读取<action>标签,并将数据保存在ActionConfig中;addResultTypes()方法负责将<result-type>标签转化为ResultTypeConfig对象;loadInterceptors()方法负责将<interceptor>标签转化为InterceptorConfi对象;loadInterceptorStack()方法负责将<interceptor-ref>标签转化为InterceptorStackConfig对象;loadInterceptorStacks()方法负责将<interceptor-stack>标签转化成InterceptorStackConfig对象。

而上面的方法最终会被addPackage()方法调用,addPackage又会被Provider的loadPackages()调用,将所读取到的数据汇集到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) {         //递归Load子file中的<include/>               docs.addAll(loadConfigurationFiles(match, child));             }           } else {             docs.addAll(loadConfigurationFiles(includeFileName, child));           }         }       }     }     docs.add(doc);     loadedFileUrls.add(url.toString());     ...     return docs;   } 

- 3:接下来第三步:

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 

去掉了第四步:init_ZeroConfiguration();
- 5:第五步是自定义的configProviders

 private void init_CustomConfigurationProviders() {      //从这里可以看到可以将自定义的Provider定义在web.xml中FilterDispatcher的param中: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);              }               ...          }      }  } 
  • 6:第六步:init_FilterInitParameters
//从这里可以看出struts.properties中的属性不仅可以在struts.xml中以constant形式定义,而且可以在FilterDispatcher的param中定义        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的依赖关系,

    接下来是看怎样调用这些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;   }  

展开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)        {        //循环调用ConfigurationProvider的init和register方法,明白了吧,在这里统一循环调用            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);              //调用PackageProvider的loadPackages()方法,这里主要是针对XmlConfigurationProvider和StrutsXmlConfigurationProvider                    ((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;    } 
阅读全文
0 0
原创粉丝点击