1. 程式人生 > >從原始碼分析struts框架執行流程

從原始碼分析struts框架執行流程

struts 原始碼解析

ActionServlet 的執行流程 •Tomcat 及封裝配置 2 // web.xml 檔案的 標籤,配置則伺服器啟動則建立ActionServlet,否則訪問時建立 Tomcat 一啟動就將 web.xml 檔案讀取到記憶體,讀取 標籤創建出中央控制器 ActionServlet ,再將主配檔案 struts-config.xml 讀取到記憶體,實際是讀取到記憶體的一個檔案 物件 ModuleConfigImpl 中進行管理 – 將主配檔案封裝到 ModuleConfigImpl 檔案物件中。

•ModuleConfigImpl() // 此物件繼續封裝配置資訊

public ModuleConfigImpl( String prefix) { this.actionConfigs = new HashMap(); this.formBeans = new HashMap(); }

ModuleConfigImpl 物件建立 actionConfigs 和 formBeans 兩個 Map 集合: □actionConfigs:存放 ActionMapping 物件 – key 為 path,value 為 ActionMapping 物件

□ActionMapping 物件 – 封裝對應的 標籤中的各個屬性 有幾個 就有幾個 ActionMapping 物件 ActionMapping 由 actionConfigs 來管理,資料結構為 Bean □formBeans:存放 FormBeanConfig 物件 – key 為 name,value 為 FormBeanConfig 物件 □FormBeanConfig 物件 – 封裝 標籤裡的 name 和type 屬性

•ActionServlet.class public class ActionServlet extends HttpServlet { // 中央控制器 struts 繼承 HttpServlet 類,故有 init() / doGet() / doPost() / destroy() 方法protected String config = “/WEB-INF/struts-config.xml”; // 預設 config 就是 struts-config.xml

public void doGet( HttpServletRequest request, HttpServletResponse response)
		 throws IOException, ServletException { 
		 		process( request, response );
		 	 }

•process( request, response )

protected void process( HttpServletRequest request, HttpServletResponse response) 		
				throws IOException, ServletException {
				getRequestProcessor(getModuleConfig(request)).process(request, response); }

•process( request, response )

public void process( HttpServletRequest request, HttpServletResponse response)
			 throws IOException, ServletException { 擷取 URL
			String path = processPath(request, response); 取得 ActionMapping
			ActionMapping mapping = processMapping(request, response, path);

建立 ActionForm ActionForm form = processActionForm(request, response, mapping); 收集資料到 ActionForm processPopulate(request, response, form, mapping); // 收集完表單資料後,如果配置了 validate,就對錶單資料中的格式做驗證 if (!processValidate(request, response, form, mapping)) { return; }

例項化 Action Action action = processActionCreate(request, response, mapping); 執行 Action 中的 execute() 方法 ActionForward forward = processActionPerform( request, response, action, form, mapping); 轉向 processForwardConfig(request, response, forward); }

擷取 URL •processPath( request, response) /* Tomcat 將請求字串放在 request 內建物件中,此方法中獲取請求字串並擷取所需字元 */ protected String processPath( HttpServletRequest request, HttpServletResponse response) throws IOException { □請求字串:http://127.0.0.1:8080/struts_login/login.do?username=“admin”&password=“admin” path = request.getServletPath(); // 從專案名之後開始擷取到最後,截取出 /login.do?username=“admin”&password=“admin” int slash = path.lastIndexOf("/"); // 找子串最後一次出現的位置,得到 slash = 0 int period = path.lastIndexOf("."); // 得到 period = 6 if ((period >= 0) && (period > slash)) { path = path.substring(0, period); } // 截取出子串 path = “/login” return (path); } // 將截取出的子串 “/login” 返回給呼叫方法 process()

○子串擷取: □request.getURL(); // 從專案名開始到結束 □request.getServletPath(); // 從專案名之後開始擷取到結束 □request.getContextPath(); // 只找專案名

取得 ActionMapping [ struts-config.xml 檔案中 標籤資訊]

** •processMapping(request, response, path)

protected ActionMapping processMapping( HttpServletRequest request, 		
		HttpServletResponse response, String path) 
					throws IOException { ActionMapping mapping = 			
					(ActionMapping)moduleConfig.findActionConfig(path); return ( mapping ); }

** ---------------------------------- findActionConfig( path ) 從 ModuleConfig 裡找 actionConfigs 集合,通過 path = “/login” 與集合中的 key 相匹配,取得該請求對應的 ActionMapping 物件 Key = “/login”,value = ActionMapping 物件 ( mapping ),取得 ActionMapping 物件後返回給呼叫方法 processMapping() □取到該請求的 ActionMapping 物件後即可取得 標籤與其中的各屬性 <action path="/login" type=“com.struts.LoginAction” name=“loginForm” scope=“request” // 一旦建立了表單Bean 物件,就將物件封裝到scope 對應的內建物件中 – 如果不配置 scope,預設為 session validate=“false” // 標識不呼叫表單Bean 的 valicate() 方法 – 如果不配置 validate,預設為 true,會對錶單中的資料進行驗證

// 此標籤為了建立 ActionForward 物件 // redirect 不寫預設為 false,即轉向方式為請求轉發

建立 ActionForm

•processActionForm(request, response, mapping)

**protected ActionForm processActionForm( HttpServletRequest request, 
				HttpServletResponse response, ActionMapping mapping) {
						 ActionForm instance = RequestUtils.createActionForm( request, 
						 mapping, moduleConfig, servlet);**
						 }

一個靜態方法,該方法的返回值就是一個表單Bean 物件 ActionForm

---------------------------------- createActionForm( request, mapping, moduleConfig, servlet) **

public static ActionForm createActionForm(HttpServletRequest request,
			ActionMapping mapping,ModuleConfig moduleConfig,ActionServlet servlet) {
		   String attribute = mapping.getAttribute();
		   if (attribute == null) { return (null); } // 先找 attribute 屬性值,如果沒有  
		   再去找 name String name = mapping.getName(); 
		   	// 從 <action> 標籤中取得 name="loginForm" 
		   	FormBeanConfig config = moduleConfig.findFormBeanConfig(name);
			ActionForm instance = lookupActionForm(request, attribute, 	
			mapping.getScope());
			return createActionForm(config, servlet); } // 通過反射創建出 ActionForm

**rm 物件並返回給呼叫方法 process() ---------------------------------- findFormBeanConfig(name)  從 ModuleConfig 裡找 formBeans 集合,通過 name=“loginForm” 與集合中的 key 相匹配,取得 FormBeanConfig 物件  key = “loginForm”,value = FormBeanConfig 物件 ( config ),返回物件給呼叫方法 processActionForm()

---------------------------------- lookupActionForm(request, attribute, mapping.getScope()) if (“request”.equals(scope)) { instance = (ActionForm) request.getAttribute(attribute); } else { session = request.getSession(); instance = (ActionForm) session.getAttribute(attribute); } return (instance); }  從 request / session 內建物件中看有沒有 ActionForm 物件,如果有就直接返回這個物件給呼叫方法 processActionForm()  如果 scope = session,只會存在一個 ActionForm

收集資料到 ActionForm

•processPopulate(request, response, form, mapping) protected void processPopulate( HttpServletRequest request, HttpServletResponse response, ActionForm form,ActionMapping mapping) throws ServletException { if (form == null) { return; } // 先判斷 ActionForm 是不是一個空物件 form.reset(mapping, request); // 如果複寫了表單Bean 的 reset() 方法,先對錶單Bean 進行初始化 // 真正的收集方法,向方法中注入表單Bean和request 內建物件RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(), request); ---------------------------------- populate(form, mapping.getPrefix(), mapping.getSuffix(), request) public static void populate( Object bean, String prefix, String suffix, HttpServletRequest request) throws ServletException { HashMap properties = new HashMap(); // Map 集合,存放最終的收集後的屬性 Enumeration names = null; // Enumeration 集合,自身迭代收集所有的 request 中的屬性 // 如果不是上傳,建立一個 Enumeration 集合物件存放 request 中所有的引數名,Multipart 上傳的標記if (!isMultipart) { names = request.getParameterNames(); } while (names.hasMoreElements()) { // 看檔案位置指標下有沒有元素 String name = (String) names.nextElement(); // 第一個引數名 username String stripped = name; } // stripped 裡的值就是集合中的第一個元素,也是 username Object parameterValue = request.getParameterValues(name); // 將通過引數名找到的引數值存放在 parameterValue 中 properties.put(stripped, parameterValue); // stripped 為 key, 對應的屬性值為 value 即:將 request 中的引數放在一個名為 properties 的Map 集合中 – key 為引數名,value 為引數值BeanUtils.populate(bean, properties); // 將 Map 裡的引數名引數值,放在 ActionForm 中 ---------------------------------- populate(bean, properties) public static void populate(Object bean, Map properties) throws IllegalAccessException, InvocationTargetException { // 迭代所有的引數名,將Map 裡的所有引數名放在Set集合中後放在 Iterator 集合中,並迭代 Iterator names = properties.keySet().iterator(); while (names.hasNext()) { String name = (String) names.next(); if (name == null) { continue; } Object value = properties.get(name); // 通過引數名取得相應的引數值並存放 setProperty(bean, name, value); // 呼叫 setProperty 方法,相當於 bean.setName(value); 將資料封裝到 ActionForm 裡

•過程描述: ○從表單中獲取所有的引數名放在Enumeration 集合中 ○迭代 Enumeration 取得所有的元素 ○從 request 中取得各元素對應的引數值 ○將表單中的引數名和引數值分別放在 Map 集合中 ○呼叫BeanUtils.populate(bean, properties); 靜態方法,傳入表單Bean 和 Map 集合 ○在靜態方法中呼叫 setProperty( bean,name,value) 方法,相當於 bean.setName( value ) – 資料封裝到了 ActionForm 中

例項化 Action

---------------------------------- processActionCreate(request, response, mapping) protected HashMap actions = new HashMap(); protected Action processActionCreate( HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException { String className = mapping.getType(); // 取得 Action 的類名 type = “com.struts.LoginAction” synchronized (actions) { // 用一個 Map 物件來做一個同步鎖,同一時刻只有一個執行緒可以拿到 Action 物件 [ 用 Map 實現單例 ]

// 從 actions 集合中通過 key 判斷有沒有已存在的 Action 物件,如果有就不建立物件,沒有就創建出 Action 物件 instance = (Action) actions.get(className); if (instance != null) { return (instance); } } instance = (Action) RequestUtils.applicationInstance(className); // 通過反射建立 Action 物件 // 將通過反射創建出的 Action 物件放在 Map 集合中,此時的單例是 Map 集合的單例 [ 不是飢漢模式也不是飽漢模式 ] actions.put(className, instance); // key = “LoginAction”,value = Action 物件 return (instance) ; } ---------------------------------- applicationInstance(className) return (applicationClass(className).newInstance());

執行 Action 中的 execute() 方法

---------------------------------- processActionPerform(request, response, action, form, mapping) protected ActionForward processActionPerform( HttpServletRequest request, HttpServletResponse response, Action action, ActionForm form, ActionMapping mapping) throws IOException, ServletException { try { return (action.execute(mapping, form, request, response)); }catch (Exception e) { // 拋給 struts 一個異常 return ( processException(request, response, e, form, mapping)); } } ○返回給 struts 一個 ActionForward 物件 [ 封裝 name / path 跳轉路徑 / redirect 跳轉方式]

轉向

---------------------------------- processForwardConfig(request, response, forward) protected void processForwardConfig( HttpServletRequest request, HttpServletResponse response, ForwardConfig forward) throws IOException, ServletException { String forwardPath = forward.getPath(); // 取得跳轉路徑 // 如果跳轉方式是 false – 服務端跳轉,為true – 客戶端跳轉 if (forward.getRedirect()) { if (uri.startsWith("/")) { uri = request.getContextPath() + uri; } response.sendRedirect(response.encodeRedirectURL(uri)); } else { doForward(uri, request, response); } // 服務端跳轉