前幾天不是一個同事使用OpenSessionInView pattern時,遇到Hibernate 3的mappinglazy="true"的問題,也不會想到它
struts啟動spring的WebApplicationContext
spring有三種啟動方式,使用ContextLoaderServlet,ContextLoaderListener和ContextLoaderPlugIn.
看一下ContextLoaderListener的源碼,這是一個ServletContextListener
/**
? * Initialize the root web application context.
? */
?public void contextInitialized(ServletContextEvent event) {
??this.contextLoader = createContextLoader();
??this.contextLoader.initWebApplicationContext(event.getServletContext());
?}
?
??/**
? * Create the ContextLoader to use. Can be overridden in subclasses.
? * @return the new ContextLoader
? */
?protected ContextLoader createContextLoader() {
??return new ContextLoader();
?}
?contextLoader的源碼
?public WebApplicationContext initWebApplicationContext(ServletContext servletContext)
???throws BeansException {
??long startTime = System.currentTimeMillis();
??if (logger.isInfoEnabled()) {
???logger.info("Root WebApplicationContext: initialization started");
??}
??servletContext.log("Loading Spring root WebApplicationContext");
??try {
???// Determine parent for root web application context, if any.
???ApplicationContext parent = loadParentContext(servletContext);
???WebApplicationContext wac = createWebApplicationContext(servletContext, parent);
???servletContext.setAttribute(
?????WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);
???if (logger.isInfoEnabled()) {
????logger.info("Using context class [" + wac.getClass().getName() +
??????"] for root WebApplicationContext");
???}
???if (logger.isDebugEnabled()) {
????logger.debug("Published root WebApplicationContext [" + wac +
??????"] as ServletContext attribute with name [" +
??????WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]");
???}
???if (logger.isInfoEnabled()) {
????long elapsedTime = System.currentTimeMillis() - startTime;
????logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms");
???}
???return wac;
??}
??catch (RuntimeException ex) {
???logger.error("Context initialization failed", ex);
???servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);
???throw ex;
??}
??catch (Error err) {
???logger.error("Context initialization failed", err);
???servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err);
???throw err;
??}
?}
?注意WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,這里面放了WebApplicationContext,需要使用時從ServletContext取出
?可以使用WebApplicationContextUtils得到WebApplicationContext
?public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
??Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
??if (attr == null) {
???return null;
??}
??if (attr instanceof RuntimeException) {
???throw (RuntimeException) attr;
??}
??if (attr instanceof Error) {
???throw (Error) attr;
??}
??if (!(attr instanceof WebApplicationContext)) {
???throw new IllegalStateException("Root context attribute is not of type WebApplicationContext: " + attr);
??}
??return (WebApplicationContext) attr;
?}
?關鍵的問題在于struts如何啟動的spring的,ContextLoaderPlugIn的源碼
?
?// Publish the context as a servlet context attribute.
??String attrName = getServletContextAttributeName();
??getServletContext().setAttribute(attrName, wac);
?
?public String getServletContextAttributeName() {
??return SERVLET_CONTEXT_PREFIX + getModulePrefix();
?}
?不同加載的Key竟然不同,原因就是WebApplicationContext放在那里的問題,可spring調用的時候會根據WebApplicationContext里面定義的那個名字去找的,問題出在這里
?在struts-config.xml中配置
??? <plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
????? <set-property property="contextConfigLocation" value="/WEB-INF/applicationContext.xml" />
??? </plug-in>
??? <controller>
??????? <set-property property="processorClass" value="org.springframework.web.struts.DelegatingRequestProcessor" />
??? </controller>
?原理是這樣的,Struts雖然只能有一個ActionServlet實例,但是對于不同的子應用分別能有自己的RequestProcessor實例每個RequestProcessor實例分別對應不同的struts配置文件。
?? 子應用的ProcessorClass類必須重寫一般就是繼承RequestProcessor類,然后再其配置文件的controller元素中的<processorClass>屬性中作出修改。那么當
? getRequestProcessor(getModuleConfig(request)).process(request,response);就能根據request選擇相應的moduleconfig,再根據其<processorClass>屬性選擇相應的RequestProcessor子類來處理相應的請求了。
Strategy策略模式是屬于設計模式中 對象行為型模式,主要是定義一系列的算法,把這些算法一個個封裝成單獨的類.
Stratrgy應用比較廣泛,比如, 公司經營業務變化圖, 可能有兩種實現方式,一個是線條曲線,一個是框圖(bar),這是兩種算法,可以使用Strategy實現.
這里以字符串替代為例, 有一個文件,我們需要讀取后,希望替代其中相應的變量,然后輸出.關于替代其中變量的方法可能有多種方法,這取決于用戶的要求,所以我們要準備幾套變量字符替代方案.
?
首先,我們建立一個抽象類RepTempRule 定義一些公用變量和方法:
public abstract class RepTempRule{ protected String oldString=""; public void setOldString(String oldString){ this.oldString=oldString; } protected String newString=""; public String getNewString(){ return newString; } public abstract void replace() throws Exception; } |
在RepTempRule中 有一個抽象方法abstract需要繼承明確,這個replace里其實是替代的具體方法.
我們現在有兩個字符替代方案,
1.將文本中aaa替代成bbb;
2.將文本中aaa替代成ccc;
對應的類分別是RepTempRuleOne RepTempRuleTwo
public class RepTempRuleOne extends RepTempRule{ |
public class RepTempRuleTwo extends RepTempRule{ public void replace() throws Exception{ newString=oldString.replaceFirst("aaa", "ccc") System.out.println("this is replace Two"); } } |
第二步:我們要建立一個算法解決類,用來提供客戶端可以自由選擇算法。
public class RepTempRuleSolve {
private RepTempRule strategy; public RepTempRuleSolve(RepTempRule rule){ public String getNewContext(Site site,String oldString) { public void changeAlgorithm(RepTempRule newAlgorithm) { } |
?
?
調用如下:
public class test{ ...... public void testReplace(){ //使用第一套替代方案 //使用第二套 solver=new RepTempRuleSolve(new RepTempRuleTwo());
} ..... } |
我們達到了在運行期間,可以自由切換算法的目的。
實際整個Strategy的核心部分就是抽象類的使用,使用Strategy模式可以在用戶需要變化時,修改量很少,而且快速.
Strategy和Factory有一定的類似,Strategy相對簡單容易理解,并且可以在運行時刻自由切換。Factory重點是用來創建對象。
Strategy適合下列場合:
1.以不同的格式保存文件;
2.以不同的算法壓縮文件;
3.以不同的算法截獲圖象;
4.以不同的格式輸出同樣數據的圖形,比如曲線 或框圖bar等
一、表單驗證的流程在 hello.jsp 網頁上,不輸入姓名,直接單擊【 Submit 】 按鈕,會看到如圖 2-6 所示的網頁。
圖 2-6? 表單驗證失敗的 hello.jsp 網頁
當客戶提交 HelloForm 表單時, 請求路徑為 “ /HelloWorld.do ”:
<html:form action="/HelloWorld.do" focus="userName" >
服務器端執行表單驗證流程如下。
( 1 ) Servlet 容器在 web.xml 文件中尋找 <url-pattern> 屬性為“ *.do ”的 <servlet-mapping> 元素:
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
( 2 ) Servlet 容器依據以上 <servlet-mapping> 元素的 <servlet-name> 屬性“ action ”,在 web.xml 文件中尋找匹配的 <servlet> 元素:
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
</servlet>
( 3 ) Servlet 容器把請求轉發給以上 <servlet> 元素指定的 ActionServlet , ActionServlet 依據用戶請求路徑 “ /HelloWorld.do ”, 在 Struts 配置文件中檢索 path 屬性為 “ /HelloWorld ” 的 <action> 元素 :
<action??? path????? = "/HelloWorld"
????????? type????? = "hello.HelloAction"
????????? name????? = "HelloForm"
??? ??????scope???? = "request"
????????? validate? = "true"
? ????????input???? = "/hello.jsp"
?>
??????? <forward name="SayHello" path="/hello.jsp" />
?</action>
?
|
更確切地說, ActionServlet 此時檢索的是 ActionMapping 對象,而不是直接訪問 Struts 配置文件中的 <action> 元素。因為 在 ActionServlet 初始化的時候,會加載 Struts 配置文件,把各種配置信息保存在相應的配置類的實例中,例如 <action> 元素的配置信息存放在 ActionMapping 對象中。 |
( 4 ) ActionServlet 根據 <action> 元素的 name 屬性,創建一個 HelloForm 對象,把客戶提交的表單數據傳給 HelloForm 對象,再把 HelloForm 對象保存在 <action> 元素的 scope 屬性指定的 request 范圍內。
( 5 )由于 <action> 元素的 validate 屬性為 true , ActionServlet 調用 HelloForm 對象的 validate() 方法執行表單驗證:
public ActionErrors validate(ActionMapping mapping,
???????????????????????????????? HttpServletRequest request) {
???????? ?? ActionErrors errors = new ActionErrors();
????????????if ((userName == null) || (userName.length() < 1))
??????? ????errors.add("username", new ActionMessage("hello.no.username.error"));
????????????return errors;
}
( 6 ) HelloForm 對象的 validate() 方法返回一個 ActionErrors 對象,里面包含一個 ActionMessage 對象,這個 ActionMessage 對象中封裝了錯誤消息,消息 key 為“ hello.no.username.error ” , 在 Resource Bundle 中與值匹配的消息文本為:
hello.no.username.error=Please enter a <i>UserName</i> to say hello to!
( 7 ) ActionServlet 把 HelloForm 的 validate() 方法返回的 ActionErrors 對象保存在 request 范圍內,然后根據 <action> 元素的 input 屬性,把客戶請求轉發給 hello.jsp 。
( 8 ) hello.jsp 的 <html:errors> 標簽從 request 范圍內讀取 ActionErrors 對象,再從 ActionErrors 對象中讀取 ActionMessage 對象,把它包含的錯誤消息顯示在網頁上。
接下來在 hello.jsp 的 HTML 表單中輸入姓名“ Monster ”,然后單擊【 Submit 】 按鈕。當服務器端響應客戶請求時,驗證流程如下。
( 1 )表單驗證 的流程( 1 )~( 4 )。
( 2 ) ActionServlet 調用 HelloForm 對象的 validate() 方法,這次 validate() 方法返回的 ActionErrors 對象中不包含任何 ActionMessage 對象,表示表單驗證成功。
( 3 ) ActionServlet 查找 HelloAction 實例是否存在,如果不存在就創建一個實例。然后調用 HelloAction 的 execute() 方法。
( 4 ) HelloAction 的 execute() 方法先進行邏輯驗證,由于沒有通過邏輯驗證,就創建一個 ActionMessage 對象,這個 ActionMessage 對象封裝了錯誤消息,消息 key 為“ hello.dont.talk.to.monster ”,在 Resource Bundle 中與值匹配的消息文本為:
hello.dont.talk.to.monster=We don't want to say hello to Monster!!!
execute() 方法把 ActionMessage 對象保存在 ActionMessages 對象中,再把 ActionMessages 對象存放在 request 范圍內。最后返回一個 ActionForward 對象,該對象包含的請求轉發路徑為 <action> 元素的 input 屬性指定的 hello.jsp 。
以下是 execute() 方法中進行邏輯驗證的代碼:
ActionMessages errors = new ActionMessages();
String userName = (String)((HelloForm) form).getUserName();
String badUserName = "Monster";
?
if (userName.equalsIgnoreCase(badUserName)) {
???? errors.add("username", new ActionMessage("hello.dont.talk.to.monster", badUserName ));
???? saveErrors(request, errors);
???? return (new ActionForward(mapping.getInput()));
}
( 5 ) ActionServlet 依據 HelloAction 返回的 ActionForward 對象,再把請求轉發給 hello.jsp 。
( 6 ) hello.jsp 的 <html:errors> 標簽從 request 范圍內讀取 ActionMessages 對象,再從 ActionMessages 對象中讀取 ActionMessage 對象,把它包含的錯誤消息顯示在網頁上, 如圖 所示。
邏輯驗證失敗時的 hello.jsp 網頁
接下來,在 hello.jsp 的 HTML 表單中輸入姓名“ Weiqin ”,然后單擊【 Submit 】 按鈕。當服務器端響應客戶請求時,流程如下。
( 1 )重復 二 的流程( 1 )~( 3 )。
( 2 ) HelloAction 的 execute() 方法先執行邏輯驗證,這次通過了驗證,然后執行相關的業務邏輯,最后調用 ActionMapping.findForward() 方法,參數為 “ SayHello ”:
// Forward control to the specified success URI
return (mapping.findForward("SayHello"));
( 3 ) ActionMapping.findForward() 方法從 <action> 元素中尋找 name 屬性為 “ SayHello ”的 <forward> 子元素,然后返回與之對應的 ActionForward 對象,它代表的請求轉發路徑為“ /hello.jsp ”。
|
更確切地說, ActionMapping 從本身包含的 HashMap 中查找 name 屬性為 “ SayHello ” 的 ActionForward 對象。在 ActionServlet 初始化時會加載 Struts 配置文件,把 <action> 元素的配置信息存放在 ActionMapping 對象中。 <action> 元素中可以包含多個 <forward> 子元素,每個 <forward> 子元素的配置信息存放在一個 ActionForward 對象中,這些 ActionForward 對象存放在 ActionMapping 對象的 HashMap 中。 |
( 4 ) HelloAction 的 execute() 方法 然后把 ActionForward 對象返回給 ActionServlet , ActionServlet 再把客戶請求轉發給 hello.jsp 。
( 5 ) hello.jsp 的 <bean:message> 標簽從 Resource Bundle 中讀取文本,把它們輸出到網頁上,最后生成 動態網頁,如圖 所示。
? 通過數據驗證的 hello.jsp 網頁
1、JSP頁面編碼