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 保存到MappackageContexts 集合中 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变量中
初始化顺序图: