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

struts2源码浅析(二)

 
阅读更多

接上一篇http://mazhiyuan.iteye.com/blog/1202064,这一篇先讲讲init方法中的7步

 

首先是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));
        }
    }	

 

再来看第二步: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()加载package及package中的属性。
   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;
    }

 

 接下来第三步: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接口的实现类
                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

 第五步是自定义的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);
                } 
				...
            }
        }
    }

 第六步: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>与实际的类相映射,就是注入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;
    }

  展开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;
    }	

 

init7步执行完之后,struts2做好了接受request的准备了,下一篇会着重讲讲filter在struts中的使用。

下一篇:http://mazhiyuan.iteye.com/blog/1202104

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics