第一篇:struts2源代碼分析(個(gè)人覺得非常經(jīng)典)
本章講述Struts2的工作原理。
讀者如果曾經(jīng)學(xué)習(xí)過Struts1.x或者有過Struts1.x的開發(fā)經(jīng)驗(yàn),那么千萬不要想當(dāng)然地以為這一章可以跳過。實(shí)際上Struts1.x與Struts2并無我們想象的血緣關(guān)系。雖然Struts2的開發(fā)小組極力保留Struts1.x的習(xí)慣,但因?yàn)镾truts2的核心設(shè)計(jì)完全改變,從思想到設(shè)計(jì)到工作流程,都有了很大的不同。
Struts2是Struts社區(qū)和WebWork社區(qū)的共同成果,我們甚至可以說,Struts2是WebWork的升級(jí)版,他采用的正是WebWork的核心,所以,Struts2并不是一個(gè)不成熟的產(chǎn)品,相反,構(gòu)建在WebWork基礎(chǔ)之上的Struts2是一個(gè)運(yùn)行穩(wěn)定、性能優(yōu)異、設(shè)計(jì)成熟的WEB框架。
本章主要對(duì)Struts的源代碼進(jìn)行分析,因?yàn)镾truts2與WebWork的關(guān)系如此密不可分,因此,讀者需要下載xwork的源代碼,訪問http://文件,則通過過濾器鏈繼續(xù)往下傳送,直到到達(dá)請(qǐng)求的資源為止。
如果getMapping()方法返回有效的ActionMapping對(duì)象,則被認(rèn)為正在請(qǐng)求某個(gè)Action,將調(diào)用Dispatcher.serviceAction(request, response, servletContext, mapping)方法,該方法是處理Action的關(guān)鍵所在。上述過程的源代碼如清單15所示。
代碼清單15:FilterDispatcher.doFilter()方法
publicvoid doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
HttpServletRequest request =(HttpServletRequest)req;
HttpServletResponse response =(HttpServletResponse)res;
ServletContext servletContext = getServletContext();
String timerKey = “FilterDispatcher_doFilter: ”;
try {
UtilTimerStack.push(timerKey);
request = prepareDispatcherAndWrapRequest(request, response);//重新包裝request
ActionMapping mapping;
try {
mapping = actionMapper.getMapping(request, dispatcher.getConfigurationManager());//得到存儲(chǔ)Action信息的ActionMapping對(duì)象
} catch(Exception ex){
……(省略部分代碼)
return;
}
if(mapping == null){//如果mapping為null,則認(rèn)為不是請(qǐng)求Action資源
String resourcePath = RequestUtils.getServletPath(request);
if(“".equals(resourcePath)&& null!= request.getPathInfo()){
resourcePath = request.getPathInfo();
}
{
ngth());
//如果請(qǐng)求的資源以/struts開頭,則當(dāng)作靜態(tài)資源處理
if(serveStatic && resourcePath.startsWith(”/struts“))
String name = resourcePath.substring(”/struts“.le
findStaticResource(name, request, response);} else {
//否則,過濾器鏈繼續(xù)往下傳遞
chain.doFilter(request, response);}
// The framework did its job here
return;
}
//如果請(qǐng)求的資源是Action,則調(diào)用serviceAction方法。
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
}
} finally {
UtilTimerStack.pop(timerKey);
} }
這段代碼的活動(dòng)圖如圖18所示:
(圖18)
在Dispatcher.serviceAction()方法中,先加載Struts2的配置文件,如果沒有人為配置,則默認(rèn)加載struts-default.xml、struts-plugin.xml和struts.xml,并且將配置信息保存在形如com.opensymphony.xwork2.config.entities.XxxxConfig的類中。
類com.opensymphony.xwork2.config.providers.XmlConfigurationProvider負(fù)責(zé)配置文件的讀取和解析,addAction()方法負(fù)責(zé)讀取
代碼清單16:XmlConfigurationProvider.addPackage()方法
protected PackageConfig addPackage(Element packageElement)throws ConfigurationException {
PackageConfig newPackage = buildPackageContext(packageElement);
age
if(newPackage.isNeedsRefresh()){
return newPackage;} if(LOG.isDebugEnabled()){
LOG.debug(”Loaded “ + newPackage);} // add result types(and default result)to this package addResultTypes(newPackage, packageElement);// load the interceptors and interceptor stacks for this packloadInterceptors(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
loadGlobalExceptionMappings(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);
configuration.addPackageConfig(newPackage.getName(), newPackage);
return newPackage;
}
活動(dòng)圖如圖19所示:
(圖19)
配置信息加載完成后,創(chuàng)建一個(gè)Action的代理對(duì)象——ActionProxy引用,實(shí)際上對(duì)Action的調(diào)用正是通過ActionProxy實(shí)現(xiàn)的,而ActionProxy又由ActionProxyFactory創(chuàng)建,ActionProxyFactory是創(chuàng)建ActionProxy的工廠。
注:ActionProxy和ActionProxyFactory都是接口,他們的默認(rèn)實(shí)現(xiàn)類分別是DefaultActionProxy和DefaultActionProxyFactory,位于com.opensymphony.xwork2包下。
在這里,我們絕對(duì)有必要介紹一下com.opensymphony.xwork2.DefaultActionInvocation類,該類是對(duì)ActionInvocation接口的默認(rèn)實(shí)現(xiàn),負(fù)責(zé)Action和截?cái)r器的執(zhí)行。
在DefaultActionInvocation類中,定義了invoke()方法,該方法實(shí)現(xiàn)了截?cái)r器的遞歸調(diào)用和執(zhí)行Action的execute()方法。其中,遞歸調(diào)用截?cái)r器的代碼如清單17所示:
代碼清單17:調(diào)用截?cái)r器,DefaultActionInvocation.invoke()方法的部分代碼
if(interceptors.hasNext()){
//從截?cái)r器集合中取出當(dāng)前的截?cái)r器
final InterceptorMapping interceptor =(InterceptorMapping)interceptors.next();
UtilTimerStack.profile(”interceptor: “+interceptor.getName(),new UtilTimerStack.ProfilingBlock
public String doProfiling()throws Exception {
//執(zhí)行截?cái)r器(Interceptor)接口中定義的intercept方法
resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
returnnull;
}
});
}
從代碼中似乎看不到截?cái)r器的遞歸調(diào)用,其實(shí)是否遞歸完全取決于程序員對(duì)程序的控制,先來看一下Interceptor接口的定義:
代碼清單18:Interceptor.java publicinterface Interceptor extends Serializable {
void destroy();
void init();
String intercept(ActionInvocation invocation)throws Exception;}
所有的截?cái)r器必須實(shí)現(xiàn)intercept方法,而該方法的參數(shù)恰恰又是ActionInvocation,所以,如果在intercept方法中調(diào)用invocation.invoke(),代碼清單17會(huì)再次執(zhí)行,從Action的Intercepor列表中找到下一個(gè)截?cái)r器,依此遞歸。下面是一個(gè)自定義截?cái)r器示例:
代碼清單19:CustomIntercepter.java publicclass CustomIntercepter extends AbstractInterceptor {
@Override
public String intercept(ActionInvocation actionInvocation)throws Exception
} { actionInvocation.invoke();return”李贊紅“;}
截?cái)r器的調(diào)用活動(dòng)圖如圖20所示:
(圖20)
如果截?cái)r器全部執(zhí)行完畢,則調(diào)用invokeActionOnly()方法執(zhí)行Action,invokeActionOnly()方法基本沒做什么工作,只調(diào)用了invokeAction()方法。
為了執(zhí)行Action,必須先創(chuàng)建該對(duì)象,該工作在DefaultActionInvocation的構(gòu)造方法中調(diào)用init()方法早早完成。調(diào)用過程是:DefaultActionInvocation()->init()->createAction()。創(chuàng)建Action的代碼如下:
代碼清單20:DefaultActionInvocation.createAction()方法
protectedvoid createAction(Map contextMap){
try {
action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
} catch(InstantiationException e){
……異常代碼省略
}
}
Action創(chuàng)建好后,輪到invokeAction()大顯身手了,該方法比較長,但關(guān)鍵語句實(shí)在很少,用心點(diǎn)看不會(huì)很難。
代碼清單20:DefaultActionInvocation.invokeAction()方法
protected String invokeAction(Object action, ActionConfig actionConfig)throws Exception {
//獲取Action中定義的execute()方法名稱,實(shí)際上該方法是可以隨便定義的
String methodName = proxy.getMethod();
String timerKey = ”invokeAction: “+proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
Method method;
try {
//將方法名轉(zhuǎn)化成Method對(duì)象
method = getAction().getClass().getMethod(methodName, new Class[0]);
} catch(NoSuchMethodException e){
// hmm--OK, try doXxx instead
try {
//如果Method出錯(cuò),則嘗試在方法名前加do,再轉(zhuǎn)成Method對(duì)象
String altMethodName = ”do“ + methodName.substring(0, 1).toUpperCase()+ methodName.substring(1);
method = getAction().getClass().getMethod(altMethodName, new Class[0]);
} catch(NoSuchMethodException e1){
// throw the original one
throw e;
}
}
//執(zhí)行方法
[0]);
}
Object methodResult = method.invoke(action, new Object
//處理跳轉(zhuǎn)
if(methodResult instanceof Result){
this.result =(Result)methodResult;
returnnull;
} else {
return(String)methodResult;
} } catch(NoSuchMethodException e){
……省略異常代碼 } finally {
UtilTimerStack.pop(timerKey);}
剛才使用了一段插述,我們繼續(xù)回到ActionProxy類。
我們說Action的調(diào)用是通過ActionProxy實(shí)現(xiàn)的,其實(shí)就是調(diào)用了ActionProxy.execute()方法,而該方法又調(diào)用了ActionInvocation.invoke()方法。歸根到底,最后調(diào)用的是DefaultActionInvocation.invokeAction()方法。
以下是調(diào)用關(guān)系圖:
其中:
?
ActionProxy:管理Action的生命周期,它是設(shè)置和執(zhí)行Action的起始點(diǎn)。
?
ActionInvocation:在ActionProxy層之下,它表示了Action的執(zhí)行狀態(tài)。它持有Action實(shí)例和所有的Interceptor
以下是serviceAction()方法的定義:
代碼清單21:Dispatcher.serviceAction()方法
publicvoid serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping)throws ServletException {
Map
// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
ValueStack stack =(ValueStack)request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
if(stack!= null){
extraContext.put(ActionContext.VALUE_STACK, ValueStackFactory.getFactory().createValueStack(stack));
}
String timerKey = ”Handling request from Dispatcher“;
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
Configuration config = configurationManager.getConfiguration();
ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, extraContext, true, false);
proxy.setMethod(method);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
// if the ActionMapping says to go straight to a result, do it!
if(mapping.getResult()!= null){
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
proxy.execute();
}
// If there was a previous value stack then set it back onto the request
if(stack!= null){
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch(ConfigurationException e){
LOG.error(”Could not find action or result", e);
sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
} catch(Exception e){
thrownew ServletException(e);
} finally {
UtilTimerStack.pop(timerKey);
}
}
最后,通過Result完成頁面的跳轉(zhuǎn)。
3.4 本小節(jié)總結(jié)
總體來講,Struts2的工作機(jī)制比Struts1.x要復(fù)雜很多,但我們不得不佩服Struts和WebWork開發(fā)小組的功底,代碼如此優(yōu)雅,甚至能夠感受看到兩個(gè)開發(fā)小組心神相通的默契。兩個(gè)字:佩服。
以下是Struts2運(yùn)行時(shí)調(diào)用方法的順序圖:
(圖21)
四、總結(jié)
閱讀源代碼是一件非常辛苦的事,對(duì)讀者本身的要求也很高,一方面要有扎實(shí)的功底,另一方面要有超強(qiáng)的耐力和恒心。本章目的就是希望能幫助讀者理清一條思路,在必要的地方作出簡單的解釋,達(dá)到事半功倍的效果。
當(dāng)然,筆者不可能為讀者解釋所有類,這也不是我的初衷。Struts2+xwork一共有700余類,除了為讀者做到現(xiàn)在的這些,已無法再做更多的事情。讀者可以到Struts官方網(wǎng)站下載幫助文檔,慢慢閱讀和理解,相信會(huì)受益頗豐。
本章并不適合java語言初學(xué)者或者對(duì)java博大精深的思想理解不深的讀者閱讀,這其中涉及到太多的術(shù)語和類的使用,特別不要去鉆牛角尖,容易使自信心受損?;靖闱宄truts2的使用之后,再回過頭來閱讀本章,對(duì)一些知識(shí)點(diǎn)和思想也許會(huì)有更深的體會(huì)。
如果讀者的java功底比較渾厚,而且對(duì)Struts2充滿興趣,但又沒太多時(shí)間研究,不妨仔細(xì)閱讀本章,再對(duì)照Struts的源代碼,希望對(duì)您有所幫助。
第二篇:struts2代碼分析
1.Struts2架構(gòu)圖和請(qǐng)求處理流程
請(qǐng)求首先通過Filter chain,F(xiàn)ilter主要包括ActionContextCleanUp,它主要清理當(dāng)前線程的ActionContext和Dispatcher;FilterDispatcher主要通過AcionMapper來決定需要調(diào)用哪個(gè)Action。
ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里創(chuàng)建ActionProxy,ActionProxy創(chuàng)建ActionInvocation,然后ActionInvocation調(diào)用Interceptors,執(zhí)行Action本身,創(chuàng)建Result并返回,當(dāng)然,如果要在返回之前做些什么,可以實(shí)現(xiàn)PreResultListener。
2.Struts2部分類介紹
這部分從Struts2參考文檔中翻譯就可以了。
ActionMapper
ActionMapper其實(shí)是HttpServletRequest和Action調(diào)用請(qǐng)求的一個(gè)映射,它屏蔽了Action對(duì)于Request等java Servlet類的依賴。Struts2中它的默認(rèn)實(shí)現(xiàn)類是DefaultActionMapper,ActionMapper很大的用處可以根據(jù)自己的需要來設(shè)計(jì)url格式,它自己也有Restful的實(shí)現(xiàn),具體可以參考文檔的docs¥actionmapper.html。
ActionProxy&ActionInvocation
Action的一個(gè)代理,由ActionProxyFactory創(chuàng)建,它本身不包括Action實(shí)例,默認(rèn)實(shí)現(xiàn)DefaultActionProxy是由ActionInvocation持有Action實(shí)例。ActionProxy作用是如何取得Action,無論是本地還是遠(yuǎn)程。而ActionInvocation的作用是如何執(zhí)行Action,攔截器的功能就是在ActionInvocation中實(shí)現(xiàn)的。
ConfigurationProvider&Configuration
ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其實(shí)現(xiàn)類XmlConfigurationProvider及其子類StrutsXmlConfigurationProvider來解析。
3.Struts2請(qǐng)求流程
1、客戶端發(fā)送請(qǐng)求
2、請(qǐng)求先通過ActionContextCleanUp-->FilterDispatcher
3、FilterDispatcher通過ActionMapper來決定這個(gè)Request需要調(diào)用哪個(gè)Action
4、如果ActionMapper決定調(diào)用某個(gè)Action,F(xiàn)ilterDispatcher把請(qǐng)求的處理交給ActionProxy,這兒已經(jīng)轉(zhuǎn)到它的Delegate--Dispatcher來執(zhí)行
5、ActionProxy根據(jù)ActionMapping和ConfigurationManager找到需要調(diào)用的Action類
6、ActionProxy創(chuàng)建一個(gè)ActionInvocation的實(shí)例
7、ActionInvocation調(diào)用真正的Action,當(dāng)然這涉及到相關(guān)攔截器的調(diào)用
8、Action執(zhí)行完畢,ActionInvocation創(chuàng)建Result并返回,當(dāng)然,如果要在返回之前做些什么,可以實(shí)現(xiàn)PreResultListener。添加PreResultListener可以在Interceptor中實(shí)現(xiàn)。
首先強(qiáng)調(diào)一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個(gè)線程都有自己獨(dú)立的實(shí)例變量,互不相干.接下來就從Dispatcher開始看起,先看其構(gòu)造函數(shù):
//創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對(duì)應(yīng)Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數(shù)
this.initParams = initParams;
}
//創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對(duì)應(yīng)Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數(shù)
this.initParams = initParams;
}
我們?cè)倏丛贔ilterDispatcher創(chuàng)建Dispatcher的:
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
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);
}
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
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接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對(duì)配置文件的屬性進(jìn)行注冊(cè)和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()
創(chuàng)建Dispatcher之后,來看init()方法
init()方法是用來Load用戶配置文件,資源文件以及默認(rèn)的配置文件.主要分七步走,看下面注釋
public void init(){
if(configurationManager == null){
//設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等
configurationManager
=
new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1]
//讀取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//讀取用戶自定義的struts.properties
init_LegacyStrutsProperties();// [3]
//自定義的configProviders
init_CustomConfigurationProviders();// [5]
//載入FilterDispatcher傳進(jìn)來的initParams
init_FilterInitParameters();// [6]
//將配置文件中的bean與具體的類映射
init_AliasStandardObjects();// [7]
//構(gòu)建一個(gè)用于依賴注射的Container對(duì)象
//在這里面會(huì)循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法
//其中的重點(diǎn)就是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);
}
}
}
public void init(){
if(configurationManager == null){
//設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等
configurationManager
=
new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1]
//讀取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//讀取用戶自定義的struts.properties
init_LegacyStrutsProperties();// [3]
//自定義的configProviders
init_CustomConfigurationProviders();// [5]
//載入FilterDispatcher傳進(jìn)來的initParams
init_FilterInitParameters();// [6]
//將配置文件中的bean與具體的類映射
init_AliasStandardObjects();// [7]
//構(gòu)建一個(gè)用于依賴注射的Container對(duì)象
//在這里面會(huì)循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法
//其中的重點(diǎn)就是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接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對(duì)配置文件的屬性進(jìn)行注冊(cè)和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了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
}
loadSettings(props, defaultSettings);
}
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了register()方法
public void register(ContainerBuilder builder, LocatableProperties props)
throws ConfigurationException {
Settings defaultSettings = null;
try {
defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);
} catch(Exception e){
new
ConfigurationException(“Could
not
find
or
error
in org/apache/struts2/default.properties”, e);
throw
}
new ConfigurationException(“Could not find or error in org/apache/struts2/default.properties”, e);
loadSettings(props, defaultSettings);
}
//PropertiesSettings構(gòu)造方法
//讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在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
// 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 {
=
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
in.close();
} catch(IOException io){
LOG.warn(“Unable to close input stream”, io);
}
}
}
}
//loadSettings主要是將progerty的value和Locale從上面PropertiesSettings中取得并存放到LocatableProperties props
//這個(gè)props是register的一個(gè)入?yún)?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.getLocationImpl(name));
}
}
//PropertiesSettings構(gòu)造方法
//讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在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()));
settings.getImpl(name),// 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
//這個(gè)props是register的一個(gè)入?yún)?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.getLocationImpl(name));
}
}
再來看第二步:init_TraditionalXmlConfigurations()
private void init_TraditionalXmlConfigurations(){
settings.getImpl(name), //首先讀取web.xml中的config初始參數(shù)值
//如果
沒
有
配
置
就
使
用
默
認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了
//如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可
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負(fù)責(zé)解析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”);
}
}
}
private void init_TraditionalXmlConfigurations(){
//首先讀取web.xml中的config初始參數(shù)值
//如果
沒
有
配
置
就
使
用
默
認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了
//如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可
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負(fù)責(zé)解析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”);
}
}
}
對(duì)于其它配置文件只用接口。
類XmlConfigurationProvider負(fù)責(zé)配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的
public static Document parse(InputSource inputSource, Map
addAction()方法負(fù)責(zé)讀取
loadInterceptorStack()方法負(fù)責(zé)將
StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實(shí)現(xiàn)ConfigurationProviderloadInterceptorStacks()方法負(fù)責(zé)將
而上面的方法最終會(huì)被addPackage()方法調(diào)用,addPackage又會(huì)被Provider的loadPackages()調(diào)用,將所讀取到的數(shù)據(jù)匯集到PackageConfig對(duì)象中。
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中的內(nèi)容
private List
loadConfigurationFiles(String
fileName,Element includeElement){
...//通過DomHelper調(diào)用SAX進(jìn)行解析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”);
//解析每個(gè)action配置是,對(duì)于include文件可以使用通配符*來進(jìn)行配置
//如Struts.xml中可配置成
if(includeFileName.indexOf('*')!=-1){
ClassPathFinder wildcardFinder = new ClassPathFinder();
wildcardFinder.setPattern(includeFileName);
Vector
for(String match : wildcardMatches){
//遞歸Load子file中的
docs.addAll(loadConfigurationFiles(match, child));
}
} else {
docs.addAll(loadConfigurationFiles(includeFileName, child));
}
}
}
}
docs.add(doc);
loadedFileUrls.add(url.toString());
...return docs;
}
首先強(qiáng)調(diào)一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個(gè)線程都有自己獨(dú)立的實(shí)例變量,互不相干.接下來就從Dispatcher開始看起,先看其構(gòu)造函數(shù):
//創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對(duì)應(yīng)Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數(shù)
this.initParams = initParams;
}
//創(chuàng)建Dispatcher,此類是一個(gè)Delegate,它是真正完成根據(jù)url解析轉(zhuǎn)向,讀取對(duì)應(yīng)Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數(shù)
this.initParams = initParams;
}
我們?cè)倏丛贔ilterDispatcher創(chuàng)建Dispatcher的:
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
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);
}
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
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接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對(duì)配置文件的屬性進(jìn)行注冊(cè)和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()
創(chuàng)建Dispatcher之后,來看init()方法
init()方法是用來Load用戶配置文件,資源文件以及默認(rèn)的配置文件.主要分七步走,看下面注釋
public void init(){
if(configurationManager == null){
//設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等
configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1]
//讀取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//讀取用戶自定義的struts.properties
init_LegacyStrutsProperties();// [3]
//自定義的configProviders
init_CustomConfigurationProviders();// [5]
//載入FilterDispatcher傳進(jìn)來的initParams
init_FilterInitParameters();// [6]
//將配置文件中的bean與具體的類映射
init_AliasStandardObjects();// [7]
//構(gòu)建一個(gè)用于依賴注射的Container對(duì)象
//在這里面會(huì)循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法
//其中的重點(diǎn)就是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);
}
}
new
}
public void init(){
if(configurationManager == null){
//設(shè)置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內(nèi)容,Framework可以是xwork,struts,webwork等
configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);
}
//讀取properties信息,默認(rèn)的default.properties,init_DefaultProperties();// [1]
//讀取xml配置文件
init_TraditionalXmlConfigurations();// [2]
//讀取用戶自定義的struts.properties
init_LegacyStrutsProperties();// [3]
//自定義的configProviders
init_CustomConfigurationProviders();// [5]
//載入FilterDispatcher傳進(jìn)來的initParams
init_FilterInitParameters();// [6]
//將配置文件中的bean與具體的類映射
init_AliasStandardObjects();// [7]
//構(gòu)建一個(gè)用于依賴注射的Container對(duì)象
//在這里面會(huì)循環(huán)調(diào)用上面七個(gè)ConfigurationProvider的register方法
//其中的重點(diǎn)就是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);
}
}
new
}
分七步載入各種配置屬性,都是通過ConfigurationProvider接口進(jìn)行的,這個(gè)接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實(shí)例添加到ConfigurationManager的List里面.最后通過循環(huán)調(diào)用List里的這些destroy(),register()等方法實(shí)現(xiàn)對(duì)配置文件的屬性進(jìn)行注冊(cè)和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了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
}
loadSettings(props, defaultSettings);
}
private void init_DefaultProperties(){
configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());
}
接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實(shí)際上只是實(shí)現(xiàn)了new
ConfigurationException(“Could
not
find
or
error
in org/apache/struts2/default.properties”, e);
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
}
loadSettings(props, defaultSettings);
}
//PropertiesSettings構(gòu)造方法
//讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在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
// Load settings
InputStream in = null;
try {
=
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
new
ConfigurationException(“Could
not
find
or
error
in org/apache/struts2/default.properties”, e);
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
//這個(gè)props是register的一個(gè)入?yún)?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.getLocationImpl(name));
}
}
//PropertiesSettings構(gòu)造方法
//讀取org/apache/struts2/default.properties的配置信息,如果項(xiàng)目中需要覆蓋,可以在classpath里的struts.properties里覆寫
public PropertiesSettings(String name){
URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());
settings.getImpl(name),if(settingsUrl == null){
LOG.debug(name + “.properties missing”);
settings = new LocatableProperties();
return;
}
settings
// 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
//這個(gè)props是register的一個(gè)入?yún)?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();
=
new
LocatableProperties(new
LocationImpl(null, settingsUrl.toString()));
props.setProperty(name, settings.getLocationImpl(name));
}
}
再來看第二步:init_TraditionalXmlConfigurations()
private void init_TraditionalXmlConfigurations(){
//首先讀取web.xml中的config初始參數(shù)值
//如果
沒
有
配
置
就
使
settings.getImpl(name),用默認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了
//如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可
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負(fù)責(zé)解析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”);
}
}
}
private void init_TraditionalXmlConfigurations(){
//首先讀取web.xml中的config初始參數(shù)值
//如果
沒
有
配
置
就
使
用
默
認(rèn)的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認(rèn)的配置文件必須取名為這三個(gè)名稱了
//如果不想使用默認(rèn)的名稱,直接在web.xml中配置config初始參數(shù)即可
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負(fù)責(zé)解析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”);
}
}
}
對(duì)于其它配置文件只用接口。
類XmlConfigurationProvider負(fù)責(zé)配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的
public static Document parse(InputSource inputSource, Map
addAction()方法負(fù)責(zé)讀取
loadInterceptorStack()方法負(fù)責(zé)將
loadInterceptorStacks()方法負(fù)責(zé)將
而上面的方法最終會(huì)被addPackage()方法調(diào)用,addPackage又會(huì)被Provider的loadPackages()調(diào)用,將所讀取到的數(shù)據(jù)匯集到PackageConfig對(duì)象中。
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中的內(nèi)容
private List
loadConfigurationFiles(String
fileName, includeElement){
...//通過DomHelper調(diào)用SAX進(jìn)行解析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”);
//解析每個(gè)action配置是,對(duì)于include文件可以使用通配符*來進(jìn)行配置
//如Struts.xml中可配置成
if(includeFileName.indexOf('*')!=-1){
ClassPathFinder wildcardFinder = new ClassPathFinder();
wildcardFinder.setPattern(includeFileName);
Element
Vector
for(String match : wildcardMatches){
//遞歸Load子file中的
docs.addAll(loadConfigurationFiles(match, child));
}
} else {
docs.addAll(loadConfigurationFiles(includeFileName, child));
}
}
}
}
docs.add(doc);
loadedFileUrls.add(url.toString());
...return docs;
}
接下來第三步:init_LegacyStrutsProperties()調(diào)用的是調(diào)用的是LegacyPropertiesConfigurationProvider 通過比較前
面
DefaultPropertiesProvider
與
調(diào)
用的是LegacyPropertiesConfigurationProvider.發(fā)現(xiàn)DefaultPropertiesProvider繼承自后者,但重寫了register()方法,主要是生成PropertiesSetting的不同,前者是根據(jù)org/apache/struts2/default.properties 后者是根據(jù)struts.properties 我們展開register()中的Settings.getInstance(),最后是調(diào)用getDefaultInstance()
private static Settings getDefaultInstance(){
if(defaultImpl == null){
// Create bootstrap implementation
//不帶參數(shù)的DefaultSettings(),區(qū)別與DefaultPropertiesProvider中直接帶default.properties參數(shù)
//不帶參數(shù)就是默認(rèn)為struts.propertes,并且加載struts.custom.properties所定義的properties文件
defaultImpl = new DefaultSettings();
// Create default implementation
try {
//STRUTS_CONFIGURATION為:struts.configuration
//在struts.proterties中查找struts.configuration的值,這個(gè)值必須是org.apache.struts2.config.Configuration接口的實(shí)現(xiàn)類
//所以我有個(gè)困惑就是在下面的轉(zhuǎn)換當(dāng)中怎么將Configuration轉(zhuǎn)換成Setting類型的...//這一點(diǎn)先放下了,有時(shí)間再研究
String className = get(StrutsConstants.STRUTS_CONFIGURATION);
if(!className.equals(defaultImpl.getClass().getName())){
try {
// singleton instances shouldn't be built accessing request or session-specific context data
defaultImpl oader().loadClass(className), null);
} catch(Exception e){
LOG.error(“Settings:
}
}
} catch(IllegalArgumentException ex){
// ignore
}
private static Settings getDefaultInstance(){
if(defaultImpl == null){
// Create bootstrap implementation
//不帶參數(shù)的DefaultSettings(),區(qū)別與DefaultPropertiesProvider中直接帶default.properties參數(shù)
//不帶參數(shù)就是默認(rèn)為struts.propertes,并且加載struts.custom.properties所定義的properties文件
defaultImpl = new DefaultSettings();
// Create default implementation
try {
//STRUTS_CONFIGURATION為:struts.configuration
//在struts.proterties中查找struts.configuration的值,這個(gè)值必須是
Could
not
instantiate
the struts.configuration object, substituting the default implementation.”, e);
=
(Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLorg.apache.struts2.config.Configuration接口的實(shí)現(xiàn)類
//所以我有個(gè)困惑就是在下面的轉(zhuǎn)換當(dāng)中怎么將Configuration轉(zhuǎn)換成Setting類型的...//這一點(diǎn)先放下了,有時(shí)間再研究
String className = get(StrutsConstants.STRUTS_CONFIGURATION);
if(!className.equals(defaultImpl.getClass().getName())){
try {
// singleton instances shouldn't be built accessing request or session-specific context data
defaultImpl oader().loadClass(className), null);
} catch(Exception e){
LOG.error(“Settings:
}
}
} catch(IllegalArgumentException ex){
// ignore
}
在2.1.6中去掉了第四步:init_ZeroConfiguration();第五步是自定義的configProviders
private void init_CustomConfigurationProviders(){
//從這里可以看到可以將自定義的Provider定義在web.xml中FilterDispatcher的param中:configProviders
String configProvs = initParams.get(”configProviders“);
if(configProvs!= null){
String[] classes = configProvs.split(”¥¥s*[,]¥¥s*“);
for(String cname : classes){
try {
Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
ConfigurationProvider(ConfigurationProvider)cls.newInstance();
configurationManager.addConfigurationProvider(prov);
prov
=
Could
not
instantiate
the struts.configuration object, substituting the default implementation.”, e);
=
(Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassL
}
...}
}
}
private void init_CustomConfigurationProviders(){
//從這里可以看到可以將自定義的Provider定義在web.xml中FilterDispatcher的param中:configProviders
String configProvs = initParams.get(“configProviders”);
if(configProvs!= null){
String[] classes = configProvs.split(“¥¥s*[,]¥¥s*”);
for(String cname : classes){
try {
Class cls = ClassLoaderUtils.loadClass(cname, this.getClass());
ConfigurationProvider(ConfigurationProvider)cls.newInstance();
configurationManager.addConfigurationProvider(prov);
}
...}
}
}
第六步:init_FilterInitParameters
//從這里可以看出struts.properties中的屬性不僅可以在struts.xml中以constant形式定義,而且可以在FilterDispatcher的param中定義
private void init_FilterInitParameters(){
configurationManager.addConfigurationProvider(new ConfigurationProvider(){
public void destroy(){}
public
void
init(Configuration
configuration)
throws ConfigurationException {}
public void loadPackages()throws ConfigurationException {}
public boolean needsReload(){ return false;}
prov
=
public void register(ContainerBuilder builder, LocatableProperties props)throws ConfigurationException {
props.putAll(initParams);//在這里實(shí)現(xiàn)滴~
}
});
}
//從這里可以看出struts.properties中的屬性不僅可以在struts.xml中以constant形式定義,而且可以在FilterDispatcher的param中定義
private void init_FilterInitParameters(){
configurationManager.addConfigurationProvider(new ConfigurationProvider(){
public void destroy(){}
public
void
init(Configuration
configuration)
throws ConfigurationException {}
public void loadPackages()throws ConfigurationException {}
public boolean needsReload(){ return false;}
public void register(ContainerBuilder builder, LocatableProperties props)throws ConfigurationException {
props.putAll(initParams);//在這里實(shí)現(xiàn)滴~
}
});
}
第七步:init_AliasStandardObjects,使用BeanSelectionProvider 這是將配置文件中定義的
接下來是看怎樣調(diào)用這些ConfigurationProviders 展開init_PreloadConfiguration()
private Container init_PreloadConfiguration(){
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;
}
//再看getConfiguration()
public synchronized Configuration getConfiguration(){
if(configuration == null){
setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
try {
//重點(diǎn)就是這個(gè)reloadContainer
configuration.reloadContainer(getContainerProviders());
} catch(ConfigurationException e){
setConfiguration(null);
throw new ConfigurationException(“Unable to load configuration.”, e);
}
} else {
conditionalReload();
}
return configuration;
}
private Container init_PreloadConfiguration(){
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;
}
//再看getConfiguration()
public synchronized Configuration getConfiguration(){
if(configuration == null){
setConfiguration(new DefaultConfiguration(defaultFrameworkBeanName));
try {
//重點(diǎn)就是這個(gè)reloadContainer
configuration.reloadContainer(getContainerProviders());
} catch(ConfigurationException e){
setConfiguration(null);
throw new ConfigurationException(“Unable to load configuration.”, e);
}
} else {
conditionalReload();
}
return configuration;
}
展開DefaultConfiguration中的reloadContainer
public synchronized List
reloadContainer(List
packageContexts.clear();
loadedFileNames.clear();
List
packageProviders = new ArrayList
();
//Struts2(xwork2)用Container來完成依賴注入的功能
//首先初始化一個(gè)ContainerBuilder,再由builder來保存接口與實(shí)現(xiàn)類或工廠類的對(duì)應(yīng)關(guān)系
//然后通過builder.create(boolean)方法產(chǎn)生container
//由container.getInstance(Class);就可以得到接口的實(shí)現(xiàn)實(shí)例了
//這一部分比較復(fù)雜,后面研究完成了,會(huì)單獨(dú)拿出來講,這里先弄清楚Xwork依賴注入的實(shí)現(xiàn)步驟就可以了
ContainerProperties props = new ContainerProperties();
ContainerBuilder builder = new ContainerBuilder();
for(final ContainerProvider containerProvider : providers)
{
//循環(huán)調(diào)用ConfigurationProvider的init和register方法,明白了吧,在這里統(tǒng)一循環(huán)調(diào)用
containerProvider.init(this);
containerProvider.register(builder, props);
}
props.setConstants(builder);
//注入依賴關(guān)系,在這里并不產(chǎn)生實(shí)例
builder.factory(Configuration.class, new Factory
public Configuration create(Context context)throws Exception {
return DefaultConfiguration.this;
}
});
ActionContext oldContext = ActionContext.getContext();
try {
// Set the bootstrap container for the purposes of factory creation
Container bootstrap = createBootstrapContainer();
setContext(bootstrap);
//create已經(jīng)注入依賴關(guān)系的Container
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){
container.inject(containerProvider);
//調(diào)用PackageProvider的loadPackages()方法,這里主要是針對(duì)XmlConfigurationProvider和StrutsXmlConfigurationProvider
((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.init(this);
provider.loadPackages();
packageProviders.add(provider);
}
}
rebuildRuntimeConfiguration();
} finally {
if(oldContext == null){
ActionContext.setContext(null);
}
}
return packageProviders;
}
Dispatcher已經(jīng)在之前講過,這就好辦了。FilterDispatcher是Struts2的核心控制器,首先看一下init()方法。
public void init(FilterConfig filterConfig)throws ServletException {
try {
this.filterConfig = filterConfig;
initLogging();
//創(chuàng)建dispatcher,前面都已經(jīng)講過啰
dispatcher = createDispatcher(filterConfig);
dispatcher.init();
//注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader
dispatcher.getContainer().inject(this);
//StaticContentLoader在BeanSelectionProvider中已經(jīng)被注入了依賴關(guān)系:DefaultStaticContentLoader
//可以在struts-default.xml中的
staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
} finally {
provider
= container.getInstance(PackageProvider.class, name);
ActionContext.setContext(null);
}
}
public void init(FilterConfig filterConfig)throws ServletException {
try {
this.filterConfig = filterConfig;
initLogging();
//創(chuàng)建dispatcher,前面都已經(jīng)講過啰
dispatcher = createDispatcher(filterConfig);
dispatcher.init();
//注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader
dispatcher.getContainer().inject(this);
//StaticContentLoader在BeanSelectionProvider中已經(jīng)被注入了依賴關(guān)系:DefaultStaticContentLoader
//可以在struts-default.xml中的
staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));
} finally {
ActionContext.setContext(null);
}
}
//下面來看DefaultStaticContentLoader的setHostConfig
public void setHostConfig(HostConfig filterConfig){
//讀取初始參數(shù)
pakages,調(diào)用
parse(),解析成類似/org/apache/struts2/static,/template的數(shù)組
String param = filterConfig.getInitParameter(“packages”);
//“org.apache.struts2.static org.apache.struts2.interceptor.debugging static”
String packages = getAdditionalPackages();
if(param!= null){
packages = param + “ ” + packages;
}
this.pathPrefixes = parse(packages);
initLogging(filterConfig);
}
template //下面來看DefaultStaticContentLoader的setHostConfig
public void setHostConfig(HostConfig filterConfig){
//讀取初始參數(shù)
pakages,調(diào)用
parse(),解析成類似/org/apache/struts2/static,/template的數(shù)組
String param = filterConfig.getInitParameter(“packages”);
//“org.apache.struts2.static org.apache.struts2.interceptor.debugging static”
String packages = getAdditionalPackages();
if(param!= null){
packages = param + “ ” + packages;
}
this.pathPrefixes = parse(packages);
initLogging(filterConfig);
}
現(xiàn)在回去doFilter的方法,每當(dāng)有一個(gè)Request,都會(huì)調(diào)用這些Filters的doFilter方法
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
HttpServletRequest request =(HttpServletRequest)req;
HttpServletResponse response =(HttpServletResponse)res;
ServletContext servletContext = getServletContext();
String timerKey = “FilterDispatcher_doFilter: ”;
try {
// FIXME: this should be refactored better to not duplicate work with the action invocation
//先看看ValueStackFactory所注入的實(shí)現(xiàn)類OgnlValueStackFactory
//new OgnlValueStack
ValueStack
stack
= dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
ActionContext ctx = new ActionContext(stack.getContext());
ActionContext.setContext(ctx);
template
UtilTimerStack.push(timerKey);
//如果是multipart/form-data就用MultiPartRequestWrapper進(jìn)行包裝
//MultiPartRequestWrapper
是
StrutsRequestWrapper的子類,兩者都是HttpServletRequest實(shí)現(xiàn)
//此時(shí)在MultiPartRequestWrapper中就會(huì)把Files給解析出來,用于文件上傳
//所有request都會(huì)StrutsRequestWrapper進(jìn)行包裝,StrutsRequestWrapper是可以訪問ValueStack
//下面是參見Dispatcher的wrapRequest
// String content_type = request.getContentType();
//if(content_type!= null&&content_type.indexOf(“multipart/form-data”)!=-1){
//MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class);
//request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext));
//} else {
//
request = new StrutsRequestWrapper(request);
// }
request = prepareDispatcherAndWrapRequest(request, response);
ActionMapping mapping;
try {
//根據(jù)url取得對(duì)應(yīng)的Action的配置信息
//看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲(chǔ)在 ActionMapping對(duì)象中
mapping
=
actionMapper.getMapping(request, dispatcher.getConfigurationManager());
} catch(Exception ex){
log.error(“error getting ActionMapping”, ex);
dispatcher.sendError(request,return;
}
//如果找不到對(duì)應(yīng)的action配置,則直接返回。比如你輸入***.jsp等等
//這兒有個(gè)例外,就是如果path是以“/struts”開頭,則到初始參數(shù)packages配置的包路徑去查找對(duì)應(yīng)的靜態(tài)資源并輸出到頁面流中,當(dāng)然.class文件除外。如果再?zèng)]有則跳轉(zhuǎn)到
response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
=new 404
if(mapping == null){
// there is no action in this request, should we look for a static resource?
String resourcePath = RequestUtils.getServletPath(request);
if(“".equals(resourcePath)&& null!= request.getPathInfo()){
resourcePath = request.getPathInfo();
}
if(staticResourceLoader.canHandle(resourcePath)){
// 在DefaultStaticContentLoader
中
:return
serveStatic
&&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“));
staticResourceLoader.findStaticResource(resourcePath, response);
} else {
// this is a normal request, let it pass through
chain.doFilter(request, response);
}
// The framework did its job here
return;
}
//正式開始Action的方法
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
}
}
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)throws IOException, ServletException {
HttpServletRequest request =(HttpServletRequest)req;
request,HttpServletResponse response =(HttpServletResponse)res;
ServletContext servletContext = getServletContext();
String timerKey = ”FilterDispatcher_doFilter: “;
try {
// FIXME: this should be refactored better to not duplicate work with the action invocation
//先看看ValueStackFactory所注入的實(shí)現(xiàn)類OgnlValueStackFactory
//new OgnlValueStack
ValueStack
stack
= dispatcher.getContainer().getInstance(ValueStackFactory.class).createValueStack();
ActionContext ctx = new ActionContext(stack.getContext());
ActionContext.setContext(ctx);
UtilTimerStack.push(timerKey);
//如果是multipart/form-data就用MultiPartRequestWrapper進(jìn)行包裝
//MultiPartRequestWrapperHttpServletRequest實(shí)現(xiàn)
//此時(shí)在MultiPartRequestWrapper中就會(huì)把Files給解析出來,用于文件上傳
//所有request都會(huì)StrutsRequestWrapper進(jìn)行包裝,StrutsRequestWrapper是可以訪問ValueStack
//下面是參見Dispatcher的wrapRequest
// String content_type = request.getContentType();
//if(content_type!= null&&content_type.indexOf(”multipart/form-data“)!=-1){
//MultiPartRequest multi =getContainer().getInstance(MultiPartRequest.class);
//request MultiPartRequestWrapper(multi,request,getSaveDir(servletContext));
//} else {
//
request = new StrutsRequestWrapper(request);
// }
request = prepareDispatcherAndWrapRequest(request, response);
ActionMapping mapping;
try {
=new
是
StrutsRequestWrapper的子類,兩者都是
//根據(jù)url取得對(duì)應(yīng)的Action的配置信息
//看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲(chǔ)在 ActionMapping對(duì)象中
mapping
} catch(Exception ex){
log.error(”error getting ActionMapping“, ex);
dispatcher.sendError(request,return;
}
//如果找不到對(duì)應(yīng)的action配置,則直接返回。比如你輸入***.jsp等等
//這兒有個(gè)例外,就是如果path是以“/struts”開頭,則到初始參數(shù)packages配置的包路徑去查找對(duì)應(yīng)的靜態(tài)資源并輸出到頁面流中,當(dāng)然.class文件除外。如果再?zèng)]有則跳轉(zhuǎn)到404
if(mapping == null){
// there is no action in this request, should we look for a static resource?
String resourcePath = RequestUtils.getServletPath(request);
if(”“.equals(resourcePath)&& null!= request.getPathInfo()){
resourcePath = request.getPathInfo();
}
if(staticResourceLoader.canHandle(resourcePath)){
// 在DefaultStaticContentLoader
中
:return
serveStatic
&&(resourcePath.startsWith(”/struts“)|| resourcePath.startsWith(”/static“));
staticResourceLoader.findStaticResource(resourcePath, response);
} else {
// this is a normal request, let it pass through
chain.doFilter(request, response);
}
// The framework did its job here
return;
}
request,response,servletContext, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
=
actionMapper.getMapping(request, dispatcher.getConfigurationManager());
//正式開始Action的方法
dispatcher.serviceAction(request, response, servletContext, mapping);
} finally {
try {
ActionContextCleanUp.cleanUp(req);
} finally {
UtilTimerStack.pop(timerKey);
}
}
}
//下面是ActionMapper接口的實(shí)現(xiàn)類 DefaultActionMapper的getMapping()方法的源代碼:
public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){
ActionMapping mapping = new ActionMapping();
String uri = getUri(request);//得到請(qǐng)求路徑的URI,如:testAtcion.action或testAction.do
int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時(shí)找不到而且的bug
uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri;
uri = dropExtension(uri, mapping);//刪除擴(kuò)展名,默認(rèn)擴(kuò)展名為action
if(uri == null){
return null;
}
parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace
handleSpecialParameters(request, mapping);//去掉重復(fù)參數(shù)
//如果Action的name沒有解析出來,直接返回
if(mapping.getName()== null){
returnnull;
}
//下面處理形如testAction!method格式的請(qǐng)求路徑
if(allowDynamicMethodCalls){
// handle ”name!method“ convention.String name = mapping.getName();
int exclamation = name.lastIndexOf(”!“);//!是Action名稱和方法名的分隔符
if(exclamation!=-1){
mapping.setName(name.substring(0, exclamation));//提取左邊為name
mapping.setMethod(name.substring(exclamation + 1));//提取右邊的method
}
}
return mapping;
}
//下面是ActionMapper接口的實(shí)現(xiàn)類 DefaultActionMapper的getMapping()方法的源代碼:
public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){
ActionMapping mapping = new ActionMapping();
String uri = getUri(request);//得到請(qǐng)求路徑的URI,如:testAtcion.action或testAction.do
int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時(shí)找不到而且的bug
uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri;
uri = dropExtension(uri, mapping);//刪除擴(kuò)展名,默認(rèn)擴(kuò)展名為action
if(uri == null){
return null;
}
parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace
handleSpecialParameters(request, mapping);//去掉重復(fù)參數(shù)
//如果Action的name沒有解析出來,直接返回
if(mapping.getName()== null){
returnnull;
}
//下面處理形如testAction!method格式的請(qǐng)求路徑
if(allowDynamicMethodCalls){
// handle ”name!method“ convention.String name = mapping.getName();
int exclamation = name.lastIndexOf(”!“);//!是Action名稱和方法名的分隔符
if(exclamation!=-1){
mapping.setName(name.substring(0, exclamation));//提取左邊為name
mapping.setMethod(name.substring(exclamation + 1));//提取右邊的method
}
}
return mapping;
}
從代碼中看出,getMapping()方法返回ActionMapping類型的對(duì)象,該對(duì)象包含三個(gè)參數(shù):Action的name、namespace和要調(diào)用的方法method。
如果getMapping()方法返回ActionMapping對(duì)象為null,則FilterDispatcher認(rèn)為用戶請(qǐng)求不是Action,自然另當(dāng)別論,F(xiàn)ilterDispatcher會(huì)做一件非常有意思的事:如果請(qǐng)求以/struts開頭,會(huì)自動(dòng)查找在web.xml文件中配置的 packages初始化參數(shù),就像下面這樣:
org.apache.struts2.dispatcher.FilterDispatcher
packages
com.lizanhong.action
org.apache.struts2.dispatcher.FilterDispatcher
packages
com.lizanhong.action
FilterDispatcher會(huì)將com.lizanhong.action包下的文件當(dāng)作靜態(tài)資源處理,即直接在頁面上顯示文件內(nèi)容,不過會(huì)忽略擴(kuò)展名為class的文件。比如在com.lizanhong.action包下有一個(gè)aaa.txt的文本文件,其內(nèi)容為“中華人民共和國”,訪問
http://localhost:8081/Struts2Demo/struts/aaa.txt時(shí)會(huì)輸出txt中的內(nèi)容
FilterDispatcher.findStaticResource()方法
protectedvoid findStaticResource(String
name,HttpServletRequest
request, HttpServletResponse response)throws IOException {
if(!name.endsWith(”.class“)){//忽略class文件
//遍歷packages參數(shù)
for(String pathPrefix : pathPrefixes){
InputStream is = findInputStream(name, pathPrefix);//讀取請(qǐng)求文件流
if(is!= null){
...// set the content-type header
String contentType = getContentType(name);//讀取內(nèi)容類型
if(contentType!= null){
response.setContentType(contentType);//重新設(shè)置內(nèi)容類型
}
...try {
//將讀取到的文件流以每次復(fù)制4096個(gè)字節(jié)的方式循環(huán)輸出
copy(is, response.getOutputStream());
} finally {
is.close();
}
return;
}
}
}
}
protectedvoid findStaticResource(String
name,HttpServletRequest
request, HttpServletResponse response)throws IOException {
if(!name.endsWith(”.class“)){//忽略class文件
//遍歷packages參數(shù)
for(String pathPrefix : pathPrefixes){
InputStream is = findInputStream(name, pathPrefix);//讀取請(qǐng)求文件流
if(is!= null){
...// set the content-type header
String contentType = getContentType(name);//讀取內(nèi)容類型
if(contentType!= null){
response.setContentType(contentType);//重新設(shè)置內(nèi)容類型
}
...try {
//將讀取到的文件流以每次復(fù)制4096個(gè)字節(jié)的方式循環(huán)輸出
copy(is, response.getOutputStream());
} finally {
is.close();
}
return;
}
}
}
}
如果用戶請(qǐng)求的資源不是以/struts開頭——可能是.jsp文件,也可能是.html文件,則通過過濾器鏈繼續(xù)往下傳送,直到到達(dá)請(qǐng)求的資源為止。
如果getMapping()方法返回有效的ActionMapping對(duì)象,則被認(rèn)為正在請(qǐng)求某個(gè)Action,將調(diào)用 Dispatcher.serviceAction(request, response, servletContext, mapping)方法,該方法是處理Action的關(guān)鍵所在。
下面就來看serviceAction,這又回到全局變量dispatcher中了
//Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping)throws ServletException {
//createContextMap方法主要把Application、Session、Request的key value值拷貝到Map中
Map
// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
ValueStack
stack
=
(ValueStack)request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
boolean nullStack = stack == null;
if(nullStack){
ActionContext ctx = ActionContext.getContext();
if(ctx!= null){
stack = ctx.getValueStack();
}
}
if(stack!= null){
extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
}
String timerKey = ”Handling request from Dispatcher“;
try {
UtilTimerStack.push(timerKey);
String namespace = mapping.getNamespace();
String name = mapping.getName();
String method = mapping.getMethod();
Configuration config = configurationManager.getConfiguration();
//創(chuàng)建一個(gè)Action的代理對(duì)象,ActionProxyFactory是創(chuàng)建ActionProxy的工廠
//參考實(shí)現(xiàn)類:DefaultActionProxy和DefaultActionProxyFactory
ActionProxy
proxy
= config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(namespace, name, method, extraContext, true, false);
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
// if the ActionMapping says to go straight to a result, do it!
//如果是Result,則直接轉(zhuǎn)向,關(guān)于Result,ActionProxy,ActionInvocation下一講中再分析
if(mapping.getResult()!= null){
Result result = mapping.getResult();
result.execute(proxy.getInvocation());
} else {
//執(zhí)行Action
proxy.execute();
}
// If there was a previous value stack then set it back onto the request
if(!nullStack){
request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
}
} catch(ConfigurationException e){
// WW-2874 Only log error if in devMode
if(devMode){
LOG.error(”Could not find action or result“, e);
}
else {
LOG.warn(”Could not find action or result“, e);
}
sendError(request, HttpServletResponse.SC_NOT_FOUND, e);
} catch(Exception e){
sendError(request,} finally {
UtilTimerStack.pop(timerKey);
}
} 下面開始講一下主菜ActionProxy了.在這之前最好先去了解一下動(dòng)態(tài)Proxy的基本知識(shí).ActionProxy是Action的一個(gè)代理類,也就是說Action的調(diào)用是通過ActionProxy實(shí)現(xiàn)的,其實(shí)就是調(diào)用了ActionProxy.execute()方法,而該方法又調(diào)用了ActionInvocation.invoke()方法。歸根到底,最后調(diào)用的是DefaultActionInvocation.invokeAction()方法。DefaultActionInvocation()->init()->createAction()。
最后
通
過
調(diào)
用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()這里的步驟是先由ActionProxyFactory創(chuàng)建ActionInvocation和ActionProxy.public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map
ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return }
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map
createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext);
response,context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
response,context,ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return }
下面先看DefaultActionInvocation的init方法
public void init(ActionProxy proxy){
this.proxy = proxy;
Map
// Setting this so that other classes, like object factories, can use the ActionProxy and other
// contextual information to operate
ActionContext actionContext = ActionContext.getContext();
if(actionContext!= null){
actionContext.setActionInvocation(this);
}
//創(chuàng)建Action,struts2中每一個(gè)Request都會(huì)創(chuàng)建一個(gè)新的Action
createAction(contextMap);
if(pushAction){
stack.push(action);
contextMap.put(”action“, action);
}
invocationContext = new ActionContext(contextMap);
invocationContext.setName(proxy.getActionName());
// get a new List so we don't get problems with the iterator if someone changes the list
List
interceptorList
=
new ArrayList
interceptors = interceptorList.iterator();
createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext);
}
protected void createAction(Map
// load action
String timerKey = ”actionCreate: “ + proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
//默認(rèn)為SpringObjectFactory:struts.objectFactory=spring.這里非常巧妙,在struts.properties中可以重寫這個(gè)屬性
//在前面BeanSelectionProvider中通過配置文件為ObjectFactory設(shè)置實(shí)現(xiàn)類
//這里以Spring為例,這里會(huì)調(diào)到SpringObjectFactory的buildBean方法,可以通過ApplicationContext的getBean()方法得到Spring的Bean
action
=
objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);
} catch(InstantiationException e){
throw new
XWorkException(”Unable
to
intantiate
Action!“,e, proxy.getConfig());
} catch(IllegalAccessException e){
throw new XWorkException(”Illegal access to constructor, is it public?", e, proxy.getConfig());
} catch(Exception e){
...} finally {
UtilTimerStack.pop(timerKey);
}
if(actionEventListener!= null){
action = actionEventListener.prepare(action, stack);
}
}
//SpringObjectFactory
public Object buildBean(String beanName, Map
Object o = null;
try {
//SpringObjectFactory
會(huì)
通
過
web.xml
中的
第三篇:Android系統(tǒng)啟動(dòng)源代碼調(diào)查分析
Android系統(tǒng)啟動(dòng)調(diào)查。
目的:Android程序入口在哪里?Mainifest配置文件如何加載實(shí)例化?從系統(tǒng)層到應(yīng)用層如何使用?
目標(biāo)從系統(tǒng)角度來了解Android啟動(dòng)過程,通過下載源代碼并且根據(jù)源代碼從底層開始跟蹤,跟著方法走一遍Android啟動(dòng)過程。了解Zygote進(jìn)程是什么?
開機(jī)一開始:Linux啟動(dòng)這一層,主要包括了兩塊:BootLoader(嵌入式系統(tǒng)的引導(dǎo)程序)和Kernel(Linux內(nèi)核層,驅(qū)動(dòng)層)第二塊:Android系統(tǒng)啟動(dòng)。
我們都知道,Linux系統(tǒng)啟動(dòng),定義了一個(gè)Init.rc這個(gè)系統(tǒng)啟動(dòng)的配置文件(放在System/bin文件下面)。
Init.rc啟動(dòng)的時(shí)候,最開始啟動(dòng)了SystemManager守護(hù)進(jìn)程,它的源代碼是一個(gè)Java文件,守護(hù)進(jìn)程是一個(gè)與界面無關(guān),會(huì)持續(xù)運(yùn)行在后臺(tái),用來接收響應(yīng),并且維持系統(tǒng)運(yùn)營的。
在啟動(dòng)servicemanager的同時(shí),再來啟動(dòng)Zygote,Zygote實(shí)際上啟動(dòng)的是:app_main.cpp的系統(tǒng)文件.這個(gè)文件的main()方法,會(huì)調(diào)用Android_Runtime.cpp的文件中的start()方法,這個(gè)方法通過JNI機(jī)制,來調(diào)用ZygoteInit.java孵化器初始文件,這個(gè)文件的Main()函數(shù),將會(huì)去調(diào)用所有進(jìn)程。
這個(gè)ZygoteInit文件的main()函數(shù),這個(gè)函數(shù)通過JNI機(jī)制調(diào)用了FrameWrok中的SystemServer文件,這個(gè)文件有三個(gè)函數(shù):main(),init1()和init2()方法。
Init1()方法會(huì)通過JNI機(jī)制再去調(diào)用com_Android__server_SystemService.java的原生態(tài)文件,去實(shí)現(xiàn)系統(tǒng)初始化的操作,(調(diào)用System_init.cpp)。
當(dāng)系統(tǒng)初始化工作做完之后,系統(tǒng)反過來會(huì)調(diào)用SystemServer文件下面的init2()方法,會(huì)通過runtime方法調(diào)用ServerThread進(jìn)程去調(diào)用激活其他的所有進(jìn)程。
第三塊:應(yīng)用程序啟動(dòng)(下次再講)。
使用工具:【代碼分析工具】source Insight 【源代碼】 Android 源代碼包
操作步驟:
在下載好Android SDK 安裝包之后(如果沒有下載好請(qǐng)移步這里)
【配置代碼分析工具】
打開source Insight 軟件,來配置Android源代碼。
“項(xiàng)目”→“新建項(xiàng)目”
在“新項(xiàng)目名”填寫:“Android 14”(Android 第14個(gè)版本,代表Android V4.0.3)在“項(xiàng)目文件儲(chǔ)存位置”填寫:SDK源代碼包的位置
繼續(xù)進(jìn)行配置,點(diǎn)擊確定。
選中右邊的所有文件夾,點(diǎn)擊“添加所有”按鈕,將這個(gè)版本的源代碼全部導(dǎo)入。
應(yīng)用級(jí)別:選中將所有的子集目錄,下級(jí)子目錄中的所有文件都導(dǎo)入查找項(xiàng)目。
進(jìn)行檢索。。。
一共找到了“213720”個(gè)文件,是否導(dǎo)入?選中“Yes”
導(dǎo)入文件,索引建立
這時(shí)候,查看正下方,項(xiàng)目文件(213720)已經(jīng)全部導(dǎo)入,項(xiàng)目準(zhǔn)備完畢??梢赃M(jìn)行調(diào)查了。
這時(shí)候你看到的右邊工具欄,就是我們可以用來方便查找的搜索欄,輸入對(duì)應(yīng)的關(guān)鍵字即可。
切入正題,查找Android系統(tǒng)啟動(dòng)文件
【查找Init文件】啟動(dòng)方法會(huì)初始化MainiFest.xml配置文件,配置文件再去調(diào)用里面的配置,但是啟動(dòng)方法何時(shí)啟動(dòng)的調(diào)查,還未找到源頭,只知道一切事物的源頭,從這里開始。
原代碼如下:
service console /system/bin/sh(啟動(dòng)Linux內(nèi)核)
console
disabled
user shell
group log
on property:ro.secure=0
start console
# adbd is controlled by the persist.service.adb.enable system property service adbd /sbin/adbd
disabled
# adbd on at boot in emulator on property:ro.kernel.qemu=1
start adbd
on property:persist.service.adb.enable=1
start adbd
on property:persist.service.adb.enable=0
stop adbd
service servicemanager /system/bin/servicemanager(啟動(dòng)服務(wù)管理進(jìn)程)
user system
critical
onrestart restart zygote
onrestart restart media
service vold /system/bin/vold
socket vold stream 0660 root mount
ioprio be 2
service netd /system/bin/netd
socket netd stream 0660 root system
socket dnsproxyd stream 0660 root inet
service debuggerd /system/bin/debuggerd
service ril-daemon /system/bin/rild
socket rild stream 660 root radio
socket rild-debug stream 660 radio system
user root
group radio cache inet misc audio sdcard_rw
service zygote /system/bin/app_process-Xzygote /system/bin--zygote--start-system-server
socket zygote stream 666
onrestart write /sys/android_power/request_state wake
onrestart write /sys/power/state on
onrestart restart media
onrestart restart netd
OK,現(xiàn)在先調(diào)查(ServerManager)這個(gè)啟動(dòng)進(jìn)程。
在system/core/libsysutils/src 目錄下(系統(tǒng)級(jí)啟動(dòng)進(jìn)程)
(啟動(dòng)孵化器進(jìn)程)
在左側(cè)點(diǎn)擊start方法
這就是守護(hù)進(jìn)程中的源代碼之一,start()方法 ServiceManager::ServiceManager(){ } int ServiceManager::start(const char *name){ //如果進(jìn)程已經(jīng)啟動(dòng),那么打印日志:“XX進(jìn)程已經(jīng)啟動(dòng)”
if(isRunning(name)){
SLOGW(“Service '%s' is already running”, name);
return 0;
}
SLOGD(“Starting service '%s'”, name);
property_set(“ctl.start”, name);
int count = 200;
while(count--){
sched_yield();
if(isRunning(name))
break;
}
if(!count){
SLOGW(“Timed out waiting for service '%s' to start”, name);
errno = ETIMEDOUT;
return-1;
}
SLOGD(“Sucessfully started '%s'”, name);
return 0;}
再來看同時(shí)啟動(dòng)的app_main的源代碼,我們?nèi)ゲ榭匆幌滤膍ain函數(shù)
int main(int argc, const char* const argv[]){
// These are global variables in ProcessState.cpp
mArgC = argc;
mArgV = argv;
mArgLen = 0;
for(int i=0;i mArgLen += strlen(argv[i])+ 1; } mArgLen--; AppRuntime runtime; const char *arg; const char *argv0; argv0 = argv[0]; // Process command line arguments // ignore argv[0] argc--; argv++; // Everything up to '--' or first non '-' arg goes to the vm int i = runtime.addVmArguments(argc, argv); // Next arg is parent directory if(i < argc){ runtime.mParentDir = argv[i++]; } // Next arg is startup classname or “--zygote” if(i < argc){ arg = argv[i++]; if(0 == strcmp(“--zygote”, arg)){ bool startSystemServer =(i < argc)? strcmp(argv[i], “--start-system-server”)== 0 : false; setArgv0(argv0, “zygote”); //設(shè)置了一個(gè)進(jìn)程名叫zygote的進(jìn)程,通過runtime來啟動(dòng)ZygoteInit文件中的startSystemServer方法 set_process_name(“zygote”); runtime.start(“com.android.internal.os.ZygoteInit”,startSystemServer); } else { set_process_name(argv0); runtime.mClassName = arg; // Remainder of args get passed to startup class main() runtime.mArgC = argc-i; runtime.mArgV = argv+i; LOGV(“App process is starting with pid=%d, class=%s.n”,getpid(), runtime.getClassName()); runtime.start(); } } else { LOG_ALWAYS_FATAL(“app_process: no class name or--zygote supplied.”); fprintf(stderr, “Error: no class name or--zygote supplied.n”); app_usage(); return 10; } } 調(diào)查一下runtime的類。AppRuntime,這就是android系統(tǒng)的運(yùn)行時(shí)類,它啟動(dòng)了zygote孵化器進(jìn)程,用來孵化Davlik虛擬機(jī)的。 runtime.start(“com.android.internal.os.ZygoteInit”,startSystemServer);所涉及到的ZygoteInit文件。 找到ZygoteInit文件(FrameWork里面的一個(gè)java類)。先去看看Main函數(shù)。 public static void main(String argv[]){ try { VMRuntime.getRuntime().setMinimumHeapSize(5 * 1024 * 1024); // Start profiling the zygote initialization.SamplingProfilerIntegration.start(); registerZygoteSocket(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis()); preloadClasses(); //cacheRegisterMaps(); preloadResources(); EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis()); // Finish profiling the zygote initialization.SamplingProfilerIntegration.writeZygoteSnapshot(); // Do an initial gc to clean up after startup gc(); // If requested, start system server directly from Zygote if(argv.length!= 2){ throw new RuntimeException(argv[0] + USAGE_STRING); } if(argv[1].equals(“true”)){ //如果輸入?yún)?shù)為真,我們就啟動(dòng)系統(tǒng)服務(wù) startSystemServer(); } else if(!argv[1].equals(“false”)){ throw new RuntimeException(argv[0] + USAGE_STRING); } Log.i(TAG, “Accepting command socket connections”); if(ZYGOTE_FORK_MODE){ //如果孵化器一直是交叉模式,就啟動(dòng)運(yùn)行交叉模式函數(shù);否則就選擇另一個(gè)循環(huán)模式 runForkMode(); } else { runSelectLoopMode(); } closeServerSocket(); } catch(MethodAndArgsCaller caller){ caller.run(); } catch(RuntimeException ex){ Log.e(TAG, “Zygote died with exception”, ex); closeServerSocket(); throw ex; } } 我們繼續(xù)查看,如果參數(shù)為真的情況下,ZygoteInit文件中的,startSystemServer()函數(shù)的源代碼。 /** * Prepare the arguments and fork for the system server process.*/ private static boolean startSystemServer() throws MethodAndArgsCaller, RuntimeException { /* Hardcoded command line to start the system server */ String args[] = { “--setuid=1000”,“--setgid=1000”,“--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,3001,3002,3003”,“--capabilities=130104352,130104352”,“--runtime-init”,“--nice-name=system_server”,“com.android.server.SystemServer”,//這個(gè)虛擬機(jī)的名字叫system Server }; ZygoteConnection.Arguments parsedArgs = null; int pid; try { parsedArgs = new ZygoteConnection.Arguments(args); /* * Enable debugging of the system process if *either* the command line flags * indicate it should be debuggable or the ro.debuggable system property * is set to “1” */ int debugFlags = parsedArgs.debugFlags; if(“1”.equals(SystemProperties.get(“ro.debuggable”))) debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER; /* Request to fork the system server process */ pid = Zygote.forkSystemServer(parsedArgs.uid, parsedArgs.gid,parsedArgs.gids, debugFlags, null,parsedArgs.permittedCapabilities,parsedArgs.effectiveCapabilities); } catch(IllegalArgumentException ex){ throw new RuntimeException(ex); } /* For child process */ if(pid == 0){ handleSystemServerProcess(parsedArgs); } return true; } 我們繼續(xù)去查看 system Server的源代碼 main函數(shù): /** * This method is called from Zygote to initialize the system.This will cause the native * services(SurfaceFlinger, AudioFlinger, etc..)to be started.After that it will call back * up into init2()to start the Android services.*/ native public static void init1(String[] args);//Init1()函數(shù)卻是個(gè)空函數(shù) public static void main(String[] args){ if(System.currentTimeMillis()< EARLIEST_SUPPORTED_TIME){ // If a device's clock is before 1970(before 0), a lot of // APIs crash dealing with negative numbers, notably // java.io.File#setLastModified, so instead we fake it and // hope that time from cell towers or NTP fixes it // shortly.Slog.w(TAG, “System clock is before 1970;setting to 1970.”); SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME); } if(SamplingProfilerIntegration.isEnabled()){ SamplingProfilerIntegration.start(); timer = new Timer(); timer.schedule(new TimerTask(){ @Override public void run(){ SamplingProfilerIntegration.writeSnapshot(“system_server”); } }, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL); } // The system server has to run all of the time, so it needs to be // as efficient as possible with its memory usage.VMRuntime.getRuntime().setTargetHeapUtilization(0.8f); System.loadLibrary(“android_servers”); init1(args);// main()函數(shù)中,會(huì)調(diào)用到 init1()的方法。 } public static final void init2(){ Slog.i(TAG, “Entered the Android system server!”); Thread thr = new ServerThread(); thr.setName(“android.server.ServerThread”); thr.start(); } 因?yàn)橥ㄟ^調(diào)查發(fā)現(xiàn),SystemServer文件的main()函數(shù)調(diào)用的init1()函數(shù),是一個(gè)空方法,native public static void init1(String[] args); 但是根據(jù)JNI調(diào)用機(jī)制,我們可以在同名文件夾(framework/base/services/)下找到JNL目錄,然后找到和系統(tǒng)相關(guān)的com_android_server_SystemServer.java文件 使用“C”的動(dòng)態(tài)鏈接嗲用system_init 的方法。它去回調(diào)Init2的方法 我們繼續(xù)看看SystemServer方法的Init2()方法是看什么用的。 去調(diào)查一下ServerThread()方法是干什么用的?這個(gè)內(nèi)部類ServerThread就是啟動(dòng),并且實(shí)例化每一個(gè)系統(tǒng)進(jìn)程的線程類 class ServerThread extends Thread { private static final String TAG = “SystemServer”; private final static boolean INCLUDE_DEMO = false; private static final int LOG_BOOT_PROGRESS_SYSTEM_RUN = 3010; private ContentResolver mContentResolver; private class AdbSettingsObserver extends ContentObserver { public AdbSettingsObserver(){ super(null); } @Override public void onChange(boolean selfChange){ boolean enableAdb =(Settings.Secure.getInt(mContentResolver,Settings.Secure.ADB_ENABLED, 0)> 0); // setting this secure property will start or stop adbd SystemProperties.set(“persist.service.adb.enable”, enableAdb ? “1” : “0”); } } @Override public void run(){ EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,SystemClock.uptimeMillis()); Looper.prepare(); android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_FOREGROUND); BinderInternal.disableBackgroundScheduling(true); android.os.Process.setCanSelfBackground(false); // Check whether we failed to shut down last time we tried.{ final String shutdownAction = SystemProperties.get(ShutdownThread.SHUTDOWN_ACTION_PROPERTY, “"); if(shutdownAction!= null && shutdownAction.length()> 0){ boolean reboot =(shutdownAction.charAt(0)== '1'); final String reason; if(shutdownAction.length()> 1){ reason = shutdownAction.substring(1, shutdownAction.length()); } else { reason = null; } ShutdownThread.rebootOrShutdown(reboot, reason); } } String factoryTestStr = SystemProperties.get(”ro.factorytest“); int factoryTest = ”“.equals(factoryTestStr)? SystemServer.FACTORY_TEST_OFF : Integer.parseInt(factoryTestStr); LightsService lights = null; PowerManagerService power = null; BatteryService battery = null; ConnectivityService connectivity = null; IPackageManager pm = null; Context context = null; WindowManagerService wm = null; BluetoothService bluetooth = null; BluetoothA2dpService bluetoothA2dp = null; HeadsetObserver headset = null; DockObserver dock = null; UsbService usb = null; UiModeManagerService uiMode = null; RecognitionManagerService recognition = null; ThrottleService throttle = null; // Critical services...try { Slog.i(TAG, ”Entropy Service“); ServiceManager.addService(”entropy“, new EntropyService()); Slog.i(TAG, ”Power Manager“); power = new PowerManagerService(); ServiceManager.addService(Context.POWER_SERVICE, power); Slog.i(TAG, ”Activity Manager“); context = ActivityManagerService.main(factoryTest); Slog.i(TAG, ”Telephony Registry“); ServiceManager.addService(”telephony.registry“, new TelephonyRegistry(context)); AttributeCache.init(context); Slog.i(TAG, ”Package Manager“); pm = PackageManagerService.main(context,factoryTest!= SystemServer.FACTORY_TEST_OFF); ActivityManagerService.setSystemProcess(); mContentResolver = context.getContentResolver(); // The AccountManager must come before the ContentService try { Slog.i(TAG, ”Account Manager“); ServiceManager.addService(Context.ACCOUNT_SERVICE,new AccountManagerService(context)); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Account Manager“, e); } Slog.i(TAG, ”Content Manager“); ContentService.main(context,factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL); Slog.i(TAG, ”System Content Providers“); ActivityManagerService.installSystemProviders(); Slog.i(TAG, ”Battery Service“); battery = new BatteryService(context); ServiceManager.addService(”battery“, battery); Slog.i(TAG, ”Lights Service“); lights = new LightsService(context); Slog.i(TAG, ”Vibrator Service“); ServiceManager.addService(”vibrator“, new VibratorService(context)); // only initialize the power service after we have started the // lights service, content providers and the battery service.power.init(context, lights, ActivityManagerService.getDefault(), battery); Slog.i(TAG, ”Alarm Manager“); AlarmManagerService alarm = new AlarmManagerService(context); ServiceManager.addService(Context.ALARM_SERVICE, alarm); Slog.i(TAG, ”Init Watchdog“); Watchdog.getInstance().init(context, battery, power, alarm,ActivityManagerService.self()); Slog.i(TAG, ”Window Manager“); wm = WindowManagerService.main(context, power,factoryTest!= SystemServer.FACTORY_TEST_LOW_LEVEL); ServiceManager.addService(Context.WINDOW_SERVICE, wm); ((ActivityManagerService)ServiceManager.getService(”activity“)).setWindowManager(wm); // Skip Bluetooth if we have an emulator kernel // TODO: Use a more reliable check to see if this product should // support Bluetooth-see bug 988521 if(SystemProperties.get(”ro.kernel.qemu“).equals(”1“)){ Slog.i(TAG, ”Registering null Bluetooth Service(emulator)“); ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, null); } else if(factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL){ Slog.i(TAG, ”Registering null Bluetooth Service(factory test)“); ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, null); } else { Slog.i(TAG, ”Bluetooth Service“); bluetooth = new BluetoothService(context); ServiceManager.addService(BluetoothAdapter.BLUETOOTH_SERVICE, bluetooth); bluetooth.initAfterRegistration(); bluetoothA2dp = new BluetoothA2dpService(context, bluetooth); ServiceManager.addService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE,bluetoothA2dp); int bluetoothOn = Settings.Secure.getInt(mContentResolver,Settings.Secure.BLUETOOTH_ON, 0); if(bluetoothOn > 0){ bluetooth.enable(); } } } catch(RuntimeException e){ Slog.e(”System“, ”Failure starting core service“, e); } DevicePolicyManagerService devicePolicy = null; StatusBarManagerService statusBar = null; InputMethodManagerService imm = null; AppWidgetService appWidget = null; NotificationManagerService notification = null; WallpaperManagerService wallpaper = null; LocationManagerService location = null; if(factoryTest!= SystemServer.FACTORY_TEST_LOW_LEVEL){ try { Slog.i(TAG, ”Device Policy“); devicePolicy = new DevicePolicyManagerService(context); ServiceManager.addService(Context.DEVICE_POLICY_SERVICE, devicePolicy); } catch(Throwable e){ Slog.e(TAG, ”Failure starting DevicePolicyService“, e); } try { Slog.i(TAG, ”Status Bar“); statusBar = new StatusBarManagerService(context); ServiceManager.addService(Context.STATUS_BAR_SERVICE, statusBar); } catch(Throwable e){ Slog.e(TAG, ”Failure starting StatusBarManagerService“, e); } try { Slog.i(TAG, ”Clipboard Service“); ServiceManager.addService(Context.CLIPBOARD_SERVICE,new ClipboardService(context)); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Clipboard Service“, e); } try { Slog.i(TAG, ”Input Method Service“); imm = new InputMethodManagerService(context, statusBar); ServiceManager.addService(Context.INPUT_METHOD_SERVICE, imm); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Input Manager Service“, e); } try { Slog.i(TAG, ”NetStat Service“); ServiceManager.addService(”netstat“, new NetStatService(context)); } catch(Throwable e){ Slog.e(TAG, ”Failure starting NetStat Service“, e); } try { Slog.i(TAG, ”NetworkManagement Service“); ServiceManager.addService(Context.NETWORKMANAGEMENT_SERVICE,NetworkManagementService.create(context)); } catch(Throwable e){ Slog.e(TAG, ”Failure starting NetworkManagement Service“, e); } try { Slog.i(TAG, ”Connectivity Service“); connectivity = ConnectivityService.getInstance(context); ServiceManager.addService(Context.CONNECTIVITY_SERVICE, connectivity); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Connectivity Service“, e); } try { Slog.i(TAG, ”Throttle Service“); throttle = new ThrottleService(context); ServiceManager.addService(Context.THROTTLE_SERVICE, throttle); } catch(Throwable e){ Slog.e(TAG, ”Failure starting ThrottleService“, e); } try { Slog.i(TAG, ”Accessibility Manager“); ServiceManager.addService(Context.ACCESSIBILITY_SERVICE,new AccessibilityManagerService(context)); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Accessibility Manager“, e); } try { /* * NotificationManagerService is dependant on MountService,*(for media / usb notifications)so we must start MountService first.*/ Slog.i(TAG, ”Mount Service“); ServiceManager.addService(”mount“, new MountService(context)); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Mount Service“, e); } try { Slog.i(TAG, ”Notification Manager“); notification = new NotificationManagerService(context, statusBar, lights); ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Notification Manager“, e); } try { Slog.i(TAG, ”Device Storage Monitor“); ServiceManager.addService(DeviceStorageMonitorService.SERVICE,new DeviceStorageMonitorService(context)); } catch(Throwable e){ Slog.e(TAG, ”Failure starting DeviceStorageMonitor service“, e); } try { Slog.i(TAG, ”Location Manager“); location = new LocationManagerService(context); ServiceManager.addService(Context.LOCATION_SERVICE, location); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Location Manager“, e); } try { Slog.i(TAG, ”Search Service“); ServiceManager.addService(Context.SEARCH_SERVICE,new SearchManagerService(context)); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Search Service“, e); } if(INCLUDE_DEMO){ Slog.i(TAG, ”Installing demo data...“); (new DemoThread(context)).start(); } try { Slog.i(TAG, ”DropBox Service“); ServiceManager.addService(Context.DROPBOX_SERVICE,new DropBoxManagerService(context, new File(”/data/system/dropbox“))); } catch(Throwable e){ Slog.e(TAG, ”Failure starting DropBoxManagerService“, e); } try { Slog.i(TAG, ”Wallpaper Service“); wallpaper = new WallpaperManagerService(context); ServiceManager.addService(Context.WALLPAPER_SERVICE, wallpaper); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Wallpaper Service“, e); } try { Slog.i(TAG, ”Audio Service“); ServiceManager.addService(Context.AUDIO_SERVICE, new AudioService(context)); } catch(Throwable e){ Slog.e(TAG, ”Failure starting Audio Service“, e); } try { Slog.i(TAG, ”Headset Observer“); // Listen for wired headset changes headset = new HeadsetObserver(context); } catch(Throwable e){ Slog.e(TAG, ”Failure starting HeadsetObserver“, e); } try { Slog.i(TAG, ”Dock Observer“); // Listen for dock station changes dock = new DockObserver(context, power); } catch(Throwable e){ Slog.e(TAG, ”Failure starting DockObserver“, e); } try { Slog.i(TAG, ”USB Service“); // Listen for USB changes usb = new UsbService(context); ServiceManager.addService(Context.USB_SERVICE, usb); } catch(Throwable e){ Slog.e(TAG, ”Failure starting UsbService“, e); } try { Slog.i(TAG, ”UI Mode Manager Service"); 三、非常完美電視節(jié)目分析 節(jié)目類型:綜藝類相親節(jié)目 節(jié)目時(shí)常:1h30min 目標(biāo)人群:適齡男女及其父母 一:片頭 15s AE 二:內(nèi)容 1、主持人出場(chǎng)、喊口號(hào)(接廣告)50s 現(xiàn)場(chǎng)畫面 2、主持人引入、嘉賓出場(chǎng) 1min50s 現(xiàn)場(chǎng)畫面 3、戀愛公開課 2min30s VCR短片 4、女嘉賓1 17min25s VCR自我介紹 現(xiàn)場(chǎng)畫面 人物出場(chǎng) 現(xiàn)場(chǎng)畫面 男嘉賓表態(tài) 現(xiàn)場(chǎng)畫面 摘面具 現(xiàn)場(chǎng)畫面 選擇心動(dòng)男生 現(xiàn)場(chǎng)畫面 與男嘉賓互動(dòng) 現(xiàn)場(chǎng)畫面+彈幕 男嘉賓進(jìn)行選擇 現(xiàn)場(chǎng)畫面 女生告白環(huán)節(jié) 現(xiàn)場(chǎng)畫面+VCR 情感專家點(diǎn)評(píng) 現(xiàn)場(chǎng)畫面 5、女嘉賓 2 略 VCR自我介紹 現(xiàn)場(chǎng)畫面 人物出場(chǎng) 現(xiàn)場(chǎng)畫面 男嘉賓表態(tài) 現(xiàn)場(chǎng)畫面 摘面具 現(xiàn)場(chǎng)畫面 女生告白環(huán)節(jié) 現(xiàn)場(chǎng)畫面+視頻 男嘉賓反問 現(xiàn)場(chǎng)畫面 四位嘉賓環(huán)節(jié)類似 6、結(jié)尾 節(jié)目總結(jié): 節(jié)目亮點(diǎn):往期節(jié)目的亮點(diǎn)在于男嘉賓 個(gè)人感受:此檔節(jié)目屬于綜藝類相親節(jié)目,節(jié)目制作的主要難點(diǎn)在于主持人對(duì)于現(xiàn)場(chǎng)的把控、以及情感專家對(duì)問題的點(diǎn)評(píng);在拍攝過程中,沒有太多復(fù)雜的鏡頭需要去拍,主要以固定鏡頭搖鏡頭為主。本期節(jié)目,編導(dǎo)對(duì)節(jié)目重心選擇有誤,主要的節(jié)目都在嘉賓方面,內(nèi)容拖沓,主持人缺乏引入,專家露面太少,告別部分過于狗血,強(qiáng)行煽淚。男嘉賓一直是那幾位,看過了很多期次節(jié)目之后,感覺已經(jīng)沒有可以吸引女性觀眾眼球的地方。 我個(gè)人覺得一節(jié)語文課,如果開頭導(dǎo)入得好,這節(jié)課也就成功了一半。這位老師做到了!以講故事的形式開始一節(jié)課,吸引學(xué)生的興趣,講到興頭上時(shí),老師突然打住,說:“想知道下面發(fā)生了什么嗎?好,打開課本,看課文?!敝圃炝藨夷?,讓學(xué)生想學(xué)課文,激發(fā)求知的欲望。并且這位老師在讓學(xué)生讀課文的時(shí)候也指出了朗讀要求,如:注意生字讀音,大聲讀課文,標(biāo)出自然段。 很喜歡這位老師語文課閱讀的方式,多樣有趣。有開火車式的,一人讀一段,分自然段讀;整體讀,或者互相讀,在學(xué)生讀的過程中,老師走下講臺(tái)進(jìn)行個(gè)別輔導(dǎo)。 在基礎(chǔ)知識(shí)解決完了之后,直接進(jìn)入課文的學(xué)習(xí)階段。由老師讀課文,幫助學(xué)生正字正音。然后提問學(xué)生,讀完之后知道什么,不明白什么。學(xué)生也很配合,大膽提出了很多稀奇古怪的問題,不過這也正表明孩子們天真的想象力,他們敢于說出自己內(nèi)心的想法,哪怕很簡單甚至很愚蠢,作為教師都應(yīng)該去鼓勵(lì),而不是批評(píng)。這位老師對(duì)于學(xué)生提出的問題都很肯定,并讓學(xué)生回到課文里去找尋答案,這種做法,可以讓學(xué)生通過讀課文,自己獨(dú)立解決先前提出的問題。之后老師運(yùn)用多媒體展示船行的過程以及劍掉入江里的動(dòng)態(tài)圖畫,幫助學(xué)生理解,以便于學(xué)生回答自己提出的諸多問題。 這位老師還注意課堂學(xué)生的交流合作,就能否撈劍這一話題讓學(xué)生進(jìn)行討論并闡述理由。老師走下去個(gè)別交流,了解學(xué)生的想法。在撈得著與撈不著之間展開討論分析,各圓其說,發(fā)散思維,只要說的有道理,老師都用一種肯定的眼神關(guān)注著這個(gè)學(xué)生。老師運(yùn)用紙片展示船行的過程,解釋為什么撈不到劍,也就順理成章。 最后,由課文聯(lián)系到自己的生活,學(xué)到了什么,揭示課文的主旨。這篇課文的學(xué)習(xí)也就暫告一段落了??傮w上感覺符合小學(xué)生的認(rèn)知結(jié)構(gòu),從趣味入手,讓學(xué)生快樂學(xué)習(xí)。 有一點(diǎn)我不太明白,為什么要讓學(xué)生把書反過來放在桌上,語文課,不是讓學(xué)生直接面對(duì)課文嗎?這樣做難道是為了讓學(xué)生對(duì)剛才的閱讀進(jìn)行回顧?這是一節(jié)新課,不是復(fù)習(xí)課??!第四篇:非常完美電視節(jié)目分析
第五篇:我個(gè)人覺得一節(jié)語文課