第一篇:Struts 2框架分析
寧波工程學院學年論文
Struts2框架分析
湯禹鑫
寧波工程學院,(315016)
E-mail 372854786@qq.com
摘 要: 本文對基于MVC模式,延續了Struts1和WebWork優勢的WEB框架——Struts2框架的各個層次的組成、功能進行了詳細的介紹。關鍵詞:Struts2;框架;J2EE;
1.引言
Apache Struts2是一個為企業級應用打造的優秀的、可擴展的Web框架。該框架旨在充分精簡應用程序的開發周期,從而減少創建、發布、應用所花費的時間。而且對于Struts1有很多革命性的改進,但它并不是全新的框架,而是在WebWork框架的基礎上發展起來的。所以也可以說Struts2是WebWork的升級,吸收了Struts1和WebWork的優勢,穩定性、性能都有了很好的保證。
2.Struts2的起源和背景
2.1 Struts1
在過去,Struts1是所有MVC框架中不容辯駁的勝利者。其程序運行流程如圖2-1所示。
圖2-1 Struts 1的程序運行流程
但是對于Struts 1框架而言,因為它與JSP/Servlet耦合非常緊密,因而導致了許多不可避免的缺陷,還有支持的表現層技術單
一、代碼嚴重依賴于Struts 1 API。隨著Web應用的拙見擴大,這些缺陷逐漸變成制約Struts 1發展的的重要因素——這也是Struts 2出現的原因。
寧波工程學院學年論文
(1)客戶端初始化一個指向Servlet容器(例如Tomcat)的請求;
(2)這個請求經過一系列的過濾器(Filter)(這些過濾器中有一個叫做ActionContextCleanUp的可選過濾器,這個過濾器對于Struts2和其它框架的集成很有幫助,例如:SiteMesh Plugin);
(3)接著FilterDispatcher被調用,FilterDispatcher詢問ActionMapper來決定這個請求是否需要調用某個Action;
(4)如果ActionMapper決定需要調用某個Action,FilterDispatcher把請求的處理交給ActionProxy;
(5)ActionProxy通過Configuration Manager詢問框架的配置文件,找到需要調用的Action類;
(6)ActionProxy創建一個ActionInvocation的實例;
(7)ActionInvocation實例使用命名模式來調用,在調用Action的過程前后,涉及到相關攔截器(Intercepter)的調用;
(8)一旦Action執行完畢,ActionInvocation負責根據struts.xml中的配置找到對應的返回結果。返回結果通常是(但不總是,也可能是另外的一個Action鏈)一個需要被表示的JSP或者FreeMarker的模版。在表示的過程中可以使用Struts 2 框架中繼承的標簽,在這個過程中需要涉及到ActionMapper。
3.2 Struts 2配置文件
Struts2 配置文件是用戶請求(View)和業務邏輯模塊(Model)Action 之間聯系的橋梁。開發者可以通過修改Struts2的配置文件來快速適應業務需求,它是整個Struts2的精髓之一。當然,熟悉Struts1和WebWork的框架的人對配置文件一定不會陌生,同Struts1和WebWork框架一樣,Struts2框架配置文件也分為XML文件和屬性資源文件兩種。struts.xml 文件中包含了Action 的定義以及Action 的返回值對應的視圖資源、命名空間等信息。此外,用戶也可以定義自己的XML文件,然后通過include指令將其包含到struts.xml文件中。另一類配置文件是屬性資源文件。資源文件中一般采用固定的Key-Value形式,用于定義Struts2 全局或者局部的資源數據,例如定義國際化、開發模式等信息。
3.3 Struts 2控制器
Struts 2的控制器組建是Struts 2框架的核心,事實上所有MVC框架都是以控制器組件為核心的。正如前面提到的,Struts 2的控制器由兩個部分組成:FilterDispatcher和業務控制器Action。前者由框架提供,負責攔截所有的用戶請求,其過程如圖3-3所示
寧波工程學院學年論文
圖3-1 過濾器處理請求過程
對于業務控制器Action,Struts 2框架為用戶提供了一個名為Action的接口,在接口中定義了SUCCESS、ERROR、INPUT、LOGIN、NONE五個靜態的字符串和一個execute方法,用戶在編寫自己的Action時只要實現該接口并重寫其中的execute方法,將所要實現的業務邏輯在該方法中處理就行了,當調用此Action時,Struts 2框架會自調用execute方法來完成所需的業務邏輯處理。實際上,在Struts2中起作用的業務邏輯并不是用戶自定義的Action ,而是系統生成的Action代理,只不過Action代理以用戶定義的Action為目標。
3.4 Struts 2標簽庫
于業Struts 2的標簽庫也是Struts 2的重要組成部分,Struts 2的標簽庫提供了非常豐富的功能,這些標簽不僅提供了表現層數據處理,而且還提供了基本的流程控制功能,還提供了國際化、Ajax支持等功能。使用標簽,開發者可以最大限度地減少頁面代碼的書寫。
4.Struts 2框架應用舉例
例如課本上的Struts2用作登陸的例子,添加Struts 2功能框架核心需要如下三個步驟:(1)修改web.xml文件,在web.xml中配置Struts 2的核心Filter。
org.apache.struts2.dispatcher.FilterDispatcher
(2)將Struts 2框架的類庫復制到Web應用的WEB-INF/lib路徑下(也可以自己手動進行添加)。
寧波工程學院學年論文
5.結論
Struts 2結合了Struts 1框架和WebWork框架的優點,其框架的各個組件都是可靠的松散耦合。Struts 2最大的特點就是簡單性,引入了OGNL表達式和值棧的概念,可以用最單間的代碼實現復雜的數據訪問。總而言之,Struts 2是一個當今Web開發中一個很重要而且功能足夠強大的框架,能讓我們更好地設計和完成Web應用。
參考文獻
[1]鄭阿奇.J2EE應用實踐教程.電子工業出版社,2011年7月.[2]李剛.Strut s2權威指南.電子工業出版社,2007年9月.[3]閆術卓.楊強.Strut s2技術詳解.電子工業出版社,2008年6月.
第二篇: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
this.servletContext = servletContext;
//配置在web.xml中的param參數
this.initParams = initParams;
}
//創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數
this.initParams = initParams;
}
我們再看在FilterDispatcher創建Dispatcher的:
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以從FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以從FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供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
addAction()方法負責讀取
loadInterceptorStack()方法負責將
StrutsXmlConfigurationProvider,此類繼承XmlConfigurationProvider,而XmlConfigurationProvider又實現ConfigurationProviderloadInterceptorStacks()方法負責將
而上面的方法最終會被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
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
this.servletContext = servletContext;
//配置在web.xml中的param參數
this.initParams = initParams;
}
//創建Dispatcher,此類是一個Delegate,它是真正完成根據url解析轉向,讀取對應Action的地方
public Dispatcher(ServletContext servletContext, Map
this.servletContext = servletContext;
//配置在web.xml中的param參數
this.initParams = initParams;
}
我們再看在FilterDispatcher創建Dispatcher的:
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以從FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
protected Dispatcher createDispatcher(FilterConfig filterConfig){
Map
for(Enumeration e = filterConfig.getInitParameterNames();e.hasMoreElements();){
String name =(String)e.nextElement();
String value = filterConfig.getInitParameter(name);
params.put(name, value);
}
都可以從FilterConfig中得到
return new Dispatcher(filterConfig.getServletContext(), params);
}
分七步載入各種配置屬性,都是通過ConfigurationProvider接口進行的,這個接口提供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
addAction()方法負責讀取
loadInterceptorStack()方法負責將
loadInterceptorStacks()方法負責將
而上面的方法最終會被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
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 這是將配置文件中定義的
接下來是看怎樣調用這些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
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初始化參數,就像下面這樣:
org.apache.struts2.dispatcher.FilterDispatcher
packages
com.lizanhong.action
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
// 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
ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return }
public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map
createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext);
response,context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
response,context,ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
container.inject(inv);
return }
下面先看DefaultActionInvocation的init方法
public void init(ActionProxy proxy){
this.proxy = proxy;
Map
// Setting this so that other classes, like object factories, can use the ActionProxy and other
// contextual information to operate
ActionContext actionContext = ActionContext.getContext();
if(actionContext!= null){
actionContext.setActionInvocation(this);
}
//創建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
interceptors = interceptorList.iterator();
createActionProxy(inv,namespace,actionName,methodName, executeResult, cleanupContext);
}
protected void createAction(Map
// load action
String timerKey = ”actionCreate: “ + proxy.getActionName();
try {
UtilTimerStack.push(timerKey);
//默認為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
Object o = null;
try {
//SpringObjectFactory
會
通
過
web.xml
中的
第三篇:實驗三 Struts2框架編程-實驗報告紙
南京信息工程大學 實驗(實習)報告
實驗(實習)名稱 Struts2框架編程 實驗(實習)日期 得分 指導教師 院 計軟 專業 計科 年級 2013級 班次 3 姓名 張文嬌 學號 20131308081
1.實驗目的:
1)掌握Struts2框架和工作流程。2)熟悉Struts標簽庫的使用。
3)掌握Struts 2攔截器的原理,并能進行相關設置和編程。4)了解和掌握文件上傳等功能實現
2.實驗內容:
1)采用Struts2框架,創建三個JSP頁面(hello.jsp、welcome.jsp)和一個Action實現類(StrutsAction),并對web.xml和Struts.xml進行必要配置,實現用戶登錄功能的處理。(參考教材3.1節)
2)采用Struts2相關技術,實現“學生綜合管理系統”的“添加學生信息”功能(具體需求詳見教材3.7所述)
3.實驗步驟
{對每個實驗題目進行簡要步驟描述,包括源碼和實驗結果截圖} 1)
1.啟動MyEclips 8.5 2.創建web project項目命名為FirstStruts2 3.添加支持包 4.配置web.xml
5.在工程中創建LoginAction.jsp import dao.CustomerDAO;public class LoginAction {
private String name;private String password;/**在此方法里實現業務邏輯處理*/ public String execute()throws Exception {
CustomerDAO dao=new CustomerDAO();boolean boo=dao.check(name, password);if(boo)return “success”;
} else return “error”;public String getName(){ return name;} public void setName(String name){ this.name = name;} public String getPassword(){ return password;} public void setPassword(String password){this.password = password;} } 6.配置struts.xml文件
第四篇:struts2框架的6個配置文件
Struts2的6個配置文件,分別是struts-default.xml,default.properties,struts-plugin.xml,struts.xml,struts.properties,web.xml
1、struts-default.xml,里面放置的是struts2框架的核心東西,如bean類,package包,result type,interceptor攔截器等
2、default.properties,properties類型的文件里面放置的是鍵值對,key和value,主要是對一些常量進行設定。
3、struts-plugin.xml,與第三方插件進行整合時需要使用的配置文件。如我們和spring進行整合時,就需要使用到struts-spring-plugin.xml這個配置文件
4、struts.xml,需要我們程序員在src目錄下手動創建,在這個里面也可以對常量進行設定,使用的是constant標簽,包含name和value屬性。
5、struts.properties,這里也是對常量進行設定
6、web.xml,這里設定struts2的啟動項,使用的是filter和filter-mapping標簽,也可以進行常量的設定,在filter標簽里使用init-param標簽。但是我們一般不再這里設定。
這里有4個配置文件,可以對常量進行設定。分別是default.properties,struts.xml,struts.properties,web.xml。他們的優先級順序是web.xml優先級最高,其次是struts.properties,struts.xml,最后是default.properties。我們最主要進行學習的就是struts.xml,其他的作為了解內容就可以了。
第五篇:Struts2介紹
Struts2集成指南
關于Struts2 Struts是Apache軟件基金會(ASF)贊助的一個開源項目。它最初是Jakarta項目中的一個子項目,并在2004年3月成為ASF的頂級項目。它通過采用Java Servlet/JSP技術,實現了基于Java EE Web應用的Model-View-Controller〔MVC〕設計模式的應用框架〔Web Framework〕,是MVC經典設計模式中的一個經典產品。
Struts,a history 在Java EE的Web應用發展的初期,除了使用Servlet技術以外,普遍是在JavaServer Pages(JSP)的源代碼中,采用HTML與Java代碼混合的方式進行開發。因為這兩種方式不可避免的要把表現與業務邏輯代碼混合在一起,都給前期開發與后期維護帶來巨大的復雜度。為了擺脫上述的約束與局限,把業務邏輯代碼從表現層中清晰的分離出來,2000年,Craig McClanahan采用了MVC的設計模式開發Struts。后來該框架產品一度被認為是最廣泛、最流行JAVA的WEB應用框架。
Craig McClanahan 2006年,WebWork與Struts這兩個優秀的Java EE Web框架(Web Framework〕的團體,決定合作共同開發一個新的,整合了WebWork與Struts優點,并且更加優雅、擴展性更強的框架,命名為“Struts 2”,原Struts的1.x版本產品稱為“Struts 1”。
至此,Struts項目并行提供與維護兩個主要版本的框架產品——Struts 1與Struts 2。Struts1 vs.Struts2 侵入性
Struts 1 在編程方面是面向抽象類編程,而不是面向接口編程。Struts 1要求自定義Action 類繼承一個特定的抽象基類Action。另一方面,Struts 1的 Action 依賴于 Servlet API,因為Struts 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 類可以實現一個Action接口,也可以實現其他接口,甚至不實現任何接口。這使得可選的和定制的服務成為可能。e.g.public class ExampleAction {
public String doSomething(){
return “success”;} }
線程模式
Struts 1 Action類 是單例模式并且必須是線程安全的,因為在web容器中,僅有Action類 的一個實例來處理所有的請求。
Struts2 Web容器為每一個請求產生一個Action類實例,因此沒有線程安全問題。可測試性
Struts1 由于對Servlet API的依賴,使得針對于自定義Action類的測試變得復雜。
Struts2 由于自定義Action可以為POJO,所以可以向測試一個POJO一樣來測試Action類。
請求參數封裝
Struts1 使用ActionForm 對象封裝用戶的請求參數,所有的 ActionForm 必須繼承一個基類:ActionForm。普通的JavaBean 不能用作ActionForm,并且需要在配置文件中定義ActionForm。e.g.public class LogonForm extends ActionForm {
private String userpassword;
private String username;}
Struts2 直接使用Action屬性來封裝用戶請求屬性,避免了開發者需要大量開發ActionForm類的煩瑣,實際上,這些屬性還可以是包含子屬性的 Rich對象類型。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 表達式語言。JSTL有基本對象圖遍歷,但在對集合和索引屬性的支持上則功能不強。在向視圖綁定值時,Struts1 使用標準JSP 機制把對象綁定到視圖頁面。
Struts2 Struts 2 可以使用JSTL,但它整合了一種更強大和靈活的表達式語言:OGNL(Object Graph Notation Language),因此,Struts 2 下的表達式語言功能更加強大。在向視圖綁定值時,Struts2 使用“ValueStack ”技術,使標簽庫能夠訪問值,而不需要把對象和視圖頁面綁定在一起。
校驗框架
Struts1 Struts1 支持在 ActionForm 重寫validate 方法中手動校驗,或者通過整合 Commons-validator 框架來完成數據校驗。
Struts2 Struts 2 支持通過重寫validate方法進行校驗,也支持整合XWork 校驗框架進行校驗。Struts2 architacture
名詞約定
凡是代碼、配置文件、IDE中出現的名詞,均采用英文原稱。
Roadmap 本文檔的目標是,幫助讀者在Eclipse中將Struts2集成至一個嶄新的Dynamic Web Project。集成步驟大致如下:配置Struts2的代碼環境-> 在web.xml中加入Struts2功能-> 測試Struts2。文檔目標達成的標志是:頁面請求能夠通過Struts2的Action Mapping成功轉發,并且基于Java的Struts2驗證框架能夠生效。
集成步驟
引入Struts2相關的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個jar文件,拷貝至WebContent/WEB-INF/lib下:
對于Web Dynamic Project,一般情況下,當你向lib目錄下copy了jar文件,eclipse會自動將jar文件加入build path下的名為Web App Libraries的Library。請確認在工程下出現了名為Web App Libraries的Library。如果有,說明這些jar文件已經被添加至build path了:
如果在工程目錄下找不到名為Web App Libraries的Library,說明jar文件沒有被添加至build path,需要進行手動添加。首先進入build path設置界面,選中Libraries 頁,并點擊Add JARs:
在JAR Selection窗口中,選中lib下所有的jar文件。選中后點擊OK:
你將看到被選中的jar文件已經被添加至build path:
在工程中會出現一個名為Referenced Libraries的Libraries。這說明jar文件已經被添加至build path:
以上兩種方法都可以將jar文件添加至build path,它們的效果是一樣的。
配置web.xml 添加filter 在web.xml中添加一個filter:
filter-name表示filter的名字,你可以任意決定這個名字。filter-class表示使用哪個類作為filter,從這個類的全稱來判斷,可以發現FilterDispatcher是Struts2提供的一個類。它是Struts2轉發請求的起點。在web.xml中添加一個filter-mapping:
filter-mapping用來映射url和filter的映射關系。filter-name表示filter的名字,這個名字必須和之前filter聲明中的filter-name一致。url-pattern表示哪些格式的url會被此filter濾中。/*表示在此web應用域名下,所有的地址都會被filter濾中,換言之,所有的http請求都會通過Struts2進行轉發。
filter的作用
通過以上的配置,FilterDispatcher和url與filter-name聯系在了一起。
由于在web容器中注冊了FilterDispatcher這個filter,Struts2可以收到所有http://localhost:8080/tyland-b2b 的http請求。隨后,FilterDispatcher會根據我們定義的action-mapping規則,將請求分發到指定的action類以及它的攔截器棧。最后,Struts2按照action-mapping規則,將后臺計算的結果返回給指定頁面。籠統地來說,Struts2就是這樣工作的,所以說,FilterDispatcher是Struts2工作的入口。
編寫代碼,測試Struts2 Struts2的環境已經配置好了,基于action-mapping的轉發機制已經可以運行了。為了證明這一點,請編寫一些測試jsp頁面和java代碼。
在編寫代碼的過程中,請確保代碼文件的位置如下圖所示:
代碼清單如下:
Java代碼
UserVO.java package com.tyland.b2b.vo;
// 一個Value Object(Data Model),用來存放用戶名、密碼 public class UserVO {
private String username;private String password;
// 成員變量password的getter方法。
// 在Strtus2中,用來在頁面和服務器間傳值的Value Object必須有getter方法
public String getPassword(){
return password;
} } // 成員變量password的setter方法。
// 在Strtus2中,用來在頁面和服務器間傳值的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;
// 為了代碼的靈活性和可擴展性,請聲明一個BaseAction基類
// BaseAction繼承Struts2的ActionSupport,因為我們想使用Struts2的一些額外幫助。// 對于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默認的action方法execute()// 由于繼承了ActionSupport,我們可以使用Struts2默認的校驗方法validate()public class UserAction extends BaseAction {
private static final long serialVersionUID =-7***3684190L;
// 用來在頁面和服務器之間傳遞用戶名、密碼的Value Object。變量名任意。
private UserVO userVO;
執行。// 用來在頁面和服務器之間傳遞message變量。名稱任意。private String message;//用來在頁面和服務器之間傳遞sayHiTo變量。名稱任意。private String sayHiTo;// 用來傳值的變量必須有getter方法 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聲明說明這個方法復寫或實現了父類或接口方法。
// 如action-mapping中不顯示指定別的方法,struts2會將execute()作為默認的action方法// 返回的SUCCESS常量,來自ActionSupport,值為“success”。
// action-mapping會根據不同的返回值采取不同的轉發或頁面跳轉動作。@Override
public String execute()throws Exception {
} System.out.println(“******execute******”);System.out.println(userVO.getUsername()+ “ logins”);return SUCCESS;
// 在Struts2執行execute()之前,會先執行validateExecute()進行用戶輸入驗證 // 這個方法名必須符合Struts2驗證框架所規定的命名規范 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”);} // 一個自定義方法。通過在action-mapping中的設置,可以實現使用POJO的自定義服務配置 public String sayHi()throws Exception {
} System.out.println(“say hi to ” + getSayHiTo());return SUCCESS;// 符合驗證框架命名規范的、真對于sayHi()的驗證方法 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” %>