首先強調一下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);
- }
- 都可以從FilterConfig中得到
- return new Dispatcher(filterConfig.getServletContext(), params);
- }
分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供init(),destroy(),register()等方法.
將各種ConfigurationProvider初始化之後將實例添加到ConfigurationManager的List裏面.
最後通過循環調用List裏的這些destroy(),register()等方法實現對配置文件的屬性進行註冊和銷燬等功能.
下面將分析這七層功夫是怎樣一步步練成的.
首先是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配置文件
- 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構造方法
- //讀取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
- loadDefaultCla***ef(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;
- }
Come From: http://qidaoxp.javaeye.com/blog/494444