久久99精品久久久久久琪琪,久久人人爽人人爽人人片亞洲,熟妇人妻无码中文字幕,亚洲精品无码久久久久久久

struts2源代碼分析(個人覺得非常經典)

時間:2019-05-12 02:43:00下載本文作者:會員上傳
簡介:寫寫幫文庫小編為你整理了多篇相關的《struts2源代碼分析(個人覺得非常經典)》,但愿對你工作學習有幫助,當然你在寫寫幫文庫還可以找到更多《struts2源代碼分析(個人覺得非常經典)》。

第一篇:struts2源代碼分析(個人覺得非常經典)

本章講述Struts2的工作原理。

讀者如果曾經學習過Struts1.x或者有過Struts1.x的開發經驗,那么千萬不要想當然地以為這一章可以跳過。實際上Struts1.x與Struts2并無我們想象的血緣關系。雖然Struts2的開發小組極力保留Struts1.x的習慣,但因為Struts2的核心設計完全改變,從思想到設計到工作流程,都有了很大的不同。

Struts2是Struts社區和WebWork社區的共同成果,我們甚至可以說,Struts2是WebWork的升級版,他采用的正是WebWork的核心,所以,Struts2并不是一個不成熟的產品,相反,構建在WebWork基礎之上的Struts2是一個運行穩定、性能優異、設計成熟的WEB框架。

本章主要對Struts的源代碼進行分析,因為Struts2與WebWork的關系如此密不可分,因此,讀者需要下載xwork的源代碼,訪問http://文件,則通過過濾器鏈繼續往下傳送,直到到達請求的資源為止。

如果getMapping()方法返回有效的ActionMapping對象,則被認為正在請求某個Action,將調用Dispatcher.serviceAction(request, response, servletContext, mapping)方法,該方法是處理Action的關鍵所在。上述過程的源代碼如清單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());//得到存儲Action信息的ActionMapping對象

} catch(Exception ex){

……(省略部分代碼)

return;

}

if(mapping == null){//如果mapping為null,則認為不是請求Action資源

String resourcePath = RequestUtils.getServletPath(request);

if(“".equals(resourcePath)&& null!= request.getPathInfo()){

resourcePath = request.getPathInfo();

}

{

ngth());

//如果請求的資源以/struts開頭,則當作靜態資源處理

if(serveStatic && resourcePath.startsWith(”/struts“))

String name = resourcePath.substring(”/struts“.le

findStaticResource(name, request, response);} else {

//否則,過濾器鏈繼續往下傳遞

chain.doFilter(request, response);}

// The framework did its job here

return;

}

//如果請求的資源是Action,則調用serviceAction方法。

dispatcher.serviceAction(request, response, servletContext, mapping);

} finally {

try {

ActionContextCleanUp.cleanUp(req);

}

} finally {

UtilTimerStack.pop(timerKey);

} }

這段代碼的活動圖如圖18所示:

(圖18)

在Dispatcher.serviceAction()方法中,先加載Struts2的配置文件,如果沒有人為配置,則默認加載struts-default.xml、struts-plugin.xml和struts.xml,并且將配置信息保存在形如com.opensymphony.xwork2.config.entities.XxxxConfig的類中。

類com.opensymphony.xwork2.config.providers.XmlConfigurationProvider負責配置文件的讀取和解析,addAction()方法負責讀取標簽,并將數據保存在ActionConfig中;addResultTypes()方法負責將標簽轉化為ResultTypeConfig對象;loadInterceptors()方法負責將標簽轉化為InterceptorConfig對象;loadInterceptorStack()方法負責將標簽轉化為InterceptorStackConfig對象;loadInterceptorStacks()方法負責將標簽轉化成InterceptorStackConfig對象。而上面的方法最終會被addPackage()方法調用,將所讀取到的數據匯集到PackageConfig對象中,細節請參考代碼清單16。

代碼清單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;

}

活動圖如圖19所示:

(圖19)

配置信息加載完成后,創建一個Action的代理對象——ActionProxy引用,實際上對Action的調用正是通過ActionProxy實現的,而ActionProxy又由ActionProxyFactory創建,ActionProxyFactory是創建ActionProxy的工廠。

注:ActionProxy和ActionProxyFactory都是接口,他們的默認實現類分別是DefaultActionProxy和DefaultActionProxyFactory,位于com.opensymphony.xwork2包下。

在這里,我們絕對有必要介紹一下com.opensymphony.xwork2.DefaultActionInvocation類,該類是對ActionInvocation接口的默認實現,負責Action和截攔器的執行。

在DefaultActionInvocation類中,定義了invoke()方法,該方法實現了截攔器的遞歸調用和執行Action的execute()方法。其中,遞歸調用截攔器的代碼如清單17所示:

代碼清單17:調用截攔器,DefaultActionInvocation.invoke()方法的部分代碼

if(interceptors.hasNext()){

//從截攔器集合中取出當前的截攔器

final InterceptorMapping interceptor =(InterceptorMapping)interceptors.next();

UtilTimerStack.profile(”interceptor: “+interceptor.getName(),new UtilTimerStack.ProfilingBlock(){

public String doProfiling()throws Exception {

//執行截攔器(Interceptor)接口中定義的intercept方法

resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);

returnnull;

}

});

}

從代碼中似乎看不到截攔器的遞歸調用,其實是否遞歸完全取決于程序員對程序的控制,先來看一下Interceptor接口的定義:

代碼清單18:Interceptor.java publicinterface Interceptor extends Serializable {

void destroy();

void init();

String intercept(ActionInvocation invocation)throws Exception;}

所有的截攔器必須實現intercept方法,而該方法的參數恰恰又是ActionInvocation,所以,如果在intercept方法中調用invocation.invoke(),代碼清單17會再次執行,從Action的Intercepor列表中找到下一個截攔器,依此遞歸。下面是一個自定義截攔器示例:

代碼清單19:CustomIntercepter.java publicclass CustomIntercepter extends AbstractInterceptor {

@Override

public String intercept(ActionInvocation actionInvocation)throws Exception

} { actionInvocation.invoke();return”李贊紅“;}

截攔器的調用活動圖如圖20所示:

(圖20)

如果截攔器全部執行完畢,則調用invokeActionOnly()方法執行Action,invokeActionOnly()方法基本沒做什么工作,只調用了invokeAction()方法。

為了執行Action,必須先創建該對象,該工作在DefaultActionInvocation的構造方法中調用init()方法早早完成。調用過程是:DefaultActionInvocation()->init()->createAction()。創建Action的代碼如下:

代碼清單20:DefaultActionInvocation.createAction()方法

protectedvoid createAction(Map contextMap){

try {

action = objectFactory.buildAction(proxy.getActionName(), proxy.getNamespace(), proxy.getConfig(), contextMap);

} catch(InstantiationException e){

……異常代碼省略

}

}

Action創建好后,輪到invokeAction()大顯身手了,該方法比較長,但關鍵語句實在很少,用心點看不會很難。

代碼清單20:DefaultActionInvocation.invokeAction()方法

protected String invokeAction(Object action, ActionConfig actionConfig)throws Exception {

//獲取Action中定義的execute()方法名稱,實際上該方法是可以隨便定義的

String methodName = proxy.getMethod();

String timerKey = ”invokeAction: “+proxy.getActionName();

try {

UtilTimerStack.push(timerKey);

Method method;

try {

//將方法名轉化成Method對象

method = getAction().getClass().getMethod(methodName, new Class[0]);

} catch(NoSuchMethodException e){

// hmm--OK, try doXxx instead

try {

//如果Method出錯,則嘗試在方法名前加do,再轉成Method對象

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;

}

}

//執行方法

[0]);

}

Object methodResult = method.invoke(action, new Object

//處理跳轉

if(methodResult instanceof Result){

this.result =(Result)methodResult;

returnnull;

} else {

return(String)methodResult;

} } catch(NoSuchMethodException e){

……省略異常代碼 } finally {

UtilTimerStack.pop(timerKey);}

剛才使用了一段插述,我們繼續回到ActionProxy類。

我們說Action的調用是通過ActionProxy實現的,其實就是調用了ActionProxy.execute()方法,而該方法又調用了ActionInvocation.invoke()方法。歸根到底,最后調用的是DefaultActionInvocation.invokeAction()方法。

以下是調用關系圖:

其中:

?

ActionProxy:管理Action的生命周期,它是設置和執行Action的起始點。

?

ActionInvocation:在ActionProxy層之下,它表示了Action的執行狀態。它持有Action實例和所有的Interceptor

以下是serviceAction()方法的定義:

代碼清單21:Dispatcher.serviceAction()方法

publicvoid serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,ActionMapping mapping)throws ServletException {

Map extraContext = createContextMap(request, response, mapping, context);

// 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完成頁面的跳轉。

3.4 本小節總結

總體來講,Struts2的工作機制比Struts1.x要復雜很多,但我們不得不佩服Struts和WebWork開發小組的功底,代碼如此優雅,甚至能夠感受看到兩個開發小組心神相通的默契。兩個字:佩服。

以下是Struts2運行時調用方法的順序圖:

(圖21)

四、總結

閱讀源代碼是一件非常辛苦的事,對讀者本身的要求也很高,一方面要有扎實的功底,另一方面要有超強的耐力和恒心。本章目的就是希望能幫助讀者理清一條思路,在必要的地方作出簡單的解釋,達到事半功倍的效果。

當然,筆者不可能為讀者解釋所有類,這也不是我的初衷。Struts2+xwork一共有700余類,除了為讀者做到現在的這些,已無法再做更多的事情。讀者可以到Struts官方網站下載幫助文檔,慢慢閱讀和理解,相信會受益頗豐。

本章并不適合java語言初學者或者對java博大精深的思想理解不深的讀者閱讀,這其中涉及到太多的術語和類的使用,特別不要去鉆牛角尖,容易使自信心受損。基本搞清楚Struts2的使用之后,再回過頭來閱讀本章,對一些知識點和思想也許會有更深的體會。

如果讀者的java功底比較渾厚,而且對Struts2充滿興趣,但又沒太多時間研究,不妨仔細閱讀本章,再對照Struts的源代碼,希望對您有所幫助。

第二篇:struts2代碼分析

1.Struts2架構圖和請求處理流程

請求首先通過Filter chain,Filter主要包括ActionContextCleanUp,它主要清理當前線程的ActionContext和Dispatcher;FilterDispatcher主要通過AcionMapper來決定需要調用哪個Action。

ActionMapper取得了ActionMapping后,在Dispatcher的serviceAction方法里創建ActionProxy,ActionProxy創建ActionInvocation,然后ActionInvocation調用Interceptors,執行Action本身,創建Result并返回,當然,如果要在返回之前做些什么,可以實現PreResultListener。

2.Struts2部分類介紹

這部分從Struts2參考文檔中翻譯就可以了。

ActionMapper

ActionMapper其實是HttpServletRequest和Action調用請求的一個映射,它屏蔽了Action對于Request等java Servlet類的依賴。Struts2中它的默認實現類是DefaultActionMapper,ActionMapper很大的用處可以根據自己的需要來設計url格式,它自己也有Restful的實現,具體可以參考文檔的docs¥actionmapper.html。

ActionProxy&ActionInvocation

Action的一個代理,由ActionProxyFactory創建,它本身不包括Action實例,默認實現DefaultActionProxy是由ActionInvocation持有Action實例。ActionProxy作用是如何取得Action,無論是本地還是遠程。而ActionInvocation的作用是如何執行Action,攔截器的功能就是在ActionInvocation中實現的。

ConfigurationProvider&Configuration

ConfigurationProvider就是Struts2中配置文件的解析器,Struts2中的配置文件主要是尤其實現類XmlConfigurationProvider及其子類StrutsXmlConfigurationProvider來解析。

3.Struts2請求流程

1、客戶端發送請求

2、請求先通過ActionContextCleanUp-->FilterDispatcher

3、FilterDispatcher通過ActionMapper來決定這個Request需要調用哪個Action

4、如果ActionMapper決定調用某個Action,FilterDispatcher把請求的處理交給ActionProxy,這兒已經轉到它的Delegate--Dispatcher來執行

5、ActionProxy根據ActionMapping和ConfigurationManager找到需要調用的Action類

6、ActionProxy創建一個ActionInvocation的實例

7、ActionInvocation調用真正的Action,當然這涉及到相關攔截器的調用

8、Action執行完畢,ActionInvocation創建Result并返回,當然,如果要在返回之前做些什么,可以實現PreResultListener。添加PreResultListener可以在Interceptor中實現。

首先強調一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個線程都有自己獨立的實例變量,互不相干.接下來就從Dispatcher開始看起,先看其構造函數:

//創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方

public Dispatcher(ServletContext servletContext, Map initParams){

this.servletContext = servletContext;

//配置在web.xml中的param參數

this.initParams = initParams;

}

//創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方

public Dispatcher(ServletContext servletContext, Map initParams){

this.servletContext = servletContext;

//配置在web.xml中的param參數

this.initParams = initParams;

}

我們再看在FilterDispatcher創建Dispatcher的:

protected Dispatcher createDispatcher(FilterConfig filterConfig){

Map params = new HashMap();

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

for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){

String name =(String)e.nextElement();

String value = filterConfig.getInitParameter(name);

params.put(name, value);

}

都可以從FilterConfig中得到

return new Dispatcher(filterConfig.getServletContext(), params);

}

分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實例添加到ConfigurationManager的List里面.最后通過循環調用List里的這些destroy(),register()等方法實現對配置文件的屬性進行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()

創建Dispatcher之后,來看init()方法

init()方法是用來Load用戶配置文件,資源文件以及默認的配置文件.主要分七步走,看下面注釋

public void init(){

if(configurationManager == null){

//設置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內容,Framework可以是xwork,struts,webwork等

configurationManager

=

new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);

}

//讀取properties信息,默認的default.properties,init_DefaultProperties();// [1]

//讀取xml配置文件

init_TraditionalXmlConfigurations();// [2]

//讀取用戶自定義的struts.properties

init_LegacyStrutsProperties();// [3]

//自定義的configProviders

init_CustomConfigurationProviders();// [5]

//載入FilterDispatcher傳進來的initParams

init_FilterInitParameters();// [6]

//將配置文件中的bean與具體的類映射

init_AliasStandardObjects();// [7]

//構建一個用于依賴注射的Container對象

//在這里面會循環調用上面七個ConfigurationProvider的register方法

//其中的重點就是DefaultConfiguration的#reload()方法

Container container = init_PreloadConfiguration();

container.inject(this);

init_CheckConfigurationReloading(container);

init_CheckWebLogicWorkaround(container);

if(!dispatcherListeners.isEmpty()){

for(DispatcherListener l : dispatcherListeners){

l.dispatcherInitialized(this);

}

}

}

public void init(){

if(configurationManager == null){

//設置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內容,Framework可以是xwork,struts,webwork等

configurationManager

=

new ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);

}

//讀取properties信息,默認的default.properties,init_DefaultProperties();// [1]

//讀取xml配置文件

init_TraditionalXmlConfigurations();// [2]

//讀取用戶自定義的struts.properties

init_LegacyStrutsProperties();// [3]

//自定義的configProviders

init_CustomConfigurationProviders();// [5]

//載入FilterDispatcher傳進來的initParams

init_FilterInitParameters();// [6]

//將配置文件中的bean與具體的類映射

init_AliasStandardObjects();// [7]

//構建一個用于依賴注射的Container對象

//在這里面會循環調用上面七個ConfigurationProvider的register方法

//其中的重點就是DefaultConfiguration的#reload()方法

Container container = init_PreloadConfiguration();

container.inject(this);

init_CheckConfigurationReloading(container);

init_CheckWebLogicWorkaround(container);

if(!dispatcherListeners.isEmpty()){

for(DispatcherListener l : dispatcherListeners){

l.dispatcherInitialized(this);

}

}

}

分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實例添加到ConfigurationManager的List里面.最后通過循環調用List里的這些destroy(),register()等方法實現對配置文件的屬性進行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()

private void init_DefaultProperties(){

configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());

}

接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實際上只是實現了register()方法

public void register(ContainerBuilder builder, LocatableProperties props)

throws ConfigurationException {

Settings defaultSettings = null;

try {

defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);

} catch(Exception e){

throw

}

loadSettings(props, defaultSettings);

}

private void init_DefaultProperties(){

configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());

}

接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實際上只是實現了register()方法

public void register(ContainerBuilder builder, LocatableProperties props)

throws ConfigurationException {

Settings defaultSettings = null;

try {

defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);

} catch(Exception e){

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構造方法

//讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在classpath里的struts.properties里覆寫

public PropertiesSettings(String name){

URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());

if(settingsUrl == null){

LOG.debug(name + “.properties missing”);

settings = new LocatableProperties();

return;

}

settings

// 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

//這個props是register的一個入參.protected void loadSettings(LocatableProperties props, final Settings settings){

// We are calling the impl methods to get around the single instance of Settings that is expected

for(Iterator i = settings.listImpl();i.hasNext();){

String name =(String)i.next();

props.setProperty(name, settings.getLocationImpl(name));

}

}

//PropertiesSettings構造方法

//讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在classpath里的struts.properties里覆寫

public PropertiesSettings(String name){

URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());

if(settingsUrl == null){

LOG.debug(name + “.properties missing”);

settings = new LocatableProperties();

return;

}

settings =

new

LocatableProperties(new

LocationImpl(null, settingsUrl.toString()));

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

//這個props是register的一個入參.protected void loadSettings(LocatableProperties props, final Settings settings){

// We are calling the impl methods to get around the single instance of Settings that is expected

for(Iterator i = settings.listImpl();i.hasNext();){

String name =(String)i.next();

props.setProperty(name, settings.getLocationImpl(name));

}

}

再來看第二步:init_TraditionalXmlConfigurations()

private void init_TraditionalXmlConfigurations(){

settings.getImpl(name), //首先讀取web.xml中的config初始參數值

//如果

使

認的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認的配置文件必須取名為這三個名稱了

//如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可

String configPaths = initParams.get(“config”);

if(configPaths == null){

configPaths = DEFAULT_CONFIGURATION_PATHS;

}

String[] files = configPaths.split(“¥¥s*[,]¥¥s*”);

for(String file : files){

if(file.endsWith(“.xml”)){

if(“xwork.xml”.equals(file)){

//XmlConfigurationProvider負責解析xwork.xml

configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));

} else {

//其它xml都是由StrutsXmlConfigurationProvider來解析

configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));

}

} else {

throw new IllegalArgumentException(“Invalid configuration file name”);

}

}

}

private void init_TraditionalXmlConfigurations(){

//首先讀取web.xml中的config初始參數值

//如果

使

認的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認的配置文件必須取名為這三個名稱了

//如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可

String configPaths = initParams.get(“config”);

if(configPaths == null){

configPaths = DEFAULT_CONFIGURATION_PATHS;

}

String[] files = configPaths.split(“¥¥s*[,]¥¥s*”);

for(String file : files){

if(file.endsWith(“.xml”)){

if(“xwork.xml”.equals(file)){

//XmlConfigurationProvider負責解析xwork.xml

configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));

} else {

//其它xml都是由StrutsXmlConfigurationProvider來解析

configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));

}

} else {

throw new IllegalArgumentException(“Invalid configuration file name”);

}

}

}

對于其它配置文件只用接口。

類XmlConfigurationProvider負責配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的

public static Document parse(InputSource inputSource, Map dtdMappings)將configFileName配置文件通過SAX解析方式按照DtdMappings解析成Document對象.然后通過Provider的register()方法加載“bean”和“constant”屬性,再通過loadPackages()加載package及package中的屬性

addAction()方法負責讀取標簽,并將數據保存在ActionConfig中; addResultTypes()方法負責將標簽轉化為ResultTypeConfig對象; loadInterceptors()方法負責將標簽轉化為InterceptorConfi對象;

loadInterceptorStack()方法負責將標簽轉化為InterceptorStackConfig對象;

StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實現ConfigurationProviderloadInterceptorStacks()方法負責將標簽轉化成InterceptorStackConfig對象。

而上面的方法最終會被addPackage()方法調用,addPackage又會被Provider的loadPackages()調用,將所讀取到的數據匯集到PackageConfig對象中。

protected PackageConfig

addPackage(Element

packageElement)

throws ConfigurationException {

PackageConfig.Builder newPackage = buildPackageContext(packageElement);

if(newPackage.isNeedsRefresh()){

return newPackage.build();

}

// add result types(and default result)to this package

addResultTypes(newPackage, packageElement);

// load the interceptors and interceptor stacks for this package

loadInterceptors(newPackage, packageElement);

// load the default interceptor reference for this package

loadDefaultInterceptorRef(newPackage, packageElement);

// load the default class ref for this package

loadDefaultClassRef(newPackage, packageElement);

// load the global result list for this package

loadGlobalResults(newPackage, packageElement);

// load the global exception handler list for this package

loadGobalExceptionMappings(newPackage, packageElement);

// get actions

NodeList actionList = packageElement.getElementsByTagName(“action”);

for(int i = 0;i < actionList.getLength();i++){

Element actionElement =(Element)actionList.item(i);

addAction(actionElement, newPackage);

}

// load the default action reference for this package

loadDefaultActionRef(newPackage, packageElement);

PackageConfig cfg = newPackage.build();

configuration.addPackageConfig(cfg.getName(), cfg);

return cfg;

}

loadConfigurationFiles解析讀取xml中的內容

private List

loadConfigurationFiles(String

fileName,Element includeElement){

...//通過DomHelper調用SAX進行解析xml

doc = DomHelper.parse(in, dtdMappings);

...Element rootElement = doc.getDocumentElement();

NodeList children = rootElement.getChildNodes();

int childSize = children.getLength();

for(int i = 0;i < childSize;i++){

Node childNode = children.item(i);

if(childNode instanceof Element){

Element child =(Element)childNode;

final String nodeName = child.getNodeName();

if(“include”.equals(nodeName)){

String includeFileName = child.getAttribute(“file”);

//解析每個action配置是,對于include文件可以使用通配符*來進行配置

//如Struts.xml中可配置成

if(includeFileName.indexOf('*')!=-1){

ClassPathFinder wildcardFinder = new ClassPathFinder();

wildcardFinder.setPattern(includeFileName);

Vector wildcardMatches = wildcardFinder.findMatches();

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;

}

首先強調一下struts2的線程程安全,在Struts2中大量采用ThreadLocal線程局部變量的方法來保證線程的安全,像Dispatcher等都是通過ThreadLocal來保存變量值,使得每個線程都有自己獨立的實例變量,互不相干.接下來就從Dispatcher開始看起,先看其構造函數:

//創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方

public Dispatcher(ServletContext servletContext, Map initParams){

this.servletContext = servletContext;

//配置在web.xml中的param參數

this.initParams = initParams;

}

//創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方

public Dispatcher(ServletContext servletContext, Map initParams){

this.servletContext = servletContext;

//配置在web.xml中的param參數

this.initParams = initParams;

}

我們再看在FilterDispatcher創建Dispatcher的:

protected Dispatcher createDispatcher(FilterConfig filterConfig){

Map params = new HashMap();

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

for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){

String name =(String)e.nextElement();

String value = filterConfig.getInitParameter(name);

params.put(name, value);

}

都可以從FilterConfig中得到

return new Dispatcher(filterConfig.getServletContext(), params);

}

分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實例添加到ConfigurationManager的List里面.最后通過循環調用List里的這些destroy(),register()等方法實現對配置文件的屬性進行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()

創建Dispatcher之后,來看init()方法

init()方法是用來Load用戶配置文件,資源文件以及默認的配置文件.主要分七步走,看下面注釋

public void init(){

if(configurationManager == null){

//設置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內容,Framework可以是xwork,struts,webwork等

configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);

}

//讀取properties信息,默認的default.properties,init_DefaultProperties();// [1]

//讀取xml配置文件

init_TraditionalXmlConfigurations();// [2]

//讀取用戶自定義的struts.properties

init_LegacyStrutsProperties();// [3]

//自定義的configProviders

init_CustomConfigurationProviders();// [5]

//載入FilterDispatcher傳進來的initParams

init_FilterInitParameters();// [6]

//將配置文件中的bean與具體的類映射

init_AliasStandardObjects();// [7]

//構建一個用于依賴注射的Container對象

//在這里面會循環調用上面七個ConfigurationProvider的register方法

//其中的重點就是DefaultConfiguration的#reload()方法

Container container = init_PreloadConfiguration();

container.inject(this);

init_CheckConfigurationReloading(container);

init_CheckWebLogicWorkaround(container);

if(!dispatcherListeners.isEmpty()){

for(DispatcherListener l : dispatcherListeners){

l.dispatcherInitialized(this);

}

}

new

}

public void init(){

if(configurationManager == null){

//設置ConfigurationManager的defaultFrameworkBeanName.//這里DEFAULT_BEAN_NAME為struts,這是xwork框架的內容,Framework可以是xwork,struts,webwork等

configurationManager = ConfigurationManager(BeanSelectionProvider.DEFAULT_BEAN_NAME);

}

//讀取properties信息,默認的default.properties,init_DefaultProperties();// [1]

//讀取xml配置文件

init_TraditionalXmlConfigurations();// [2]

//讀取用戶自定義的struts.properties

init_LegacyStrutsProperties();// [3]

//自定義的configProviders

init_CustomConfigurationProviders();// [5]

//載入FilterDispatcher傳進來的initParams

init_FilterInitParameters();// [6]

//將配置文件中的bean與具體的類映射

init_AliasStandardObjects();// [7]

//構建一個用于依賴注射的Container對象

//在這里面會循環調用上面七個ConfigurationProvider的register方法

//其中的重點就是DefaultConfiguration的#reload()方法

Container container = init_PreloadConfiguration();

container.inject(this);

init_CheckConfigurationReloading(container);

init_CheckWebLogicWorkaround(container);

if(!dispatcherListeners.isEmpty()){

for(DispatcherListener l : dispatcherListeners){

l.dispatcherInitialized(this);

}

}

new

}

分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供init(),destroy(),register()等方法.將各種ConfigurationProvider初始化之后將實例添加到ConfigurationManager的List里面.最后通過循環調用List里的這些destroy(),register()等方法實現對配置文件的屬性進行注冊和銷毀等功能.下面將分析這七層功夫是怎樣一步步練成的.首先是init_DefaultProperties()

private void init_DefaultProperties(){

configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());

}

接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實際上只是實現了register()方法

public void register(ContainerBuilder builder, LocatableProperties props)

throws ConfigurationException {

Settings defaultSettings = null;

try {

defaultSettings = new PropertiesSettings(“org/apache/struts2/default”);

} catch(Exception e){

throw

}

loadSettings(props, defaultSettings);

}

private void init_DefaultProperties(){

configurationManager.addConfigurationProvider(new DefaultPropertiesProvider());

}

接來看DefaultPropertiesProvider好了,DefaultPropertiesProvider實際上只是實現了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構造方法

//讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在classpath里的struts.properties里覆寫

public PropertiesSettings(String name){

URL settingsUrl = ClassLoaderUtils.getResource(name + “.properties”, getClass());

if(settingsUrl == null){

LOG.debug(name + “.properties missing”);

settings = new LocatableProperties();

return;

}

settings

// 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

//這個props是register的一個入參.protected void loadSettings(LocatableProperties props, final Settings settings){

// We are calling the impl methods to get around the single instance of Settings that is expected

for(Iterator i = settings.listImpl();i.hasNext();){

String name =(String)i.next();

props.setProperty(name, settings.getLocationImpl(name));

}

}

//PropertiesSettings構造方法

//讀取org/apache/struts2/default.properties的配置信息,如果項目中需要覆蓋,可以在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

//這個props是register的一個入參.protected void loadSettings(LocatableProperties props, final Settings settings){

// We are calling the impl methods to get around the single instance of Settings that is expected

for(Iterator i = settings.listImpl();i.hasNext();){

String name =(String)i.next();

=

new

LocatableProperties(new

LocationImpl(null, settingsUrl.toString()));

props.setProperty(name, settings.getLocationImpl(name));

}

}

再來看第二步:init_TraditionalXmlConfigurations()

private void init_TraditionalXmlConfigurations(){

//首先讀取web.xml中的config初始參數值

//如果

使

settings.getImpl(name),用默認的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認的配置文件必須取名為這三個名稱了

//如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可

String configPaths = initParams.get(“config”);

if(configPaths == null){

configPaths = DEFAULT_CONFIGURATION_PATHS;

}

String[] files = configPaths.split(“¥¥s*[,]¥¥s*”);

for(String file : files){

if(file.endsWith(“.xml”)){

if(“xwork.xml”.equals(file)){

//XmlConfigurationProvider負責解析xwork.xml

configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));

} else {

//其它xml都是由StrutsXmlConfigurationProvider來解析

configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));

}

} else {

throw new IllegalArgumentException(“Invalid configuration file name”);

}

}

}

private void init_TraditionalXmlConfigurations(){

//首先讀取web.xml中的config初始參數值

//如果

使

認的DEFAULT_CONFIGURATION_PATHS:“struts-default.xml,struts-plugin.xml,struts.xml”,//這兒就可以看出為什么默認的配置文件必須取名為這三個名稱了

//如果不想使用默認的名稱,直接在web.xml中配置config初始參數即可

String configPaths = initParams.get(“config”);

if(configPaths == null){

configPaths = DEFAULT_CONFIGURATION_PATHS;

}

String[] files = configPaths.split(“¥¥s*[,]¥¥s*”);

for(String file : files){

if(file.endsWith(“.xml”)){

if(“xwork.xml”.equals(file)){

//XmlConfigurationProvider負責解析xwork.xml

configurationManager.addConfigurationProvider(new XmlConfigurationProvider(file, false));

} else {

//其它xml都是由StrutsXmlConfigurationProvider來解析

configurationManager.addConfigurationProvider(new StrutsXmlConfigurationProvider(file, false, servletContext));

}

} else {

throw new IllegalArgumentException(“Invalid configuration file name”);

}

}

}

對于其它配置文件只用接口。

類XmlConfigurationProvider負責配置文件的讀取和解析,首先通過init()中的loadDocuments(configFileName);利用DomHelper中的

public static Document parse(InputSource inputSource, Map dtdMappings)將configFileName配置文件通過SAX解析方式按照DtdMappings解析成StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實現ConfigurationProviderDocument對象.然后通過Provider的register()方法加載“bean”和“constant”屬性,再通過loadPackages()加載package及package中的屬性

addAction()方法負責讀取標簽,并將數據保存在ActionConfig中; addResultTypes()方法負責將標簽轉化為ResultTypeConfig對象; loadInterceptors()方法負責將標簽轉化為InterceptorConfi對象;

loadInterceptorStack()方法負責將標簽轉化為InterceptorStackConfig對象;

loadInterceptorStacks()方法負責將標簽轉化成InterceptorStackConfig對象。

而上面的方法最終會被addPackage()方法調用,addPackage又會被Provider的loadPackages()調用,將所讀取到的數據匯集到PackageConfig對象中。

protected PackageConfig

addPackage(Element

packageElement)

throws ConfigurationException {

PackageConfig.Builder newPackage = buildPackageContext(packageElement);

if(newPackage.isNeedsRefresh()){

return newPackage.build();

}

// add result types(and default result)to this package

addResultTypes(newPackage, packageElement);

// load the interceptors and interceptor stacks for this package

loadInterceptors(newPackage, packageElement);

// load the default interceptor reference for this package

loadDefaultInterceptorRef(newPackage, packageElement);

// load the default class ref for this package

loadDefaultClassRef(newPackage, packageElement);

// load the global result list for this package

loadGlobalResults(newPackage, packageElement);

// load the global exception handler list for this package

loadGobalExceptionMappings(newPackage, packageElement);

// get actions

NodeList actionList = packageElement.getElementsByTagName(“action”);

for(int i = 0;i < actionList.getLength();i++){

Element actionElement =(Element)actionList.item(i);

addAction(actionElement, newPackage);

}

// load the default action reference for this package

loadDefaultActionRef(newPackage, packageElement);

PackageConfig cfg = newPackage.build();

configuration.addPackageConfig(cfg.getName(), cfg);

return cfg;

}

loadConfigurationFiles解析讀取xml中的內容

private List

loadConfigurationFiles(String

fileName, includeElement){

...//通過DomHelper調用SAX進行解析xml

doc = DomHelper.parse(in, dtdMappings);

...Element rootElement = doc.getDocumentElement();

NodeList children = rootElement.getChildNodes();

int childSize = children.getLength();

for(int i = 0;i < childSize;i++){

Node childNode = children.item(i);

if(childNode instanceof Element){

Element child =(Element)childNode;

final String nodeName = child.getNodeName();

if(“include”.equals(nodeName)){

String includeFileName = child.getAttribute(“file”);

//解析每個action配置是,對于include文件可以使用通配符*來進行配置

//如Struts.xml中可配置成

if(includeFileName.indexOf('*')!=-1){

ClassPathFinder wildcardFinder = new ClassPathFinder();

wildcardFinder.setPattern(includeFileName);

Element

Vector wildcardMatches = wildcardFinder.findMatches();

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()調用的是調用的是LegacyPropertiesConfigurationProvider 通過比較前

DefaultPropertiesProvider

調

用的是LegacyPropertiesConfigurationProvider.發現DefaultPropertiesProvider繼承自后者,但重寫了register()方法,主要是生成PropertiesSetting的不同,前者是根據org/apache/struts2/default.properties 后者是根據struts.properties 我們展開register()中的Settings.getInstance(),最后是調用getDefaultInstance()

private static Settings getDefaultInstance(){

if(defaultImpl == null){

// Create bootstrap implementation

//不帶參數的DefaultSettings(),區別與DefaultPropertiesProvider中直接帶default.properties參數

//不帶參數就是默認為struts.propertes,并且加載struts.custom.properties所定義的properties文件

defaultImpl = new DefaultSettings();

// Create default implementation

try {

//STRUTS_CONFIGURATION為:struts.configuration

//在struts.proterties中查找struts.configuration的值,這個值必須是org.apache.struts2.config.Configuration接口的實現類

//所以我有個困惑就是在下面的轉換當中怎么將Configuration轉換成Setting類型的...//這一點先放下了,有時間再研究

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

//不帶參數的DefaultSettings(),區別與DefaultPropertiesProvider中直接帶default.properties參數

//不帶參數就是默認為struts.propertes,并且加載struts.custom.properties所定義的properties文件

defaultImpl = new DefaultSettings();

// Create default implementation

try {

//STRUTS_CONFIGURATION為:struts.configuration

//在struts.proterties中查找struts.configuration的值,這個值必須是

Could

not

instantiate

the struts.configuration object, substituting the default implementation.”, e);

=

(Settings)ObjectFactory.getObjectFactory().buildBean(Thread.currentThread().getContextClassLorg.apache.struts2.config.Configuration接口的實現類

//所以我有個困惑就是在下面的轉換當中怎么將Configuration轉換成Setting類型的...//這一點先放下了,有時間再研究

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);//在這里實現滴~

}

});

}

//從這里可以看出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);//在這里實現滴~

}

});

}

第七步:init_AliasStandardObjects,使用BeanSelectionProvider 這是將配置文件中定義的與實際的類相映射,就是注入bean的依賴關系,這部分以后有時候再研究Container

接下來是看怎樣調用這些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 {

//重點就是這個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 {

//重點就是這個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 providers)throws ConfigurationException {

packageContexts.clear();

loadedFileNames.clear();

List

packageProviders = new ArrayList

();

//Struts2(xwork2)用Container來完成依賴注入的功能

//首先初始化一個ContainerBuilder,再由builder來保存接口與實現類或工廠類的對應關系

//然后通過builder.create(boolean)方法產生container

//由container.getInstance(Class);就可以得到接口的實現實例了

//這一部分比較復雜,后面研究完成了,會單獨拿出來講,這里先弄清楚Xwork依賴注入的實現步驟就可以了

ContainerProperties props = new ContainerProperties();

ContainerBuilder builder = new ContainerBuilder();

for(final ContainerProvider containerProvider : providers)

{

//循環調用ConfigurationProvider的init和register方法,明白了吧,在這里統一循環調用

containerProvider.init(this);

containerProvider.register(builder, props);

}

props.setConstants(builder);

//注入依賴關系,在這里并不產生實例

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已經注入依賴關系的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);

//調用PackageProvider的loadPackages()方法,這里主要是針對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已經在之前講過,這就好辦了。FilterDispatcher是Struts2的核心控制器,首先看一下init()方法。

public void init(FilterConfig filterConfig)throws ServletException {

try {

this.filterConfig = filterConfig;

initLogging();

//創建dispatcher,前面都已經講過啰

dispatcher = createDispatcher(filterConfig);

dispatcher.init();

//注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader

dispatcher.getContainer().inject(this);

//StaticContentLoader在BeanSelectionProvider中已經被注入了依賴關系: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();

//創建dispatcher,前面都已經講過啰

dispatcher = createDispatcher(filterConfig);

dispatcher.init();

//注入將FilterDispatcher中的變量通過container注入,如下面的staticResourceLoader

dispatcher.getContainer().inject(this);

//StaticContentLoader在BeanSelectionProvider中已經被注入了依賴關系:DefaultStaticContentLoader

//可以在struts-default.xml中的可以找到

staticResourceLoader.setHostConfig(new FilterHostConfig(filterConfig));

} finally {

ActionContext.setContext(null);

}

}

//下面來看DefaultStaticContentLoader的setHostConfig

public void setHostConfig(HostConfig filterConfig){

//讀取初始參數

pakages,調用

parse(),解析成類似/org/apache/struts2/static,/template的數組

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){

//讀取初始參數

pakages,調用

parse(),解析成類似/org/apache/struts2/static,/template的數組

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);

}

現在回去doFilter的方法,每當有一個Request,都會調用這些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所注入的實現類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進行包裝

//MultiPartRequestWrapper

StrutsRequestWrapper的子類,兩者都是HttpServletRequest實現

//此時在MultiPartRequestWrapper中就會把Files給解析出來,用于文件上傳

//所有request都會StrutsRequestWrapper進行包裝,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 {

//根據url取得對應的Action的配置信息

//看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲在 ActionMapping對象中

mapping

=

actionMapper.getMapping(request, dispatcher.getConfigurationManager());

} catch(Exception ex){

log.error(“error getting ActionMapping”, ex);

dispatcher.sendError(request,return;

}

//如果找不到對應的action配置,則直接返回。比如你輸入***.jsp等等

//這兒有個例外,就是如果path是以“/struts”開頭,則到初始參數packages配置的包路徑去查找對應的靜態資源并輸出到頁面流中,當然.class文件除外。如果再沒有則跳轉到

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所注入的實現類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進行包裝

//MultiPartRequestWrapperHttpServletRequest實現

//此時在MultiPartRequestWrapper中就會把Files給解析出來,用于文件上傳

//所有request都會StrutsRequestWrapper進行包裝,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的子類,兩者都是

//根據url取得對應的Action的配置信息

//看一下注入的DefaultActionMapper的getMapping()方法.Action的配置信息存儲在 ActionMapping對象中

mapping

} catch(Exception ex){

log.error(”error getting ActionMapping“, ex);

dispatcher.sendError(request,return;

}

//如果找不到對應的action配置,則直接返回。比如你輸入***.jsp等等

//這兒有個例外,就是如果path是以“/struts”開頭,則到初始參數packages配置的包路徑去查找對應的靜態資源并輸出到頁面流中,當然.class文件除外。如果再沒有則跳轉到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接口的實現類 DefaultActionMapper的getMapping()方法的源代碼:

public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){

ActionMapping mapping = new ActionMapping();

String uri = getUri(request);//得到請求路徑的URI,如:testAtcion.action或testAction.do

int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時找不到而且的bug

uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri;

uri = dropExtension(uri, mapping);//刪除擴展名,默認擴展名為action

if(uri == null){

return null;

}

parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace

handleSpecialParameters(request, mapping);//去掉重復參數

//如果Action的name沒有解析出來,直接返回

if(mapping.getName()== null){

returnnull;

}

//下面處理形如testAction!method格式的請求路徑

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接口的實現類 DefaultActionMapper的getMapping()方法的源代碼:

public ActionMapping getMapping(HttpServletRequest request,ConfigurationManager configManager){

ActionMapping mapping = new ActionMapping();

String uri = getUri(request);//得到請求路徑的URI,如:testAtcion.action或testAction.do

int indexOfSemicolon = uri.indexOf(”;“);//修正url的帶;jsessionid 時找不到而且的bug

uri =(indexOfSemicolon >-1)? uri.substring(0, indexOfSemicolon): uri;

uri = dropExtension(uri, mapping);//刪除擴展名,默認擴展名為action

if(uri == null){

return null;

}

parseNameAndNamespace(uri, mapping, configManager);//匹配Action的name和namespace

handleSpecialParameters(request, mapping);//去掉重復參數

//如果Action的name沒有解析出來,直接返回

if(mapping.getName()== null){

returnnull;

}

//下面處理形如testAction!method格式的請求路徑

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類型的對象,該對象包含三個參數:Action的name、namespace和要調用的方法method。

如果getMapping()方法返回ActionMapping對象為null,則FilterDispatcher認為用戶請求不是Action,自然另當別論,FilterDispatcher會做一件非常有意思的事:如果請求以/struts開頭,會自動查找在web.xml文件中配置的 packages初始化參數,就像下面這樣:

struts2

org.apache.struts2.dispatcher.FilterDispatcher

packages

com.lizanhong.action

struts2

org.apache.struts2.dispatcher.FilterDispatcher

packages

com.lizanhong.action

FilterDispatcher會將com.lizanhong.action包下的文件當作靜態資源處理,即直接在頁面上顯示文件內容,不過會忽略擴展名為class的文件。比如在com.lizanhong.action包下有一個aaa.txt的文本文件,其內容為“中華人民共和國”,訪問

http://localhost:8081/Struts2Demo/struts/aaa.txt時會輸出txt中的內容

FilterDispatcher.findStaticResource()方法

protectedvoid findStaticResource(String

name,HttpServletRequest

request, HttpServletResponse response)throws IOException {

if(!name.endsWith(”.class“)){//忽略class文件

//遍歷packages參數

for(String pathPrefix : pathPrefixes){

InputStream is = findInputStream(name, pathPrefix);//讀取請求文件流

if(is!= null){

...// set the content-type header

String contentType = getContentType(name);//讀取內容類型

if(contentType!= null){

response.setContentType(contentType);//重新設置內容類型

}

...try {

//將讀取到的文件流以每次復制4096個字節的方式循環輸出

copy(is, response.getOutputStream());

} finally {

is.close();

}

return;

}

}

}

}

protectedvoid findStaticResource(String

name,HttpServletRequest

request, HttpServletResponse response)throws IOException {

if(!name.endsWith(”.class“)){//忽略class文件

//遍歷packages參數

for(String pathPrefix : pathPrefixes){

InputStream is = findInputStream(name, pathPrefix);//讀取請求文件流

if(is!= null){

...// set the content-type header

String contentType = getContentType(name);//讀取內容類型

if(contentType!= null){

response.setContentType(contentType);//重新設置內容類型

}

...try {

//將讀取到的文件流以每次復制4096個字節的方式循環輸出

copy(is, response.getOutputStream());

} finally {

is.close();

}

return;

}

}

}

}

如果用戶請求的資源不是以/struts開頭——可能是.jsp文件,也可能是.html文件,則通過過濾器鏈繼續往下傳送,直到到達請求的資源為止。

如果getMapping()方法返回有效的ActionMapping對象,則被認為正在請求某個Action,將調用 Dispatcher.serviceAction(request, response, servletContext, mapping)方法,該方法是處理Action的關鍵所在。

下面就來看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 extraContext = createContextMap(request, response, mapping, context);

// 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();

//創建一個Action的代理對象,ActionProxyFactory是創建ActionProxy的工廠

//參考實現類: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,則直接轉向,關于Result,ActionProxy,ActionInvocation下一講中再分析

if(mapping.getResult()!= null){

Result result = mapping.getResult();

result.execute(proxy.getInvocation());

} else {

//執行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了.在這之前最好先去了解一下動態Proxy的基本知識.ActionProxy是Action的一個代理類,也就是說Action的調用是通過ActionProxy實現的,其實就是調用了ActionProxy.execute()方法,而該方法又調用了ActionInvocation.invoke()方法。歸根到底,最后調用的是DefaultActionInvocation.invokeAction()方法。DefaultActionInvocation()->init()->createAction()。

最后

調

用ActionProxy.exute()-->ActionInvocation.invoke()-->Intercepter.intercept()-->ActionInvocation.invokeActionOnly()-->invokeAction()這里的步驟是先由ActionProxyFactory創建ActionInvocation和ActionProxy.public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext){

ActionInvocation inv = new DefaultActionInvocation(extraContext, true);

container.inject(inv);

return }

public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map extraContext, boolean executeResult, boolean cleanupContext){

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 contextMap = createContextMap();

// 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);

}

//創建Action,struts2中每一個Request都會創建一個新的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(proxy.getConfig().getInterceptors());

interceptors = interceptorList.iterator();

createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext);

}

protected void createAction(Map contextMap){

// load action

String timerKey = ”actionCreate: “ + proxy.getActionName();

try {

UtilTimerStack.push(timerKey);

//默認為SpringObjectFactory:struts.objectFactory=spring.這里非常巧妙,在struts.properties中可以重寫這個屬性

//在前面BeanSelectionProvider中通過配置文件為ObjectFactory設置實現類

//這里以Spring為例,這里會調到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 extraContext, boolean injectInternal)throws Exception {

Object o = null;

try {

//SpringObjectFactory

web.xml

中的

第三篇:Android系統啟動源代碼調查分析

Android系統啟動調查。

目的:Android程序入口在哪里?Mainifest配置文件如何加載實例化?從系統層到應用層如何使用?

目標從系統角度來了解Android啟動過程,通過下載源代碼并且根據源代碼從底層開始跟蹤,跟著方法走一遍Android啟動過程。了解Zygote進程是什么?

開機一開始:Linux啟動這一層,主要包括了兩塊:BootLoader(嵌入式系統的引導程序)和Kernel(Linux內核層,驅動層)第二塊:Android系統啟動。

我們都知道,Linux系統啟動,定義了一個Init.rc這個系統啟動的配置文件(放在System/bin文件下面)。

Init.rc啟動的時候,最開始啟動了SystemManager守護進程,它的源代碼是一個Java文件,守護進程是一個與界面無關,會持續運行在后臺,用來接收響應,并且維持系統運營的。

在啟動servicemanager的同時,再來啟動Zygote,Zygote實際上啟動的是:app_main.cpp的系統文件.這個文件的main()方法,會調用Android_Runtime.cpp的文件中的start()方法,這個方法通過JNI機制,來調用ZygoteInit.java孵化器初始文件,這個文件的Main()函數,將會去調用所有進程。

這個ZygoteInit文件的main()函數,這個函數通過JNI機制調用了FrameWrok中的SystemServer文件,這個文件有三個函數:main(),init1()和init2()方法。

Init1()方法會通過JNI機制再去調用com_Android__server_SystemService.java的原生態文件,去實現系統初始化的操作,(調用System_init.cpp)。

當系統初始化工作做完之后,系統反過來會調用SystemServer文件下面的init2()方法,會通過runtime方法調用ServerThread進程去調用激活其他的所有進程。

第三塊:應用程序啟動(下次再講)。

使用工具:【代碼分析工具】source Insight 【源代碼】 Android 源代碼包

操作步驟:

在下載好Android SDK 安裝包之后(如果沒有下載好請移步這里)

【配置代碼分析工具】

打開source Insight 軟件,來配置Android源代碼。

“項目”→“新建項目”

在“新項目名”填寫:“Android 14”(Android 第14個版本,代表Android V4.0.3)在“項目文件儲存位置”填寫:SDK源代碼包的位置

繼續進行配置,點擊確定。

選中右邊的所有文件夾,點擊“添加所有”按鈕,將這個版本的源代碼全部導入。

應用級別:選中將所有的子集目錄,下級子目錄中的所有文件都導入查找項目。

進行檢索。。。

一共找到了“213720”個文件,是否導入?選中“Yes”

導入文件,索引建立

這時候,查看正下方,項目文件(213720)已經全部導入,項目準備完畢。可以進行調查了。

這時候你看到的右邊工具欄,就是我們可以用來方便查找的搜索欄,輸入對應的關鍵字即可。

切入正題,查找Android系統啟動文件

【查找Init文件】啟動方法會初始化MainiFest.xml配置文件,配置文件再去調用里面的配置,但是啟動方法何時啟動的調查,還未找到源頭,只知道一切事物的源頭,從這里開始。

原代碼如下:

service console /system/bin/sh(啟動Linux內核)

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(啟動服務管理進程)

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,現在先調查(ServerManager)這個啟動進程。

在system/core/libsysutils/src 目錄下(系統級啟動進程)

(啟動孵化器進程)

在左側點擊start方法

這就是守護進程中的源代碼之一,start()方法 ServiceManager::ServiceManager(){ } int ServiceManager::start(const char *name){ //如果進程已經啟動,那么打印日志:“XX進程已經啟動”

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;}

再來看同時啟動的app_main的源代碼,我們去查看一下它的main函數

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”);

//設置了一個進程名叫zygote的進程,通過runtime來啟動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;

} } 調查一下runtime的類。AppRuntime,這就是android系統的運行時類,它啟動了zygote孵化器進程,用來孵化Davlik虛擬機的。

runtime.start(“com.android.internal.os.ZygoteInit”,startSystemServer);所涉及到的ZygoteInit文件。

找到ZygoteInit文件(FrameWork里面的一個java類)。先去看看Main函數。

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”)){ //如果輸入參數為真,我們就啟動系統服務

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){ //如果孵化器一直是交叉模式,就啟動運行交叉模式函數;否則就選擇另一個循環模式

runForkMode();

} else {

runSelectLoopMode();

}

closeServerSocket();

} catch(MethodAndArgsCaller caller){

caller.run();

} catch(RuntimeException ex){

Log.e(TAG, “Zygote died with exception”, ex);

closeServerSocket();

throw ex;

}

}

我們繼續查看,如果參數為真的情況下,ZygoteInit文件中的,startSystemServer()函數的源代碼。

/**

* 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”,//這個虛擬機的名字叫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;

}

我們繼續去查看 system Server的源代碼

main函數:

/**

* 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()函數卻是個空函數

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()函數中,會調用到 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();

}

因為通過調查發現,SystemServer文件的main()函數調用的init1()函數,是一個空方法,native public static void init1(String[] args);

但是根據JNI調用機制,我們可以在同名文件夾(framework/base/services/)下找到JNL目錄,然后找到和系統相關的com_android_server_SystemServer.java文件

使用“C”的動態鏈接嗲用system_init 的方法。它去回調Init2的方法

我們繼續看看SystemServer方法的Init2()方法是看什么用的。

去調查一下ServerThread()方法是干什么用的?這個內部類ServerThread就是啟動,并且實例化每一個系統進程的線程類

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");

第四篇:非常完美電視節目分析

三、非常完美電視節目分析

節目類型:綜藝類相親節目 節目時常:1h30min 目標人群:適齡男女及其父母

一:片頭 15s AE 二:內容

1、主持人出場、喊口號(接廣告)50s 現場畫面

2、主持人引入、嘉賓出場 1min50s 現場畫面

3、戀愛公開課 2min30s VCR短片

4、女嘉賓1 17min25s VCR自我介紹 現場畫面 人物出場 現場畫面 男嘉賓表態 現場畫面 摘面具 現場畫面 選擇心動男生 現場畫面 與男嘉賓互動 現場畫面+彈幕 男嘉賓進行選擇 現場畫面 女生告白環節 現場畫面+VCR 情感專家點評 現場畫面

5、女嘉賓 2 略

VCR自我介紹 現場畫面 人物出場 現場畫面 男嘉賓表態 現場畫面 摘面具 現場畫面 女生告白環節 現場畫面+視頻 男嘉賓反問 現場畫面 四位嘉賓環節類似

6、結尾

節目總結:

節目亮點:往期節目的亮點在于男嘉賓

個人感受:此檔節目屬于綜藝類相親節目,節目制作的主要難點在于主持人對于現場的把控、以及情感專家對問題的點評;在拍攝過程中,沒有太多復雜的鏡頭需要去拍,主要以固定鏡頭搖鏡頭為主。本期節目,編導對節目重心選擇有誤,主要的節目都在嘉賓方面,內容拖沓,主持人缺乏引入,專家露面太少,告別部分過于狗血,強行煽淚。男嘉賓一直是那幾位,看過了很多期次節目之后,感覺已經沒有可以吸引女性觀眾眼球的地方。

第五篇:我個人覺得一節語文課

我個人覺得一節語文課,如果開頭導入得好,這節課也就成功了一半。這位老師做到了!以講故事的形式開始一節課,吸引學生的興趣,講到興頭上時,老師突然打住,說:“想知道下面發生了什么嗎?好,打開課本,看課文。”制造了懸念,讓學生想學課文,激發求知的欲望。并且這位老師在讓學生讀課文的時候也指出了朗讀要求,如:注意生字讀音,大聲讀課文,標出自然段。

很喜歡這位老師語文課閱讀的方式,多樣有趣。有開火車式的,一人讀一段,分自然段讀;整體讀,或者互相讀,在學生讀的過程中,老師走下講臺進行個別輔導。

在基礎知識解決完了之后,直接進入課文的學習階段。由老師讀課文,幫助學生正字正音。然后提問學生,讀完之后知道什么,不明白什么。學生也很配合,大膽提出了很多稀奇古怪的問題,不過這也正表明孩子們天真的想象力,他們敢于說出自己內心的想法,哪怕很簡單甚至很愚蠢,作為教師都應該去鼓勵,而不是批評。這位老師對于學生提出的問題都很肯定,并讓學生回到課文里去找尋答案,這種做法,可以讓學生通過讀課文,自己獨立解決先前提出的問題。之后老師運用多媒體展示船行的過程以及劍掉入江里的動態圖畫,幫助學生理解,以便于學生回答自己提出的諸多問題。

這位老師還注意課堂學生的交流合作,就能否撈劍這一話題讓學生進行討論并闡述理由。老師走下去個別交流,了解學生的想法。在撈得著與撈不著之間展開討論分析,各圓其說,發散思維,只要說的有道理,老師都用一種肯定的眼神關注著這個學生。老師運用紙片展示船行的過程,解釋為什么撈不到劍,也就順理成章。

最后,由課文聯系到自己的生活,學到了什么,揭示課文的主旨。這篇課文的學習也就暫告一段落了。總體上感覺符合小學生的認知結構,從趣味入手,讓學生快樂學習。

有一點我不太明白,為什么要讓學生把書反過來放在桌上,語文課,不是讓學生直接面對課文嗎?這樣做難道是為了讓學生對剛才的閱讀進行回顧?這是一節新課,不是復習課啊!

下載struts2源代碼分析(個人覺得非常經典)word格式文檔
下載struts2源代碼分析(個人覺得非常經典).doc
將本文檔下載到自己電腦,方便修改和收藏,請勿使用迅雷等下載。
點此處下載文檔

文檔為doc格式


聲明:本文內容由互聯網用戶自發貢獻自行上傳,本網站不擁有所有權,未作人工編輯處理,也不承擔相關法律責任。如果您發現有涉嫌版權的內容,歡迎發送郵件至:645879355@qq.com 進行舉報,并提供相關證據,工作人員會在5個工作日內聯系你,一經查實,本站將立刻刪除涉嫌侵權內容。

相關范文推薦

    個人通訊錄管理系統,java源代碼

    package cn.pab.manager; import java.util.List; import java.util.Scanner; import cn.pab.dao.PersonDao; import cn.pab.dao.TypeDao; import cn.pab.dao.UserDao; im......

    個人述職報告模板(非常實用)

    個人述職報告模板(非常實用)述職是管理干部依據自己的職務要求,就一定時期內的任期目標,向主管部門及本企業的全體工友,匯報自己履行崗位責任情況,是干部管理考核的一種形式。個......

    個人優缺點評價(非常實用)

    個人優缺點評價(非常實用) 1. 優點:樂于助人,善于團結,吃苦耐勞,平易近人。 缺點:工作作風過于拘謹不夠大膽 2.優點:尊敬師長,團結同學,樂于助人,是老師的好幫手,同學的好朋友, 學習勤奮,積......

    個人覺得比較好的英文句子

    英語名句 1. ...And so on and forth and so fifth! 真是沒完沒了!2.Age before beauty! 長者優先。3.Old habits die hard! 積習難改。4.Never say die. 永不言敗。5.Hang i......

    C語言面試題大匯總,個人覺得還是比較全

    4. static有什么用途?(請至少說明兩種) 1.限制變量的作用域 2.設置變量的存儲域 7. 引用與指針有什么區別? 1) 引用必須被初始化,指針不必。 2) 引用初始化以后不能被改變,指針可......

    就業指導ppt整理(個人覺得相對比較重要的)(推薦)

    就業指導ppt整理 派遣的有關規定: (1)畢業生的報到期限一般為一個月。 (2)畢業生上半月報到的,發給全月工資;下半月報到的,發給半月工資。 (3)三年以上固定期限和無固定期限勞動合同的......

    銀行個人述職報告(非常不錯)

    尊敬的各位領導、同事們,大家好! 2010年是我從事客戶工作的第一年。這一年櫛風沐雨、耕耘收獲;這一年忙碌充實、緊張有序;這一年學習思考、感觸良多。 感觸之一:德行品質得到了鍛......

    非常實用的個人述職報告供借鑒

    5篇非常實用的個人述職報告供借鑒尊敬的領導:今年,在各級領導的正確指引下,我始終恪盡職守、團結同事,堅決服從各項管理方針、政策,想方設法推進工作進步。以下是本人的述職報告:......

主站蜘蛛池模板: 久久久国产成人一区二区| 欧美巨大巨粗黑人性aaaaaa| 97久久久人妻一区精品| 国产精品成人99一区无码| 日韩国产亚洲欧美中国v| 无码国产69精品久久久久同性| 丰满人妻被两个按摩师| 亚洲国产一区二区精品无码| 亚洲最大av无码国产| 97超碰人人爱香蕉精品| 久久99精品这里精品6| 亚洲无码免费在线观看| 精品三级片| 免费无码av片在线观看| 色一情一乱一伦一区二区三欧美| 亚洲欧美不卡视频在线播放| 狼群社区www中文视频| 大地资源在线观看免费下载| 亚洲国产一区二区三区波多野结衣| 欧美性生交xxxxx无码久久久| 欧洲熟妇色xxxx欧美老妇免费| 国产成人一区二区精品视频| 日本一区二区三区免费高清| 狠狠cao日日穞夜夜穞av| 久久国产精品99精品国产987| 久久久噜噜噜久久| 国产在线视频福利资源站| 国产精品美女久久久久久久久| 精品亚洲麻豆1区2区3区| 级r片内射在线视频播放| 中文久久乱码一区二区| 亚洲乱码日产精品m| 国模吧双双大尺度炮交gogo| 国产精品久久久久久久久软件| 成人国产精品一区二区免费看| 日韩精品一区二区亚洲av| 国内最真实的xxxx人伦| 亚洲成成熟女人专区| 久久久久久国产精品无码下载| 天天澡夜夜澡人人澡| 亚洲日韩精品a∨片无码|