Java筆記-反射機制(三)-小demo

凱倫說_美團點評發表於2017-12-09

經過前面幾次反射機制的學習,這次用反射的知識寫一個類似於Struts框架處理機制的小demo。

Servlet 和 Sturts

在引入反射知識前,先簡單介紹下Sturts框架和Servlet。 在沒有一些Web框架之前,當我們要寫Java Web應用使用的就是Servlet。一個簡單的Servletdemo就是如下所示。

public class HelloWorld extends HttpServlet {
 
   private String message;

   public void init() throws ServletException {
      message = "Hello World";
   }

   public void doGet(HttpServletRequest request, HttpServletResponse response)
      throws ServletException, IOException {
      response.setContentType("text/html");
      PrintWriter out = response.getWriter();
      out.println("<h1>" + message + "</h1>");
   }

   public void destroy() {
   }
}
複製程式碼

servlet會提供出來doGet和doPost,同時接收使用者傳入的引數,進行業務處理,再返回檢視。那麼Servlet如何與URL對應起來呢,答案就是在web.xml,繫結servlet和url之間的對映關係。

<servlet>
   <servlet-name>HelloWorld</servlet-name>
   <servlet-class>HelloWorld</servlet-class>
</servlet>

<servlet-mapping>
   <servlet-name>HelloWorld</servlet-name>
   <url-pattern>/HelloWorld</url-pattern>
</servlet-mapping>
複製程式碼

對映、業務邏輯處理、檢視返回全部在servlet中完成,耦合度比較高,隨著url的增多,servlet會越來越多,需要在web.xml配置很多對映關係,不利於維護。同時servlet的入參以及返回的引數很依賴於當前執行的容器,本身也是執行緒不安全的,當入參非常多時,需要多次呼叫getParm方法,程式碼很冗餘。 之後Struts框架誕生,通過統一的ActionServlet處理具體的url請求和引數對映以及根據不同的返回結果跳轉不同的檢視,開發者只需要關心自己的業務邏輯,就可以實現web應用的開發。具體的Struts的配置檔案,大致如下面XML所示。

<?xml version="1.0" encoding="UTF-8"?>
<struts>
    <action name="login" class="com.coderising.kailuncen.LoginAction">
        <result name="success">/jsp/homepage.jsp</result>
        <result name="fail">/jsp/showLogin.jsp</result>
    </action>
    <action name="logout" class="com.coderising.kailuncen.LogoutAction">
    	<result name="success">/jsp/welcome.jsp</result>
    	<result name="error">/jsp/error.jsp</result>
    </action>
</struts>
複製程式碼

我們只需要分別實現檢視和業務邏輯,再通過struts將他們繫結起來,就可以完成開發工作,也更便於理解,方便維護。有興趣的讀者可以自行深入學習下servlet和struts的思想。

小demo

我想寫的小demo就是利用讀取xml,利用反射載入不同的action,進行業務邏輯處理,最後輸出返回的檢視,整個邏輯思路還是比較簡單,純當反射學習的練手。 首先是定義配置類,把xml中action對應的對映關係儲存下來

private class ActionConfig {
		private String name;
		private String className;
		private Map<String, String> viewResult = new HashMap<>();
複製程式碼

當初始化讀取xml完畢後,得到如下結構,action的名字對應著具體的action配置

Map<String, ActionConfig> actionConfigMap = new HashMap<>();
複製程式碼

模擬Struts ActionServlet的運作方式

public View runAction(String actionName, Map<String, String> params) {
		String className = cfg.getClassName(actionName);
		if (className == null) {
			return null;
		}
		try {
			Class<?> clz = Class.forName(className);
			Object action = clz.newInstance();
			ReflectionUtil.invokeSetMethods(action, params);
			String resultName = (String) clz.getDeclaredMethod("execute").invoke(action);
			Map<String, Object> result = ReflectionUtil.invokeGetMethods(action);
			String resultView = cfg.getViewResult(actionName, resultName);
			return new View(resultView, result);
複製程式碼

通過actionName從配置類中拿到具體的執行類的全類名,其實Struts框架就是直接解析url,然後對應到xml配置的對應action名稱,將url和具體的執行類繫結在一起。 之後是使用Class.forName建立類型別,然後建立對應的例項。ReflectionUtil裡面做的事情就是,先獲取action中對應的field的Name,然後從變數中,根據filed名稱找對應的值,然後使用set方法對action的field進行賦值操作,就是LoginAction中的相關資訊。

public class LoginAction {
	private String name;
	private String password;
	private String message;
複製程式碼

這一步就省去了使用servlet時,重複去get賦值的繁瑣操作,利用反射機制,直接對成員變數進行賦值,開發者只需要將前端會傳入的引數名稱和後端類中的名稱做好事先的確認即可。 然後就是通過反射呼叫execute方法,使用了Method.invoke方法。再次使用反射獲取field的最新值,組成map返回,同時根據方法的返回值,去actionConfigMap中獲取對應的view。 最後根據field的返回值map和view的名稱組成最終展示的檢視。

結尾

以上其實就是根據反射知識模仿的struts核心執行流程的小demo,整個web框架處理了非常多的其他的事情比如引數對映,安全,Json處理等,如果有興趣,可以進一步做學習。

相關文章