Struts2源码阅读(三)_DispatcherConfigurationProvider
首先強調一下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); } return new Dispatcher(filterConfig.getServletContext(), params); }
分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供init(),destroy(),register()等方法.
將各種ConfigurationProvider初始化之后將實例添加到ConfigurationManager的List里面.
最后通過循環調用List里的這些destroy(),register()等方法實現對配置文件的屬性進行注冊和銷毀等功能.
下面將分析這七層功夫是怎樣一步步練成的.
?XxxProvider類圖:
首先是init_DefaultProperties()
創建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配置文件,默認的struts-default.xml,struts-plugin.xml,struts.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()等方法實現對配置文件的屬性進行注冊和銷毀等功能.
下面將分析這七層功夫是怎樣一步步練成的.
首先是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類圖:
ClassLoaderUtils.getResource( resourceName, ?callingClass ): 查找指定資源
public static URL getResource(String resourceName, Class callingClass) {URL url = null;url = Thread.currentThread().getContextClassLoader().getResource(resourceName);if (url == null) {url = ClassLoaderUtils.class.getClassLoader().getResource(resourceName);}if (url == null) {url = callingClass.getClassLoader().getResource(resourceName);}return url;} 為什么要按照這個順序?前面的加載為什么可能會失敗?
LocatableProperties.load( inputStream ): ?將文件內容加載到 LocatableProperties 對象內。
public void load(InputStream in) throws IOException {Reader reader = new InputStreamReader(in);PropertiesReader pr = new PropertiesReader(reader);while (pr.nextProperty()) {String name = pr.getPropertyName();String val = pr.getPropertyValue();int line = pr.getLineNumber();String desc = convertCommentsToString(pr.getCommentLines());Location loc = new LocationImpl(desc, location.getURI(), line, 0);setProperty(name, val, loc);}}
LocatableProperties.setProperty( name, value, location ): ?如何實現一個屬性名稱對應一個屬性值,而且有對應一個Location
LocatableProperties 內部有一個Map<String,Location> propLocations;
Properties 類本身又類似一個Map,這樣通過兩個Map 來實現內部存儲。
public Object setProperty(String key, String value, Object locationObj) {Object obj = super.setProperty(key, value);if (location != null) {Location loc = LocationUtils.getLocation(locationObj);propLocations.put(key, loc);}return obj;}
的十大書店?
的十大書店?
的十大書店?
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; }
總結
以上是生活随笔為你收集整理的Struts2源码阅读(三)_DispatcherConfigurationProvider的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java调用shell脚本并传递参数
- 下一篇: Exception 和解决办法