Struts2源码浅析-初始化

StrutsPrepareAndExecuteFilter实现了Filter接口  init方法为初始化入口

StrutsPrepareAndExecuteFilter init方法 

    public void init(FilterConfig filterConfig) throws ServletException {
    	//初始化辅助类 类似一个Delegate
        InitOperations init = new InitOperations();
        try {
        	// FilterHostConfig 封装了FilterConfig参数对象 
            FilterHostConfig config = new FilterHostConfig(filterConfig);
            //LoggerFactory配置加载 
            //如果没有web.xml 没有配置“loggerFactory”参数  尝试org.apache.commons.logging.LogFactory
            //如果失败 使用JdkLoggerFactory
            //TODO SPI
            init.initLogging(config);
            //TODO 创建Dispatcher 注册加载器   执行加载器  创建容器 解析xml  
            Dispatcher dispatcher = init.initDispatcher(config);
            init.initStaticContentLoader(config, dispatcher);
            //预处理类 请求处理时才会真正用到
            //1.主要负责在每次请求 创建ActionContext 清除ActionContext
            //2.当接收到一个请求时 通过uri查找 ActionConfig 创建ActionMapping
            prepare = new PrepareOperations(filterConfig.getServletContext(), dispatcher);
            //处理请求  Delegate
            execute = new ExecuteOperations(filterConfig.getServletContext(), dispatcher);
			this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
			//空实现 留作扩展
            postInit(dispatcher, filterConfig);
        } finally {
            init.cleanup();
        }
    }

InitOperations 类似与一个Delegate 主要负责实例化Dispatche  再把初始化操作转交给Dispatche init处理

    public Dispatcher initDispatcher( HostConfig filterConfig ) {
    	//创建Dispatcher
        Dispatcher dispatcher = createDispatcher(filterConfig);
        //核心方法  Container容器的创建   xml解析在此方法发生
        dispatcher.init();
        return dispatcher;
    }
    
    private Dispatcher createDispatcher( HostConfig filterConfig ) {
        Map<String, String> params = new HashMap<String, String>();
        for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
            String name = (String) e.next();
            String value = filterConfig.getInitParameter(name);
            params.put(name, value);
        }
        return new Dispatcher(filterConfig.getServletContext(), params);
    }

Dispatcher init方法  1.针对配置文件 注册不同的加载器 保存到ConfigurationManager类中的一个变量中  2.创建容器 解析xml

 public void init() {
    	//创建配置操作管理类  ,  会保存元素加载器 
    	if (configurationManager == null) {
    		configurationManager = createConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
    	}
        try {
        	/**初始化各种形式加载器,保存到ConfigurationManager#containerProviders Map集合中 没有真正执行加载 解析逻辑*/
        	
        	//org/apache/struts2/default.properties属性文件  里面定义了一系列struts常量
            init_DefaultProperties(); // [1]
            
            //web.xml配置的 config参数 [配置多个用","分开]
            //如果没有该参数 默认为 struts-default.xml[框架级],struts-plugin.xml[框架级],struts.xml[系统级别]
            //根据文件名称 创建加载器  加载xml主要有一下两个解析器
            //XmlConfigurationProvider[xwork.xml],
            //StrutsXmlConfigurationProvider[struts相关配置文件]配置元素加载器 
            init_TraditionalXmlConfigurations(); // [2]
            
            //struts.locale 注册
            init_LegacyStrutsProperties(); // [3]
            
            //实例化  我们自定义的加载器  保存到containerProviders集合中
            // web.xml configProviders参数  多个用","分开  配置器必须是ConfigurationProvider接口的实例
            //TODO SPI
            init_CustomConfigurationProviders(); // [5]
            
            //web.xml配置的init-param参数 加载器  最终会保存到Container容器中
            init_FilterInitParameters() ; // [6]
            
            //TODO 根据我们在struts.xml定义的 常量 选择插件类   
            //比如集成spring 会用到org.apache.struts2.spring.StrutsSpringObjectFactory 
            init_AliasStandardObjects() ; // [7]
            
            /** 执行加载器 */
            //TODO 创建容器  解析xml  真正执行加载器方法 
            Container container = init_PreloadConfiguration();
            //执行当前Dispatcher对象 依赖关系注入 
            container.inject(this);
            
            //额外动作 
            init_CheckConfigurationReloading(container);
            init_CheckWebLogicWorkaround(container);

        } catch (Exception ex) {
            if (LOG.isErrorEnabled())
                LOG.error("Dispatcher initialization failed", ex);
            throw new StrutsException(ex);
        }
    }

ConfigurationManager 主要管理 创建的各种加载器

public class ConfigurationManager {

    protected static final Logger LOG = LoggerFactory.getLogger(ConfigurationManager.class);
    //配置元素管理器
    protected Configuration configuration;
    
    protected Lock providerLock = new ReentrantLock();
    //创建的xml加载器会保存到次集合中
    private List<ContainerProvider> containerProviders = new CopyOnWriteArrayList<ContainerProvider>();
    
}

Dispatcher的 createConfigurationManager方法

    protected ConfigurationManager createConfigurationManager(String name) {
    	//name - >  struts
        return new ConfigurationManager(name);
    }

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

    private void init_DefaultProperties() {
    	//保存到ConfigurationManager加载器集合中
        configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
    }

2.创建struts相关文件加载器 StrutsXmlConfigurationProvider 

    private void init_TraditionalXmlConfigurations() {
    	//web.xml 配置的config
        String configPaths = initParams.get("config");
        if (configPaths == null) {
        	//如果没有配置  默认 struts-default.xml,struts-plugin.xml,struts.xml
            configPaths = DEFAULT_CONFIGURATION_PATHS;
        }
        String[] files = configPaths.split("\\s*[,]\\s*");
        for (String file : files) {
            if (file.endsWith(".xml")) {
                if ("xwork.xml".equals(file)) {
                    configurationManager.addConfigurationProvider(createXmlConfigurationProvider(file, false));
                } else {
                	//struts xml加载器  
                	//StrutsXmlConfigurationProvider
                    configurationManager.addConfigurationProvider(createStrutsXmlConfigurationProvider(file, false, servletContext));
                }
            } else {
                throw new IllegalArgumentException("Invalid configuration file name");
            }
        }
    }

    protected XmlConfigurationProvider createXmlConfigurationProvider(String filename, boolean errorIfMissing) {
        return new XmlConfigurationProvider(filename, errorIfMissing);
    }

    protected XmlConfigurationProvider createStrutsXmlConfigurationProvider(String filename, boolean errorIfMissing, ServletContext ctx) {
        return new StrutsXmlConfigurationProvider(filename, errorIfMissing, ctx);
    }

3.web.xml扩展的ContainerProviders加载器 实例化

    private void init_CustomConfigurationProviders() {
    	//web.xml 中configProviders 节点
        String configProvs = initParams.get("configProviders");
        if (configProvs != null) {
            String[] classes = configProvs.split("\\s*[,]\\s*");
            for (String cname : classes) {
	            Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
	            ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
	            configurationManager.addConfigurationProvider(prov);
            }
        }
    }

init_PreloadConfiguration 方法主要完成创建容器, 解析xml动作

    private Container init_PreloadConfiguration() {
    	//创建Container  解析xml
        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;
    }

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

    public synchronized Configuration getConfiguration() {
    	//创建配置元素管理器
        if (configuration == null) {
        	// defaultFrameworkBeanName  - > struts
            setConfiguration(createConfiguration(defaultFrameworkBeanName));
            try {
            	// getContainerProviders  返回注册的各种加载器 
            	// reloadContainer  创建Container  解析xml
                configuration.reloadContainer(getContainerProviders());
            } catch (ConfigurationException e) {
                setConfiguration(null);
                throw new ConfigurationException("Unable to load configuration.", e);
            }
        } else {
            conditionalReload();
        }
        return configuration;
    }
    protected Configuration createConfiguration(String beanName) {
        return new DefaultConfiguration(beanName);
    }

DefaultConfiguration的reloadContainer方法 会去执行已注册的各种加载器 ,和创建容器

public synchronized List<PackageProvider> reloadContainer(List<ContainerProvider> providers) throws ConfigurationException {
		packageContexts.clear();
		loadedFileNames.clear();
		List<PackageProvider> packageProviders = new ArrayList<PackageProvider>();
		// 保存struts常量
		ContainerProperties props = new ContainerProperties();
		//容器构建器
		ContainerBuilder builder = new ContainerBuilder();
		for (final ContainerProvider containerProvider : providers) {
			/**
			 * 初始化Document 准备解析
			 * 具体在XmlConfigurationProvider实现类 会处理include节点 
			 * 处理完成之后Document会保存到XmlConfigurationProvider#documents list集合中
			 * include file路径会保存到XmlConfigurationProvider#loadedFileUrls set集合中 
			 * 从代码中发现 include file属性中 可以使用通配符 "*"
			 */
			/** StrutsXmlConfigurationProvider 是 XmlConfigurationProvider的子类 */
			/** StrutsXmlConfigurationProvider struts*.xml */
			containerProvider.init(this);
			//针对"bean","constant","unknown-handler-stack"节点  不包括"package"节点   解析xml 
			//每一个bean 对应一个LocatableFactory  LocatableFactory保存了bean的定义
			//bean定义 保存到ContainerBuilder#factories map集合中 
			//配置文件中定义的常量  保存到props中
			containerProvider.register(builder, props);
		}
		//将常量保存到ContainerBuilder#factories map集合中 
		//每一个常量对应一个LocatableConstantFactory
		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 {
			//创建辅助容器 ContainerImpl并且 实例化 struts一些核心类 
			Container bootstrap = createBootstrapContainer();
			setContext(bootstrap);
			
			//主容器 这是一个全局变量 
			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) {
					//com.opensymphony.xwork2.config.providers.XmlConfigurationProvider#setObjectFactory(ObjectFactory)
					container.inject(containerProvider);
					//解析  xml  package节点
					//保存packageContexts map集合中
					//com.opensymphony.xwork2.config.providers.XmlConfigurationProvider  line 481
					((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);
				}
			}
            //TODO
			rebuildRuntimeConfiguration();
		} finally {
			if (oldContext == null) {
				ActionContext.setContext(null);
			}
		}
		return packageProviders;
	}

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

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

loadDocuments方法中调用了loadConfigurationFiles方法  返回一个Document集合

	private void loadDocuments(String configFileName) {
		loadedFileUrls.clear();
		 //List<Document> documents
		documents = loadConfigurationFiles(configFileName, null);
	}

loadConfigurationFiles方法 递归处理include节点 最终生成Document集合

private List<Document> loadConfigurationFiles(String fileName, Element includeElement) {
		List<Document> docs = new ArrayList<Document>();
		List<Document> finalDocs = new ArrayList<Document>();
		//防止include重复引入
		if (!includedFileNames.contains(fileName)) {
			if (LOG.isDebugEnabled()) {
				LOG.debug("Loading action configurations from: " + fileName);
			}
			includedFileNames.add(fileName);
			Iterator<URL> urls = null;
			InputStream is = null;
			IOException ioException = null;
			try {
				urls = getConfigurationUrls(fileName);
			} catch (IOException ex) {
				ioException = ex;
			}

			if (urls == null || !urls.hasNext()) {
				if (errorIfMissing) {
					throw new ConfigurationException("Could not open files of the name " + fileName, ioException);
				} else {
					LOG.info("Unable to locate configuration files of the name " + fileName + ", skipping");
					return docs;
				}
			}
			URL url = null;
			while (urls.hasNext()) {
				try {
					url = urls.next();
					is = FileManager.loadFile(url);
					InputSource in = new InputSource(is);
					in.setSystemId(url.toString());
					//生成Document对象
					docs.add(DomHelper.parse(in, dtdMappings));
				} catch (XWorkException e) {
					if (includeElement != null) {
						throw new ConfigurationException("Unable to load " + url, e, includeElement);
					} else {
						throw new ConfigurationException("Unable to load " + url, e);
					}
				} catch (Exception e) {
					final String s = "Caught exception while loading file " + fileName;
					throw new ConfigurationException(s, e, includeElement);
				} finally {
					if (is != null) {
						try {
							is.close();
						} catch (IOException e) {
							LOG.error("Unable to close input stream", e);
						}
					}
				}
			}
			//sort the documents, according to the "order" attribute
			Collections.sort(docs, new Comparator<Document>() {
				public int compare(Document doc1, Document doc2) {
					return XmlHelper.getLoadOrder(doc1).compareTo(XmlHelper.getLoadOrder(doc2));
				}
			});
			for (Document doc : docs) {
				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");
							//可以使用通配符匹配  
							if (includeFileName.indexOf('*') != -1) {
								ClassPathFinder wildcardFinder = new ClassPathFinder();
								wildcardFinder.setPattern(includeFileName);
								Vector<String> wildcardMatches = wildcardFinder.findMatches();
								for (String match : wildcardMatches) {
									//递归处理include节点
									finalDocs.addAll(loadConfigurationFiles(match, child));
								}
							} else {
								//递归处理include节点
								finalDocs.addAll(loadConfigurationFiles(includeFileName, child));
							}
						}
					}
				}
				finalDocs.add(doc);
				loadedFileUrls.add(url.toString());
			}
		}
		return finalDocs;
	}

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

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

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

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

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

	public void register(ContainerBuilder containerBuilder, LocatableProperties props) throws ConfigurationException {
		Map<String, Node> loadedBeans = new HashMap<String, Node>();
		for (Document doc : documents) {
			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();
					//解析bean节点
					if ("bean".equals(nodeName)) {
						String type = child.getAttribute("type");
						String name = child.getAttribute("name");
						String impl = child.getAttribute("class");
						String onlyStatic = child.getAttribute("static");
						String scopeStr = child.getAttribute("scope");
						boolean optional = "true".equals(child.getAttribute("optional"));
						Scope scope = Scope.SINGLETON;
						if ("default".equals(scopeStr)) {
							scope = Scope.DEFAULT;
						} else if ("request".equals(scopeStr)) {
							scope = Scope.REQUEST;
						} else if ("session".equals(scopeStr)) {
							scope = Scope.SESSION;
						} else if ("singleton".equals(scopeStr)) {
							scope = Scope.SINGLETON;
						} else if ("thread".equals(scopeStr)) {
							scope = Scope.THREAD;
						}
						if (StringUtils.isEmpty(name)) {
							name = Container.DEFAULT_NAME;
						}
						try {
							Class cimpl = ClassLoaderUtil.loadClass(impl, getClass());
							Class ctype = cimpl;
							if (StringUtils.isNotEmpty(type)) {
								ctype = ClassLoaderUtil.loadClass(type, getClass());
							}
							if ("true".equals(onlyStatic)) {
								// Force loading of class to detect no class def found exceptions
								cimpl.getDeclaredClasses();
								containerBuilder.injectStatics(cimpl);
							} else {
								// beanName + class 构成唯一约束 
								if (containerBuilder.contains(ctype, name)) {
									//用loadedBeans map集合检查是否有重复配置的bean
									Location loc = LocationUtils.getLocation(loadedBeans.get(ctype.getName() + name));
									if (throwExceptionOnDuplicateBeans) {
										throw new ConfigurationException("Bean type " + ctype + " with the name " + name + " has already been loaded by " + loc, child);
									}
								}
								// Force loading of class to detect no class def found exceptions
								cimpl.getDeclaredConstructors();

								if (LOG.isDebugEnabled()) {
									LOG.debug("Loaded type:" + type + " name:" + name + " impl:" + impl);
								}
								//LocatableFactory 类似spring中 BeanDefinition 
								//bean定义 保存到ContainerBuilder#factories map集合中
								//目前为止  并未真正实例化bean
								containerBuilder.factory(ctype, name, new LocatableFactory(name, ctype, cimpl, scope, childNode), scope);
							}
							//loadedBeans 检查重复配置的bean
							loadedBeans.put(ctype.getName() + name, child);
						} catch (Throwable ex) {
							if (!optional) {
								throw new ConfigurationException("Unable to load bean: type:" + type + " class:" + impl, ex, childNode);
							} else {
								LOG.debug("Unable to load optional class: " + ex);
							}
						}
						//constant常量节点
					} else if ("constant".equals(nodeName)) {
						String name = child.getAttribute("name");
						String value = child.getAttribute("value");
						//ContainerProperties ->props
						props.setProperty(name, value, childNode);
					} else if (nodeName.equals("unknown-handler-stack")) {
						List<UnknownHandlerConfig> unknownHandlerStack = new ArrayList<UnknownHandlerConfig>();
						NodeList unknownHandlers = child.getElementsByTagName("unknown-handler-ref");
						int unknownHandlersSize = unknownHandlers.getLength();
						for (int k = 0; k < unknownHandlersSize; k++) {
							Element unknownHandler = (Element) unknownHandlers.item(k);
							unknownHandlerStack.add(new UnknownHandlerConfig(unknownHandler.getAttribute("name")));
						}
						if (!unknownHandlerStack.isEmpty()) configuration.setUnknownHandlerStack(unknownHandlerStack);
					}
				}
			}
		}
	}

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

保存到DefaultConfiguration packageContexts map集合中

public void loadPackages() throws ConfigurationException {
		List<Element> reloads = new ArrayList<Element>();
		for (Document doc : documents) {
			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 ("package".equals(nodeName)) {
						//解析package节点  包装成PackageConfig对象
						PackageConfig cfg = addPackage(child);
						if (cfg.isNeedsRefresh()) {
							reloads.add(child);
						}
					}
				}
			}
			//空实现  扩展时用
			loadExtraConfiguration(doc);
		}

		if (reloads.size() > 0) {
			reloadRequiredPackages(reloads);
		}
		for (Document doc : documents) {
			//空实现  扩展时用
			loadExtraConfiguration(doc);
		}
		documents.clear();
		configuration = null;
	}

package节点下的所有子节点

	protected PackageConfig addPackage(Element packageElement) throws ConfigurationException {
		PackageConfig.Builder newPackage = buildPackageContext(packageElement);
		if (newPackage.isNeedsRefresh()) {
			return newPackage.build();
		}
		//处理所有的ResultType 包括自定义的 , strust-default.xml中定义的
		addResultTypes(newPackage, packageElement);
		//interceptor节点
		loadInterceptors(newPackage, packageElement);
		//default-interceptor-ref
		loadDefaultInterceptorRef(newPackage, packageElement);
		//default-class-ref节点
		loadDefaultClassRef(newPackage, packageElement);
		//全局result global-results节点
		loadGlobalResults(newPackage, packageElement);
		//global-exception-mappings节点 异常处理 
		loadGobalExceptionMappings(newPackage, packageElement);

		NodeList actionList = packageElement.getElementsByTagName("action");
		for (int i = 0; i < actionList.getLength(); i++) {
			Element actionElement = (Element) actionList.item(i);
			// action节点 result节点处理
			addAction(actionElement, newPackage);
		}
		//default-action-ref
		loadDefaultActionRef(newPackage, packageElement);
		PackageConfig cfg = newPackage.build();
		//TODO 保存到Map packageContexts 集合中
		configuration.addPackageConfig(cfg.getName(), cfg);
		return cfg;
	}

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

	public void rebuildRuntimeConfiguration() {
		runtimeConfiguration = buildRuntimeConfiguration();
	}
	protected synchronized RuntimeConfiguration buildRuntimeConfiguration() throws ConfigurationException {
		Map<String, Map<String, ActionConfig>> namespaceActionConfigs = new LinkedHashMap<String, Map<String, ActionConfig>>();
		Map<String, String> namespaceConfigs = new LinkedHashMap<String, String>();

		for (PackageConfig packageConfig : packageContexts.values()) {

			if (!packageConfig.isAbstract()) {
				String namespace = packageConfig.getNamespace();
				Map<String, ActionConfig> configs = namespaceActionConfigs.get(namespace);

				if (configs == null) {
					configs = new LinkedHashMap<String, ActionConfig>();
				}

				Map<String, ActionConfig> actionConfigs = packageConfig.getAllActionConfigs();

				for (Object o : actionConfigs.keySet()) {
					String actionName = (String) o;
					ActionConfig baseConfig = actionConfigs.get(actionName);
					//这里设置action的拦截器
					//TODO buildFullActionConfig
					configs.put(actionName, buildFullActionConfig(packageConfig, baseConfig));
				}
				//key -> nameSpace
				//value - > <actionName, ActionConfig>  ,ActionConfig包含了拦截器
				namespaceActionConfigs.put(namespace, configs);
				if (packageConfig.getFullDefaultActionRef() != null) {
					namespaceConfigs.put(namespace, packageConfig.getFullDefaultActionRef());
				}
			}
		}
		return new RuntimeConfigurationImpl(namespaceActionConfigs, namespaceConfigs);
	}

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

初始化顺序图:

 

posted on 2012-05-16 18:19  YangJin  阅读(164)  评论(0编辑  收藏  举报