在web.xml中我們除了配置actionservlet還配置了一些初始化參數信息,首先我們看第一個config參數,這里配置的是/web-inf/struts-config.xml,因為要下面傳遞一個這樣一個配置信息,這個xml文件名是struts1標準的名字,所以這里這個初始化信息完全可以刪除,如果不用這個標準名稱這里就必須要在這里配置?,F在我們配置的是標準名字,所以我們可以刪除,這是為什么呢?這里要看actionservlet源代碼才可以。
從圖片上我們能看到actionservlet中已經寫好了默認的config信息了,就是標準名字。所以這里刪除也是可以的。
在看下面的debug和detail參數,這兩個參數信息是有關日志信息級別的設置,主要關于解析配置文件/web-inf/struts-config.xml級別的初始化參數。這里這兩個參數可以完全去掉也不影響。
最后還有一個load-on-startup配置,這個是初始化servlet級別的初始化信息,這個參數如果大于等于0就是說明在服務器一啟動就把servlet初始化,也就是調用actionservlet的init方法,這個也可以到actionservlet的源代碼中去查找。
當actionservlet初始化的時候就會讀取/web-inf/struts-config.xml信息到內存中,讀到內存是以什么樣的形式展現的呢?我們現在可以看一下以前博客的那個mvc實例,那里面讀取配置文件中的信息是以actionmapping的形式展現的。另外servlet-mapping的配置就不講解了,這個都知道就是匹配url路徑的,當遇到url-pattern的路徑時候就會實例化actionservlet。
通過這篇文章我們知道了當我們請求的時候actionservlet是怎樣實例化的,也知道為什么我們要配置web.xml信息了。那么我們為什么要配置/web-inf/struts-config.xml文件,actionservlet是如何傳遞請求的,如何和actionform、actionmapping、action等交互的最終完成用戶請求的呢?
我們先從actionservlet源代碼的init方法開始。因為actionservlet就是一個servlet,它也是具有典型的那幾個方法init、doget、dopost等方法。既然是初始化,那么我們就要看init方法。init方法的源代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
/** * <p>initialize this servlet. most of the processing has been factored into * support methods so that you can overrideparticular functionality at a * fairly granular level.</p> * * @exception servletexception if we cannotconfigure ourselves correctly */ publicvoidinit() throwsservletexception { // wraps the entire initialization in a try/catch tobetter handle // unexpected exceptions and errors to provide better feedback // to the developer try { initinternal(); initother(); initservlet(); getservletcontext().setattribute(globals.action_servlet_key, this ); initmoduleconfigfactory(); // initialize modules as needed moduleconfig moduleconfig =initmoduleconfig( "" , config); initmodulemessageresources(moduleconfig); initmoduledatasources(moduleconfig); initmoduleplugins(moduleconfig); moduleconfig.freeze(); enumeration names =getservletconfig().getinitparameternames(); while (names.hasmoreelements()) { string name = (string)namesnextelement(); if (!name.startswith( "config/" )) { continue ; } string prefix =name.substring( 6 ); moduleconfig = initmoduleconfig (prefix,getservletconfig().getinitparameter(name)); initmodulemessageresources(moduleconfig); initmoduledatasources(moduleconfig); initmoduleplugins(moduleconfig); moduleconfig.freeze(); } this .initmoduleprefixes( this .getservletcontext()); thisdestroyconfigdigester(); } catch (unavailableexception ex) { throw ex; } catch (throwable t) { // the follow error message is not retrieved from internal message // resources as they may not have been able to have been // initialized logerror( "unable to initialize struts actionservlet due to an " + "unexpected exception or error thrown, so marking the " + "servlet as unavailable. mostlikely, this is due to an " + "incorrect or missing library dependency." , t); throw new unavailableexception(t.getmessage()); } } |
在解釋這段代碼的流程和意思之前,有必要說一句,就是當我們在eclipse里面看代碼的時候,尤其是看一段生疏的很長的代碼的時候,希望能夠經常使用ctrl鍵(多余的不解釋)。
下面開始講解這段代碼的流程和具體每一步的含義,如果有不正確的地方,希望指正。
首先映入眼簾的是initinternal()方法。這個方法的實現代碼是:
代碼段一:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
/** * <p>initialize our internal messageresourcesbundle</p> * * @exception servletexception if we cannotinitialize these resources */ protectedvoidinitinternal() throwsservletexception { // :fixme: document unavailableexception try { internal = messageresourcesgetmessageresources(internalname); } catch (missingresourceexception e) { log.error( "cannot load internal resources from '" + internalname+ "'" , e); throw new unavailableexception ( "cannot load internal resources from '" + internalname+ "'" ); } } |
代碼段二:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/** * create and return an instance of <code>messageresources</code> for the * created by the default <code>messageresourcesfactory</code>. * * @param config configuration parameterfor this message bundle. */ publicsynchronizedstaticmessageresources getmessageresources(string config) { if (defaultfactory == null ) { defaultfactory =messageresourcesfactory.createfactory(); } return defaultfactory.createresources(config); } |
代碼段三:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
/** * create and return a <code>messageresourcesfactory</code> instance ofthe * appropriate class, which can be used tocreate customized * <code>messageresources</code>instances if no such factory can be * created, return <code>null</code> instead */ publicstaticmessageresourcesfactory createfactory(){ // construct a new instance of the specified factory class try { if (clazz == null ) clazz = requestutils.applicationclass(factoryclass); messageresourcesfactory factory = (messageresourcesfactory) clazz.newinstance(); return (factory); } catch (throwable t) { log.error( "messageresourcesfactory.createfactory" ,t); return ( null ); } } |
這個方法的具體作用就是初始化messageresources,具體實現是工廠模式,首先判斷defaultfactory是否存在,不存在則創建工廠,defaultfactory = messageresourcesfactory.createfactory(),在通過工廠創建資源類defaultfactory.createresources(config);存在則直接創建資源類。
initother()的方法,主要是初始化其它的配置,獲取我們自己的struts-config配置文件的路徑,而它的默認路徑就是web-inf/struts-config.xml,另外這個方法還會注冊一些轉換類的。具體源代碼是:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
/** * <p>initialize other global characteristics ofthe controller servlet</p> * * @exception servletexception if we cannotinitialize these resources */ protectedvoidinitother() throwsservletexception { string value = null ; value =getservletconfig().getinitparameter( "config" ); if (value != null ) { config = value; } // backwards compatibility for form beans of java wrapper classes // set to true for strict struts 0 compatibility value =getservletconfig().getinitparameter( "convertnull" ); if ( "true" .equalsignorecase(value) || "yes" .equalsignorecase(value) || "on" .equalsignorecase(value) || "y" .equalsignorecase(value) || "1" .equalsignorecase(value)) { convertnull = true ; } if (convertnull) { convertutils.deregister(); convertutils.register( new bigdecimalconverter( null ), bigdecimal. class ); convertutils.register( new bigintegerconverter( null ), biginteger. class ); convertutils.register( new booleanconverter( null ), boolean . class ); convertutils.register( new byteconverter( null ), byte . class ); convertutils.register( new characterconverter( null ), character. class ); convertutils.register( new doubleconverter( null ), double . class ); convertutils.register( new floatconverter( null ), float . class ); convertutils.register( new integerconverter( null ), integer. class ); convertutils.register( new longconverter( null ), long . class ); convertutils.register( new shortconverter( null ), short . class ); } } |
initservlet()方法是利用digester讀取web.xml文件并且放到servletcontext中。具體實現源代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
|
/** * <p>initialize the servlet mapping under which our controller servlet * is being accessed. this will be used in the <code>&html:form></code> * tag to generate correct destination urls for form submissions.</p> * * @throws servletexception if error happens while scanning web.xml */ protected void initservlet() throws servletexception { // remember our servlet name this .servletname = getservletconfig().getservletname(); // prepare a digester to scan the web application deployment descriptor digester digester = new digester(); digester.push( this ); digester.setnamespaceaware( true ); digester.setvalidating( false ); // register our local copy of the dtds that we can find for ( int i = 0 ; i < registrations.length; i += 2 ) { url url = this .getclass().getresource(registrations[i+ 1 ]); if (url != null ) { digester.register(registrations[i], url.tostring()); } } // configure the processing rules that we need digester.addcallmethod( "web-app/servlet-mapping" , "addservletmapping" , 2 ); digester.addcallparam( "web-app/servlet-mapping/servlet-name" , 0 ); digester.addcallparam( "web-app/servlet-mapping/url-pattern" , 1 ); // process the web application deployment descriptor if (log.isdebugenabled()) { log.debug( "scanning web.xml for controller servlet mapping" ); } inputstream input = getservletcontext().getresourceasstream( "/web-inf/web.xml" ); if (input == null ) { log.error(internal.getmessage( "configwebxml" )); throw new servletexception(internal.getmessage( "configwebxml" )); } try { digester.parse(input); } catch (ioexception e) { log.error(internal.getmessage( "configwebxml" ), e); throw new servletexception(e); } catch (saxexception e) { log.error(internal.getmessage( "configwebxml" ), e); throw new servletexception(e); } finally { try { input.close(); } catch (ioexception e) { log.error(internal.getmessage( "configwebxml" ), e); throw new servletexception(e); } } // record a servlet context attribute (if appropriate) if (log.isdebugenabled()) { logdebug( "mapping for servlet '" + servletname + "' = '" + servletmapping + "'" ); } if (servletmapping != null ) { getservletcontext().setattribute(globals.servlet_key, servletmapping); } } |
首先說在說之前還是先講init方法的具體實現代碼寫出來以便大家方便閱讀和理解。
init源代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
public void init() throws servletexception { try { //初始化資源類 initinternal(); //注冊轉換類 initother(); //利用digester讀取webxml文件并且將其放到servletcontext中 initservlet(); getservletcontext().setattribute(globals.action_servlet_key, this ); initmoduleconfigfactory(); moduleconfig moduleconfig = initmoduleconfig( "" , config); initmodulemessageresources(moduleconfig); initmoduledatasources(moduleconfig); initmoduleplugins(moduleconfig); moduleconfig.freeze(); enumeration names = getservletconfig().getinitparameternames(); while (names.hasmoreelements()) { string name = (string) names.nextelement(); if (!name.startswith( "config/" )) { continue ; } string prefix = name.substring( 6 ); moduleconfig = initmoduleconfig (prefix, getservletconfig()getinitparameter(name)); initmodulemessageresources(moduleconfig); initmoduledatasources(moduleconfig); initmoduleplugins(moduleconfig); moduleconfig.freeze(); } this .initmoduleprefixes( this .getservletcontext()); this .destroyconfigdigester(); } catch (unavailableexception ex) { throw ex; } catch (throwable t) { log.error( "unable to initialize struts actionservlet due to an " + "unexpected exception or error thrown, so marking the " + "servlet as unavailable most likely, this is due to an " + "incorrect or missing library dependency" , t); throw new unavailableexception(t.getmessage()); } } |
getservletcontext().setattribute(globals.action_servlet_key,this);這句話是將actionservlet實例將以globals.action_servlet_key作為key存入servletcontext中。
這里的globals.action_servlet_key在actionservlet已經給出了聲明:
1
|
public static final string action_servlet_key= "org.apache.struts.action.action_servlet" ; |
接下來initmoduleconfigfactory()方法,這個方法主要的作用是解析在web.xml中configfactory的text值。如果configfactory有配置,則將設置moduleconfigfactory中得factoryclass值,否則默認得為efaultmoduleconfigfactory。該方法其實宗旨是讓開發人員自己開發出moduleconfigfactory,從而得到自己所需要的moduleconfig類。因為我們的實例中沒有配置這個參數信息,所以我們這里的實例是要defalutmodelconfigfactory了。
代碼段一:
1
2
3
4
5
6
|
protected voidinitmoduleconfigfactory(){ string configfactory =getservletconfig().getinitparameter( "configfactory" ); if (configfactory != null ) { moduleconfigfactory.setfactoryclass(configfactory); } } |
代碼段二:
1
2
3
4
|
public static void setfactoryclass(string factoryclass) { moduleconfigfactory.factoryclass = factoryclass; moduleconfigfactory.clazz = null ; } |
代碼段三:
1
2
3
|
protected static string factoryclass = "org.apache.struts.config.impl.defaultmoduleconfigfactory" ; } |
moduleconfig moduleconfig =initmoduleconfig("", config)方法是非常重要的,initmoduleconfig方法給strits-config里面的屬性初始化后放入moduleconfig對象里面去,放到moduleconfig對象里面去便于以后操作更快,因為它是文件流。
具體實現代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
|
protected moduleconfig initmoduleconfig(stringprefix, string paths) throws servletexception { // :fixme: document unavailableexception? (doesn't actually throw anything) if (log.isdebugenabled()) { log.debug( "initializing module path '" + prefix + "' configuration from '" + paths + "'" ); } // parse the configuration for this module moduleconfigfactory factoryobject= moduleconfigfactory.createfactory(); moduleconfig config =factoryobject.createmoduleconfig(prefix); // configure the digester instance we will use digester digester =initconfigdigester(); // process each specified resource path while (paths.length() > 0 ) { digester.push(config); string path = null ; int comma = paths.indexof( ',' ); if (comma >= 0 ) { path =paths.substring( 0 , comma).trim(); paths =paths.substring(comma + 1 ); } else { path = pathstrim(); paths = "" ; } if (pathlength() < 1 ){ break ; } this .parsemoduleconfigfile(digester,path); } getservletcontext().setattribute( globals.module_key +config.getprefix(), config); // force creation and registration of dynaactionformclass instances // for all dynamic form beans we wil be using formbeanconfig fbs[] =config.findformbeanconfigs(); for ( int i = 0 ; i < fbs.length; i++) { if (fbs[i].getdynamic()) { fbs[i].getdynaactionformclass(); } } return config; } |
這里有必要解析一下這段代碼。首先得到繼承moduleconfigfactory的實現類,如果在initmoduleconfigfactory()中能設置factoryclass屬性,則能生成客戶化得factory,否則得到得是默認得defaultmoduleconfigfactory類,該工廠得到moduleconfigimpl類。然后調用initconfigdigester()該方法為解析配置文件做準備,初始化digest類(具體digest的初始化實現就不講解)。最后返回moduleconfig,而這時的moduleconfig里面封裝了所有的struts-config.xml中的信息。
最后的幾個方法就簡單說一下就行,不是非常難理解:
initmodulemessageresources(moduleconfig)方法是通過moduleconfig中的配置文件信息,創建messageresource對象.
initmoduledatasources(moduleconfig)方法是通過moduleconfig中的配置文件信息,創建datasource對象. initmoduleplugins(moduleconfig)加載并初始化默認應用模塊的所有插件的。
moduleconfig.freeze()是將配置文件中的各個對象,設置成已配置狀態.
最后我們看到了,下面還有一段同上面代碼的循環代碼,這段代碼的主要意思就是當默認子應用模塊被成功初始化后,如果應用還包括其他子應用模塊,將重復流程,分別對其他子應用模塊進行初始化。這個也是很好理解的。
到此為止actionservlet就init完成。