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

struts2代碼分析

時(shí)間:2019-05-12 02:51:45下載本文作者:會(huì)員上傳
簡介:寫寫幫文庫小編為你整理了多篇相關(guān)的《struts2代碼分析》,但愿對(duì)你工作學(xué)習(xí)有幫助,當(dāng)然你在寫寫幫文庫還可以找到更多《struts2代碼分析》。

第一篇: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 initParams){

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

this.servletContext = servletContext;

//配置在web.xml中的param參數(shù)

this.initParams = initParams;

}

我們?cè)倏丛贔ilterDispatcher創(chuàng)建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接口進(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 dtdMappings)將configFileName配置文件通過SAX解析方式按照DtdMappings解析成Document對(duì)象.然后通過Provider的register()方法加載“bean”和“constant”屬性,再通過loadPackages()加載package及package中的屬性

addAction()方法負(fù)責(zé)讀取標(biāo)簽,并將數(shù)據(jù)保存在ActionConfig中; addResultTypes()方法負(fù)責(zé)將標(biāo)簽轉(zhuǎn)化為ResultTypeConfig對(duì)象; loadInterceptors()方法負(fù)責(zé)將標(biāo)簽轉(zhuǎn)化為InterceptorConfi對(duì)象;

loadInterceptorStack()方法負(fù)責(zé)將標(biāo)簽轉(zhuǎn)化為InterceptorStackConfig對(duì)象;

StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實(shí)現(xiàn)ConfigurationProviderloadInterceptorStacks()方法負(fù)責(zé)將標(biāo)簽轉(zhuǎn)化成InterceptorStackConfig對(duì)象。

而上面的方法最終會(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 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;

}

首先強(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 initParams){

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

this.servletContext = servletContext;

//配置在web.xml中的param參數(shù)

this.initParams = initParams;

}

我們?cè)倏丛贔ilterDispatcher創(chuàng)建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接口進(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 dtdMappings)將configFileName配置文件通過SAX解析方式按照DtdMappings解析成StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實(shí)現(xiàn)ConfigurationProviderDocument對(duì)象.然后通過Provider的register()方法加載“bean”和“constant”屬性,再通過loadPackages()加載package及package中的屬性

addAction()方法負(fù)責(zé)讀取標(biāo)簽,并將數(shù)據(jù)保存在ActionConfig中; addResultTypes()方法負(fù)責(zé)將標(biāo)簽轉(zhuǎn)化為ResultTypeConfig對(duì)象; loadInterceptors()方法負(fù)責(zé)將標(biāo)簽轉(zhuǎn)化為InterceptorConfi對(duì)象;

loadInterceptorStack()方法負(fù)責(zé)將標(biāo)簽轉(zhuǎn)化為InterceptorStackConfig對(duì)象;

loadInterceptorStacks()方法負(fù)責(zé)將標(biāo)簽轉(zhuǎn)化成InterceptorStackConfig對(duì)象。

而上面的方法最終會(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 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()調(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 這是將配置文件中定義的與實(shí)際的類相映射,就是注入bean的依賴關(guān)系,這部分以后有時(shí)候再研究Container

接下來是看怎樣調(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 providers)throws ConfigurationException {

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ù),就像下面這樣:

struts2

org.apache.struts2.dispatcher.FilterDispatcher

packages

com.lizanhong.action

struts2

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

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

}

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

//默認(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 extraContext, boolean injectInternal)throws Exception {

Object o = null;

try {

//SpringObjectFactory

會(huì)

web.xml

中的

第二篇:Struts2介紹

Struts2集成指南

關(guān)于Struts2 Struts是Apache軟件基金會(huì)(ASF)贊助的一個(gè)開源項(xiàng)目。它最初是Jakarta項(xiàng)目中的一個(gè)子項(xiàng)目,并在2004年3月成為ASF的頂級(jí)項(xiàng)目。它通過采用Java Servlet/JSP技術(shù),實(shí)現(xiàn)了基于Java EE Web應(yīng)用的Model-View-Controller〔MVC〕設(shè)計(jì)模式的應(yīng)用框架〔Web Framework〕,是MVC經(jīng)典設(shè)計(jì)模式中的一個(gè)經(jīng)典產(chǎn)品。

Struts,a history 在Java EE的Web應(yīng)用發(fā)展的初期,除了使用Servlet技術(shù)以外,普遍是在JavaServer Pages(JSP)的源代碼中,采用HTML與Java代碼混合的方式進(jìn)行開發(fā)。因?yàn)檫@兩種方式不可避免的要把表現(xiàn)與業(yè)務(wù)邏輯代碼混合在一起,都給前期開發(fā)與后期維護(hù)帶來巨大的復(fù)雜度。為了擺脫上述的約束與局限,把業(yè)務(wù)邏輯代碼從表現(xiàn)層中清晰的分離出來,2000年,Craig McClanahan采用了MVC的設(shè)計(jì)模式開發(fā)Struts。后來該框架產(chǎn)品一度被認(rèn)為是最廣泛、最流行JAVA的WEB應(yīng)用框架。

Craig McClanahan 2006年,WebWork與Struts這兩個(gè)優(yōu)秀的Java EE Web框架(Web Framework〕的團(tuán)體,決定合作共同開發(fā)一個(gè)新的,整合了WebWork與Struts優(yōu)點(diǎn),并且更加優(yōu)雅、擴(kuò)展性更強(qiáng)的框架,命名為“Struts 2”,原Struts的1.x版本產(chǎn)品稱為“Struts 1”。

至此,Struts項(xiàng)目并行提供與維護(hù)兩個(gè)主要版本的框架產(chǎn)品——Struts 1與Struts 2。Struts1 vs.Struts2 侵入性

Struts 1 在編程方面是面向抽象類編程,而不是面向接口編程。Struts 1要求自定義Action 類繼承一個(gè)特定的抽象基類Action。另一方面,Struts 1的 Action 依賴于 Servlet API,因?yàn)镾truts 1 Action 的execute 方法中有HttpServletRequest 和HttpServletResponse 方法。例如 e.g.public class LogonAction extends Action {

public ActionForward execute(ActionMapping mapping,ActionForm form,HttpServletRequest request,HttpServletResponse response){

} }

Struts 2 Action 類可以實(shí)現(xiàn)一個(gè)Action接口,也可以實(shí)現(xiàn)其他接口,甚至不實(shí)現(xiàn)任何接口。這使得可選的和定制的服務(wù)成為可能。e.g.public class ExampleAction {

public String doSomething(){

return “success”;} }

線程模式

Struts 1 Action類 是單例模式并且必須是線程安全的,因?yàn)樵趙eb容器中,僅有Action類 的一個(gè)實(shí)例來處理所有的請(qǐng)求。

Struts2 Web容器為每一個(gè)請(qǐng)求產(chǎn)生一個(gè)Action類實(shí)例,因此沒有線程安全問題。可測試性

Struts1 由于對(duì)Servlet API的依賴,使得針對(duì)于自定義Action類的測試變得復(fù)雜。

Struts2 由于自定義Action可以為POJO,所以可以向測試一個(gè)POJO一樣來測試Action類。

請(qǐng)求參數(shù)封裝

Struts1 使用ActionForm 對(duì)象封裝用戶的請(qǐng)求參數(shù),所有的 ActionForm 必須繼承一個(gè)基類:ActionForm。普通的JavaBean 不能用作ActionForm,并且需要在配置文件中定義ActionForm。e.g.public class LogonForm extends ActionForm {

private String userpassword;

private String username;}

Struts2 直接使用Action屬性來封裝用戶請(qǐng)求屬性,避免了開發(fā)者需要大量開發(fā)ActionForm類的煩瑣,實(shí)際上,這些屬性還可以是包含子屬性的 Rich對(duì)象類型。e.g.public class ExampleAction {

private String responseMessage;private String requestMessage;

public String getResponseMessage(){

return responseMessage;}

public void setResponseMessage(String responseMessage){

this.responseMessage = responseMessage;}

public String getRequestMessage(){

return requestMessage;

} } public void setRequestMessage(String requestMessage){ } this.requestMessage = requestMessage;public String doSomething(){

} setMessage(“Hi, ” + getRequestMessage());return “success”;EL Struts1 整合了 JSTL,因此可以使用JSTL 表達(dá)式語言。JSTL有基本對(duì)象圖遍歷,但在對(duì)集合和索引屬性的支持上則功能不強(qiáng)。在向視圖綁定值時(shí),Struts1 使用標(biāo)準(zhǔn)JSP 機(jī)制把對(duì)象綁定到視圖頁面。

Struts2 Struts 2 可以使用JSTL,但它整合了一種更強(qiáng)大和靈活的表達(dá)式語言:OGNL(Object Graph Notation Language),因此,Struts 2 下的表達(dá)式語言功能更加強(qiáng)大。在向視圖綁定值時(shí),Struts2 使用“ValueStack ”技術(shù),使標(biāo)簽庫能夠訪問值,而不需要把對(duì)象和視圖頁面綁定在一起。

校驗(yàn)框架

Struts1 Struts1 支持在 ActionForm 重寫validate 方法中手動(dòng)校驗(yàn),或者通過整合 Commons-validator 框架來完成數(shù)據(jù)校驗(yàn)。

Struts2 Struts 2 支持通過重寫validate方法進(jìn)行校驗(yàn),也支持整合XWork 校驗(yàn)框架進(jìn)行校驗(yàn)。Struts2 architacture

名詞約定

凡是代碼、配置文件、IDE中出現(xiàn)的名詞,均采用英文原稱。

Roadmap 本文檔的目標(biāo)是,幫助讀者在Eclipse中將Struts2集成至一個(gè)嶄新的Dynamic Web Project。集成步驟大致如下:配置Struts2的代碼環(huán)境-> 在web.xml中加入Struts2功能-> 測試Struts2。文檔目標(biāo)達(dá)成的標(biāo)志是:頁面請(qǐng)求能夠通過Struts2的Action Mapping成功轉(zhuǎn)發(fā),并且基于Java的Struts2驗(yàn)證框架能夠生效。

集成步驟

引入Struts2相關(guān)的jar文件

Struts2 jars ? ? ? ? ? ? ? ? ? ? commons-fileupload-1.2.2.jar commons-io-2.1.jar commons-lang-2.4.jar commons-logging-1.1.1.jar freemarker-2.3.16.jar javassist.jar jstl-1.2.jar ognl-3.0.1.jar struts2-core-2.2.3.jar xwork-core-2.2.3.jar 加入build path 將以上10個(gè)jar文件,拷貝至WebContent/WEB-INF/lib下:

對(duì)于Web Dynamic Project,一般情況下,當(dāng)你向lib目錄下copy了jar文件,eclipse會(huì)自動(dòng)將jar文件加入build path下的名為Web App Libraries的Library。請(qǐng)確認(rèn)在工程下出現(xiàn)了名為Web App Libraries的Library。如果有,說明這些jar文件已經(jīng)被添加至build path了:

如果在工程目錄下找不到名為Web App Libraries的Library,說明jar文件沒有被添加至build path,需要進(jìn)行手動(dòng)添加。首先進(jìn)入build path設(shè)置界面,選中Libraries 頁,并點(diǎn)擊Add JARs:

在JAR Selection窗口中,選中l(wèi)ib下所有的jar文件。選中后點(diǎn)擊OK:

你將看到被選中的jar文件已經(jīng)被添加至build path:

在工程中會(huì)出現(xiàn)一個(gè)名為Referenced Libraries的Libraries。這說明jar文件已經(jīng)被添加至build path:

以上兩種方法都可以將jar文件添加至build path,它們的效果是一樣的。

配置web.xml 添加filter 在web.xml中添加一個(gè)filter:

struts2

org.apache.struts2.dispatcher.FilterDispatcher

filter-name表示filter的名字,你可以任意決定這個(gè)名字。filter-class表示使用哪個(gè)類作為filter,從這個(gè)類的全稱來判斷,可以發(fā)現(xiàn)FilterDispatcher是Struts2提供的一個(gè)類。它是Struts2轉(zhuǎn)發(fā)請(qǐng)求的起點(diǎn)。在web.xml中添加一個(gè)filter-mapping:

struts2

/*

filter-mapping用來映射url和filter的映射關(guān)系。filter-name表示filter的名字,這個(gè)名字必須和之前filter聲明中的filter-name一致。url-pattern表示哪些格式的url會(huì)被此filter濾中。/*表示在此web應(yīng)用域名下,所有的地址都會(huì)被filter濾中,換言之,所有的http請(qǐng)求都會(huì)通過Struts2進(jìn)行轉(zhuǎn)發(fā)。

filter的作用

通過以上的配置,F(xiàn)ilterDispatcher和url與filter-name聯(lián)系在了一起。

由于在web容器中注冊(cè)了FilterDispatcher這個(gè)filter,Struts2可以收到所有http://localhost:8080/tyland-b2b 的http請(qǐng)求。隨后,F(xiàn)ilterDispatcher會(huì)根據(jù)我們定義的action-mapping規(guī)則,將請(qǐng)求分發(fā)到指定的action類以及它的攔截器棧。最后,Struts2按照action-mapping規(guī)則,將后臺(tái)計(jì)算的結(jié)果返回給指定頁面?;\統(tǒng)地來說,Struts2就是這樣工作的,所以說,F(xiàn)ilterDispatcher是Struts2工作的入口。

編寫代碼,測試Struts2 Struts2的環(huán)境已經(jīng)配置好了,基于action-mapping的轉(zhuǎn)發(fā)機(jī)制已經(jīng)可以運(yùn)行了。為了證明這一點(diǎn),請(qǐng)編寫一些測試jsp頁面和java代碼。

在編寫代碼的過程中,請(qǐng)確保代碼文件的位置如下圖所示:

代碼清單如下:

Java代碼

UserVO.java package com.tyland.b2b.vo;

// 一個(gè)Value Object(Data Model),用來存放用戶名、密碼 public class UserVO {

private String username;private String password;

// 成員變量password的getter方法。

// 在Strtus2中,用來在頁面和服務(wù)器間傳值的Value Object必須有g(shù)etter方法

public String getPassword(){

return password;

} } // 成員變量password的setter方法。

// 在Strtus2中,用來在頁面和服務(wù)器間傳值的Value Object必須有setter方法 public void setPassword(String password){ } this.password = password;// 同password

public String getUsername(){ } return username;// 同password

public void setUsername(String username){ } this.username = username;BaseAction.java package com.tyland.b2b.web.base;

import com.opensymphony.xwork2.ActionSupport;

// 為了代碼的靈活性和可擴(kuò)展性,請(qǐng)聲明一個(gè)BaseAction基類

// BaseAction繼承Struts2的ActionSupport,因?yàn)槲覀兿胧褂肧truts2的一些額外幫助。// 對(duì)于ActionSupport的繼承不是必須的

public class BaseAction extends ActionSupport {

private static final long serialVersionUID = ***74952195L;} UserAction.java package com.tyland.b2b.web;

import com.tyland.b2b.vo.UserVO;import com.tyland.b2b.web.base.BaseAction;

// 自定義的Action類,繼承BaseAction // 由于繼承了ActionSupport,我們可以使用Struts2默認(rèn)的action方法execute()// 由于繼承了ActionSupport,我們可以使用Struts2默認(rèn)的校驗(yàn)方法validate()public class UserAction extends BaseAction {

private static final long serialVersionUID =-7***3684190L;

// 用來在頁面和服務(wù)器之間傳遞用戶名、密碼的Value Object。變量名任意。

private UserVO userVO;

執(zhí)行。// 用來在頁面和服務(wù)器之間傳遞message變量。名稱任意。private String message;//用來在頁面和服務(wù)器之間傳遞sayHiTo變量。名稱任意。private String sayHiTo;// 用來傳值的變量必須有g(shù)etter方法 public UserVO getUserVO(){ return userVO;}

//用來傳值的變量必須有setter方法

public void setUserVO(UserVO userVO){ } this.userVO = userVO;public String getMessage(){ } return message;public void setMessage(String message){ } this.message = message;public String getSayHiTo(){ } return sayHiTo;public void setSayHiTo(String sayHiTo){ } this.sayHiTo = sayHiTo;// Override聲明說明這個(gè)方法復(fù)寫或?qū)崿F(xiàn)了父類或接口方法。

// 如action-mapping中不顯示指定別的方法,struts2會(huì)將execute()作為默認(rèn)的action方法// 返回的SUCCESS常量,來自ActionSupport,值為“success”。

// action-mapping會(huì)根據(jù)不同的返回值采取不同的轉(zhuǎn)發(fā)或頁面跳轉(zhuǎn)動(dòng)作。@Override

public String execute()throws Exception {

} System.out.println(“******execute******”);System.out.println(userVO.getUsername()+ “ logins”);return SUCCESS;

// 在Struts2執(zhí)行execute()之前,會(huì)先執(zhí)行validateExecute()進(jìn)行用戶輸入驗(yàn)證 // 這個(gè)方法名必須符合Struts2驗(yàn)證框架所規(guī)定的命名規(guī)范 public void validateExecute(){

}

} System.out.println(“******validateExecute******” + userVO.getUsername());if(null == userVO.getUsername()|| userVO.getUsername().length()< 5){ this.addFieldError(“username”, “USERNAME ERROR”);} if(null == userVO.getPassword()|| userVO.getPassword().length()< 5){ this.addFieldError(“password”, “PASSWORD ERROR”);} // 一個(gè)自定義方法。通過在action-mapping中的設(shè)置,可以實(shí)現(xiàn)使用POJO的自定義服務(wù)配置 public String sayHi()throws Exception {

} System.out.println(“say hi to ” + getSayHiTo());return SUCCESS;// 符合驗(yàn)證框架命名規(guī)范的、真對(duì)于sayHi()的驗(yàn)證方法 public void validateSayHi(){

} System.out.println(“******validateSayHi******” + getSayHiTo());if(null == getSayHiTo()|| getSayHiTo().length()< 5){ this.addFieldError(“sayHiTo”, “SAYHITO ERROR”);} ExampleAction.java package com.tyland.b2b.web;

import com.tyland.b2b.web.base.BaseAction;

public class ExampleAction extends BaseAction {

private static final long serialVersionUID =-***7281L;

private String message;private String sayHiTo;

public String getMessage(){

return message;}

public void setMessage(String message){

this.message = message;}

public String getSayHiTo(){

return sayHiTo;}

} public void setSayHiTo(String sayHiTo){ } this.sayHiTo = sayHiTo;public String finish(){

} System.out.println(“example finished”);setMessage(getSayHiTo());return SUCCESS;JSP代碼

index.jsp <%@ page language=“java” contentType=“text/html;charset=UTF-8”

pageEncoding=“UTF-8”%>

<%@ taglib uri = “http://java.sun.com/jsp/jstl/core” prefix = “c” %>

Hello World

第三篇:Struts2試題

Struts2 試題

1.struts2的執(zhí)行流程?

客戶端提交一個(gè)HttpServletRequest請(qǐng)求(action或JSP頁面)

請(qǐng)求被提交到一系列Filter過濾器,如ActionCleanUp和FiterDispatcher等

FilterDispatcher是Struts2控制器的核心,它通常是過濾器鏈中的最后一個(gè)過濾器 詢問ActionMapper是否需要調(diào)用某個(gè)action來處理

如果ActonMapper據(jù)誒的那個(gè)需要調(diào)用某個(gè)A傳統(tǒng),F(xiàn)ilterDispatcher則把請(qǐng)求教到ActionProxy,由其進(jìn)行處理

ActionProxy通過Configuration Manager詢問框架配置文件,找到需要調(diào)用的Action類 ActionProxy創(chuàng)建一個(gè)ActionInvocation實(shí)例,而ActionInvocation通過代理模式調(diào)用action Action執(zhí)行完畢后,返回一個(gè)result字符串,此時(shí)再按相反的順序通過Interceptor攔截器 最后ActionInvocation負(fù)責(zé)根據(jù)struts配置文件中配置的result元素,找到與返回值對(duì)應(yīng)的result

2.struts2的web里需要配置什么?

1. 2. 3.struts2 4. 5.org.apache.struts2.dispatcher.FilterDispatcher 6. 7. 8. 9. 10.struts2 11./* 12.

3.struts.xml文件哪個(gè)常量可以實(shí)現(xiàn)不用重新啟動(dòng)服務(wù)就可以得到反饋? ?

4.struts中的package的作用是什么?namespace可以為空嗎?如果namespace為空會(huì)意味著什么呢?

標(biāo)識(shí)

可以、Timmer

如果沒寫,說明該包為默認(rèn)空間 只要敲index沒有精確對(duì)應(yīng)的package都交給namespackage 如果還沒有就崩潰

5.Action 的實(shí)現(xiàn)通集成常用那種方法? 一種是直接實(shí)現(xiàn)自己的Action 另一種是實(shí)現(xiàn)Action的接口 繼承ActionSupport

6.路徑設(shè)置用什么路徑,絕對(duì)還是相對(duì)? 都可以

7.通配符有幾種?要遵循什么原則? {1}

方法名

8.Action的屬性?

path屬性:指定請(qǐng)求訪問Action的路徑

type屬性:指定Action的完整類名

name屬性:指定需要傳遞給Action的ActionForm Bean scope屬性:指定ActionForm Bean的存放范圍

validate屬性:指定是否執(zhí)行表單驗(yàn)證

input屬性:指定當(dāng)表單驗(yàn)證失敗時(shí)的轉(zhuǎn)發(fā)路徑。

元素還包含一個(gè)子元素,它定義了一個(gè)請(qǐng)求轉(zhuǎn)發(fā)路徑。

9.用于讀取資源文件,實(shí)現(xiàn)國際化的Struts2的標(biāo)簽()。

10.一下關(guān)于struts的描述中,不正確的是(D)struts 是一個(gè)基于JAVA EE的MVC struts2的框架配置文件是struts-configxml struts2的控制組建核心包括核心控制和業(yè)務(wù)控制器

在struts中解決中文亂碼可以通過修改struts。I18n。encoding的值為GBK來實(shí)現(xiàn) 11.不屬于Struts2的動(dòng)作類的常量返回值的是(C)

A.success

B.input

C.never

D.login 12.一下不屬于Struts2動(dòng)作執(zhí)行的結(jié)果類型是(A)a.a(chǎn)ction b.redirect c.redirectAction d.dispatcher 13.在struts.xml中。假如在一個(gè)package中沒有配置namespace,那么在其中配置一個(gè)Action名字叫做login,它的result是這樣配置的/login.jsp,已知在此配置文件中還有另一個(gè)name為user的package其中也有一個(gè)Action名字為login,它的result是這樣配置的/loginUser.jsp那么在以下什么情況下將能訪問到login.jsp A A.在根路徑后輸入/login B.在根路徑后輸入/user/login C.在根路徑后輸入/login/user D.在根路徑后輸入/abc/login

14.正規(guī)開發(fā)中,在使用struts2時(shí)定義Action使用哪種方式。B A.直接定義Action類。B.從ActionSupport繼承。C.從Action繼承。D.實(shí)現(xiàn)Action接口。

15.不同的線程在調(diào)用Action時(shí)創(chuàng)建幾個(gè)Action實(shí)例。C A.1個(gè) B.2個(gè)

C.每個(gè)線程創(chuàng)建1個(gè)。D.可以自定義配置。

16.struts2的體系結(jié)構(gòu)主要包括那幾個(gè)部分(A)A.模型層

B.struts控制器組件 C。struts配置文件 D。struts標(biāo)簽庫

17.struts提供了Action接口定義了5個(gè)標(biāo)準(zhǔn)字符串常量,不包括的有(C)A SUCCESS

B NONE C REG D LOGIN 18.struts中數(shù)據(jù)驗(yàn)證失敗時(shí),通過(B)標(biāo)簽顯示錯(cuò)誤信息。A

B

C

D

19.列舉出action執(zhí)行結(jié)果類型,并說明用途。

默認(rèn)結(jié)果類型是dispatcher chain:Action鏈?zhǔn)教幚淼慕Y(jié)果類型,也就是將結(jié)果轉(zhuǎn)發(fā)到這個(gè)action中。dispatcher:用于整合JSP的結(jié)果類型

redirect:實(shí)際上dispatcher和redirect的區(qū)別就是在于轉(zhuǎn)發(fā)和重定向的區(qū)別。redirect-action:重定向action

20.簡述struts2文件上傳的方法。

Commons FileUpload通過將HTTP的數(shù)據(jù)保存到臨時(shí)文件夾,然后Struts使用fileUpload攔截器將文件綁定到Action的實(shí)例中。從而我們就能夠以本地文件方式的操作瀏覽器上傳的文件。

21.在struts2標(biāo)簽內(nèi)部 能否使用el表達(dá)式?

不可

22.struts2中result-type的chain和redirectAction的區(qū)別

chain,它是一個(gè)璉式的,是從一個(gè)Action轉(zhuǎn)發(fā)另外一個(gè)Aciton redirectAction,是從新請(qǐng)求一個(gè)Action

23.能否取到Httpsession的值 不可

24.action的name里邊寫的名字是什么地方用的? From 表單action引用

25.action的class里面寫的是什么? Action相應(yīng)類的詳細(xì)路徑

26.action的extends里面調(diào)用的是什么?

從另外一個(gè)包繼承、相當(dāng)于擁有即承包的所有配置

27.如果不寫action的后面mothod,默認(rèn)的調(diào)用的方法是什么,如果寫的話,寫的時(shí)候有什么需要注意的?

不寫會(huì)默認(rèn)調(diào)用最上面的方法

28.輸出值的strtus標(biāo)簽是什么?

s:property

28.循環(huán)的struts標(biāo)簽是什么?

s:iterator

29.判斷的strtuts標(biāo)簽是什么?

s:if

30.顯示文本的strtuts標(biāo)簽是什么?

31.要調(diào)用struts標(biāo)簽需要導(dǎo)入什么? <%@taglib uri=“/struts-tags” prefix=“s”%>

32.如果需要調(diào)用sc:datetimepicker需要導(dǎo)入什么,sc代表的是什么意思?

33.中的s代表的是什么,這個(gè)標(biāo)簽是干什么用的?

獲取前臺(tái)文本的值

34.請(qǐng)簡述前臺(tái)提交后是如何到達(dá)后臺(tái)action的。

通過from表單action找到struts配置文件相應(yīng)action的類 再去找相應(yīng)action類里的方法

第四篇:struts2課件

—高級(jí)軟件人才實(shí)作培訓(xùn)專家!Struts2的啟動(dòng)配置在struts1.x中,struts框架是通過Servlet啟動(dòng)的。在struts2中,struts框架是通過Filter啟動(dòng)的。他在web.xml中的配置如下:struts2org.apache.struts2.dispatcher.FilterDispatcherstruts2/*在FilterDispatcher的init()方法中將會(huì)讀取類路徑下默認(rèn)的配置文件struts.xml完成初始化操作。注意:struts2讀取到struts.xml的內(nèi)容后,會(huì)將內(nèi)容封裝進(jìn)javabean對(duì)象并存放在內(nèi)存中,對(duì)于用戶每次請(qǐng)求的處理將使用內(nèi)存中的數(shù)據(jù),而不是每次請(qǐng)求都讀取struts.xml文件

—高級(jí)軟件人才實(shí)作培訓(xùn)專家!Struts2的配置文件Struts2默認(rèn)的配置文件為struts.xml,F(xiàn)ilterDispatcher過濾器在初始化時(shí)將會(huì)在WEB-INF/classes下尋找該文件,該文件的配置模版如下:

—高級(jí)軟件人才實(shí)作培訓(xùn)專家!第一個(gè)Struts2應(yīng)用Struts2默認(rèn)的配置文件為struts.xml,F(xiàn)ilterDispatcher過濾器在初始化時(shí)將會(huì)在WEB-INF/classes下尋找該文件,該文件的配置模版如下:

/WEB-INF/page/hello.jsp —高級(jí)軟件人才實(shí)作培訓(xùn)專家!第一個(gè)Struts2應(yīng)用例子中使用到的cn.itcast.action.HelloWorldAction類如下:package cn.itcast.action;public class HelloWorldAction{private String message;public String getMessage(){return message;}public void setMessage(String message){this.message = message;}public String execute()throws Exception{this.message = “我的第一個(gè)struts2應(yīng)用”;return “success”;}} 2

—高級(jí)軟件人才實(shí)作培訓(xùn)專家!第一個(gè)Struts2應(yīng)用例子中使用到的/WEB-INF/page/hello.jsp如下:<%@ page language=“java” pageEncoding=“UTF-8”%>第一個(gè)struts2應(yīng)用${message }

第五篇:Struts2習(xí)題

Struts2習(xí)題1 1.struts中的package的作用是什么?namespace可以為空嗎?如果namespace為空會(huì)意味著什么呢?

標(biāo)識(shí)、可以、如果沒寫,說明該包為默認(rèn)空間 2.Action 的實(shí)現(xiàn)通集成常用那種方法? 一種是直接實(shí)現(xiàn)自己的Action 另一種是實(shí)現(xiàn)Action的接口 繼承ActionSupport 3.不屬于Struts2的動(dòng)作類的常量返回值的是(C)

A.success

B.input

C.never

D.login 4.以下不屬于Struts2動(dòng)作執(zhí)行的結(jié)果類型是(A)a.a(chǎn)ction b.redirect c.redirectAction d.dispatcher 5.在struts.xml中。假如在一個(gè)package中沒有配置namespace,那么在其中配置一個(gè)Action名字叫做login,它的result是這樣配置的/login.jsp,已知在此配置文件中還有另一個(gè)name為user的package其中也有一個(gè)Action名字為login,它的result是這樣

配置的/loginUser.jsp那么在以下什么情況下將能訪問到login.jsp

A A.在根路徑后輸入/login B.在根路徑后輸入/user/login C.在根路徑后輸入/login/user D.在根路徑后輸入/abc/login 6.正規(guī)開發(fā)中,在使用struts2時(shí)定義Action使用哪種方式。B A.直接定義Action類。B.從ActionSupport繼承。C.從Action繼承。D.實(shí)現(xiàn)Action接口。

7.不同的線程在調(diào)用Action時(shí)創(chuàng)建幾個(gè)Action實(shí)例。C A.1個(gè) B.2個(gè)

C.每個(gè)線程創(chuàng)建1個(gè)。D.可以自定義配置。

8.struts提供了Action接口定義了5個(gè)標(biāo)準(zhǔn)字符串常量,不包括的有(C)A SUCCESS

B NONE C REG D LOGIN

9.列舉出action執(zhí)行結(jié)果類型,并說明用途。

默認(rèn)結(jié)果類型是dispatcher chain:Action鏈?zhǔn)教幚淼慕Y(jié)果類型,也就是將結(jié)果轉(zhuǎn)發(fā)到這個(gè)action中。

dispatcher:用于整合JSP的結(jié)果類型

redirect:實(shí)際上dispatcher和redirect的區(qū)別就是在于轉(zhuǎn)發(fā)和重定向的區(qū)別。

redirect-action:重定向action 10.struts2中result-type的chain和redirectAction的區(qū)別 chain,它是一個(gè)璉式的,是從一個(gè)Action轉(zhuǎn)發(fā)另外一個(gè)Aciton redirectAction,是從新請(qǐng)求一個(gè)Action 11.action的class里面寫的是什么? Action相應(yīng)類的詳細(xì)路徑

12.action的extends里面調(diào)用的是什么?

從另外一個(gè)包繼承、相當(dāng)于擁有即承包的所有配置

13.如果不寫action的后面mothod,默認(rèn)的調(diào)用的方法是什么,如果寫的話,寫的時(shí)候有什么需要注意的?

不寫會(huì)默認(rèn)調(diào)用最上面的方法

14.輸出值的strtus標(biāo)簽是什么?

s:property

15.循環(huán)的struts標(biāo)簽是什么?

s:iterator

15.判斷的strtuts標(biāo)簽是什么?

s:if

16.顯示文本的strtuts標(biāo)簽是什么?

17.請(qǐng)簡述前臺(tái)提交后是如何到達(dá)后臺(tái)action的。

通過form表單action找到struts配置文件相應(yīng)action的類 再去找相應(yīng)action類里的方法

Struts2習(xí)題2

一、填空題

1.Struts2框架由___________和___________框架發(fā)展而來。(Struts&WebWork)2.Struts2以___________為核心,采用___________的機(jī)制來處理用戶的請(qǐng)求。(WebWork,攔截器)

3.Struts2中的控制器類是一個(gè)普通的___________。Class或填過濾器類

二、選擇題

1.不屬于Action接口中定義的字符串常量的是___________。B A.SUCCESS B.FAILURE

C.ERROR D.INPUT 2.在控制器類中一般需要添加相應(yīng)屬性的___________和___________。AC A.setter方法 B.a(chǎn)s方法 C.getter方法 D.is方法 3.控制器需要在___________配置文件中進(jìn)行配置。B A.web.xml B.struts.xml C.struts2.xml D.webwork.xml 4.不屬于Struts 2表單標(biāo)簽庫的是___________。D A. B. C. D.

      <blockquote id="11161"></blockquote>
        <label id="11161"><strong id="11161"><td id="11161"></td></strong></label>

          <abbr id="11161"><dl id="11161"></dl></abbr><cite id="11161"></cite>
          1. <blockquote id="11161"><ins id="11161"></ins></blockquote>