完整JavaWeb專案筆記 第五部分-通過方法控制代碼實現動態方法呼叫
阿新 • • 發佈:2018-12-31
文章目錄
一 方法控制代碼
方法控制代碼是Java7的JSR 292版本中新增的功能,我簡單介紹下。方法控制代碼和反射是較為類似的,但是兩者的使用場景有所卻別,相對而言反射速度慢,安全性差,但是使用更簡單,而方法控制代碼執行速度快,但使用上也較為麻煩。
當我設想通過的一個通用的Servlet來將所有請求進行分發的時候,我首先想到了這個機制,作為Web專案,服務端的響應速度還是很重要的,所以我沒有選擇通過過濾器或者其他設計方案,僅僅是因為這個輕量級的方法控制代碼可以滿足我當下的所有需求。
二 如何使用方法控制代碼
還是做一個簡單的樣例,最後再將如何整合到我們的核心Servlet中。
方法控制代碼和反射的Method類似,我們想一下Method的invoke是如何工作的?需要確定方法歸屬型別,需要確定方法簽名,有可能需要確定方法的返回型別,當然還有方法的訪問修飾符。所以簡單介紹下方法控制代碼執行流程,更為具體的介紹請自行參考官方文件:
- 建立MethodType物件,指定方法的簽名;
- 在MethodHandles.Lookup中查詢型別為MethodType的MethodHandle;
- 傳入方法引數並呼叫MethodHandle.invoke或者MethodHandle.invokeExact方法。
更為直觀的看一下測試程式碼吧:
package com.bubbling.test;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodHandles.Lookup;
import java.lang.invoke.MethodType;
public class Test
{
public static void main(String[] args)
{
Lookup lookup = MethodHandles. lookup();
/*
* 獲得方法控制代碼通過java.lang.invoke.MethodHandles.Lookup類來完成
* findConstructor就是查詢構造器的
* findVirtual就是查詢一般函式的(同invokeVirtual)
* findStatic 就是查詢靜態方法的(同invokeStatic)
* findSpecial查詢私有方法的
* 獲取屬性的話通過findGetter或者fingStaticGetter就可以了
*/
MethodHandle methodHandler;
try
{
methodHandler = lookup.findVirtual(A.class, "test1", MethodType.methodType(void.class));
methodHandler.invoke(new A());
methodHandler = lookup.findStatic(A.class, "test2", MethodType.methodType(String.class));
String str = (String) methodHandler.invoke();
System.out.println(str);
}
catch (NoSuchMethodException e)
{
e.printStackTrace();
}
catch (IllegalAccessException e)
{
e.printStackTrace();
}
catch (Throwable e)
{
e.printStackTrace();
}
}
}
class A extends Test
{
public void test1()
{
System.out.println(1);
}
public static String test2()
{
return 2 + "";
}
}
上例的執行輸出如下:
這裡沒有列舉其他詳細方法,有興趣的朋友可以參閱API手冊,裡面有詳細的說明。
三 核心Servlet的大致框架構建
按照之前的設計思路,我們需要將所有請求通過核心Servlet分發到具體的Servlet處理類中,其實現原理就是第一小節提到的方法控制代碼,那麼我們需要進行一些約定設計了:
- 所有請求均通過IServlet處理;
- 所有Servlet均派生自IServlet;
- IServlet實現類獲取請求、應答物件及請求引數等操作均需要通過IServlet提供的介面處理;
- IServet實現類的每一個方法應對一個請求的處理過程;
- IServelt實現類的每一個方法宣告都為pubilc void,且方法引數列表為空
所以IServlet的大致結構如下:
package com.bubbling.servlet.base;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import com.bubbling.common.Constant;
import com.bubbling.util.GsonUtil;
import com.google.gson.Gson;
/**
* @author 胡楠
*
* 所有Servlet均需要自該類派生,並且需實現處理請求對映的獲取方法,派生類的所有方法訪問型別按規範必須宣告為public,
* 且返回值為void
*
*/
public abstract class IServlet extends HttpServlet
{
private static final long serialVersionUID = 1L;
private HttpServletRequest request;
private HttpServletResponse response;
private HttpSession session;
private IResponse result = new IResponse();
public IResponse getWebResponse()
{
return result;
}
public HttpServletRequest getRequest()
{
return request;
}
public HttpServletResponse getResponse()
{
return response;
}
public HttpSession getSession()
{
return session;
}
public String getParam(String name)
{
return request.getParameter(name);
}
/**
* @return kEY值為請求引數action值,VALUE值為處理該請求的方法名
*/
protected abstract Map<String, String> getMethodMap();
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
doPost(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
this.request = request;
this.response = response;
this.session = request.getSession(true);
if (this.result == null)
{
this.result = new IResponse();
}
process();
}
private void process() throws IOException
{
}
private void processAction() throws Throwable
{
Map<String, String> methodMap = getMethodMap();
String action = request.getParameter("action");
if (!methodMap.containsKey(action))
{
setWebResponseInvalidAction();
}
else
{
MethodHandles.lookup().findVirtual(getClass(), methodMap.get(action), MethodType.methodType(void.class))
.invoke(this);
}
}
}