博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Struts2源码浅析-初始化
阅读量:6996 次
发布时间:2019-06-27

本文共 22691 字,大约阅读时间需要 75 分钟。

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
params = new HashMap
(); 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
containerProviders = new CopyOnWriteArrayList
(); }

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
reloadContainer(List
providers) throws ConfigurationException { packageContexts.clear(); loadedFileNames.clear(); List
packageProviders = new ArrayList
(); // 保存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
() { 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
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
documents documents = loadConfigurationFiles(configFileName, null); }

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

private List
loadConfigurationFiles(String fileName, Element includeElement) { List
docs = new ArrayList
(); List
finalDocs = new ArrayList
(); //防止include重复引入 if (!includedFileNames.contains(fileName)) { if (LOG.isDebugEnabled()) { LOG.debug("Loading action configurations from: " + fileName); } includedFileNames.add(fileName); Iterator
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
() { 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
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
loadedBeans = new HashMap
(); 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
unknownHandlerStack = new ArrayList
(); 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
reloads = new ArrayList
(); 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
> namespaceActionConfigs = new LinkedHashMap
>(); Map
namespaceConfigs = new LinkedHashMap
(); for (PackageConfig packageConfig : packageContexts.values()) { if (!packageConfig.isAbstract()) { String namespace = packageConfig.getNamespace(); Map
configs = namespaceActionConfigs.get(namespace); if (configs == null) { configs = new LinkedHashMap
(); } Map
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 - >
,ActionConfig包含了拦截器 namespaceActionConfigs.put(namespace, configs); if (packageConfig.getFullDefaultActionRef() != null) { namespaceConfigs.put(namespace, packageConfig.getFullDefaultActionRef()); } } } return new RuntimeConfigurationImpl(namespaceActionConfigs, namespaceConfigs); }

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

初始化顺序图:

 

转载于:https://www.cnblogs.com/java-wl/archive/2012/05/16/2923166.html

你可能感兴趣的文章
bootstrop
查看>>
文字过长隐藏
查看>>
02 用户注册通过发送邮箱激活
查看>>
通过XDocument方式把List写入Xml文件
查看>>
zTree根节点默认打开
查看>>
【BZOJ5306】 [Haoi2018]染色
查看>>
linux操作系统不重启添加raid0步骤
查看>>
C#判断程序是由Windows服务启动还是用户启动
查看>>
【BZOJ1123】 [POI2008]BLO (tarjan)
查看>>
Django中的CSRF
查看>>
跨年错月查询数据
查看>>
PHP安装时踩坑
查看>>
确定对象被使用前已经被初始化
查看>>
Java中的Filter过滤器
查看>>
jquery实现下拉框多选
查看>>
套接字超时
查看>>
sql server存储过程
查看>>
常见的HTTP返回码如4xx, 5xx
查看>>
Django-CSRF的使用
查看>>
价值观
查看>>