使用普通類簡單模擬Servlet
需求
通過使用普通類+過濾器來模擬實現前端頁面向後端發送請求時Servlet的工作
步驟
配置訪問路徑xml
爲了能夠靈活的匹配到用戶想要訪問的路徑,我們將自定義的資源類的類名、資源類的完全限定名以及默認訪問的方法,保存到路徑映射文件中。之後再解析此配置xml文件。
我這裏簡單模擬了兩個資源類,怎麼樣看這裏是不是有種似曾相識的感覺。
解析配置文件,結果放入map
自定義過濾器類,並在web.xml中配置我們的過濾器,攔截所有的請求
當服務器啓動加載時,會加載過濾器,在過濾器的初始化方法中解析我們的訪問配置文件:這裏使用的是dom4j+Xpath解析,
public void init(FilterConfig arg0) throws ServletException {
/**
* 爲了靈活的獲取到用戶想要訪問的類,配置xml文件,配置路徑名、類完全限定名、默認訪問方法
* 在服務器加載、過濾器初始化時就解析配置文件
*
* 使用dom4j解析xml
* 用一個對象存儲解析出的name、class、method等屬性的值
* 使用map存儲對象
*/
try {
SAXReader reader = new SAXReader();
//使用類加載器以輸入流形式將配置文件加載
InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("config.xml");
Document read = reader.read(stream);
//使用Xpath獲取bean節點 '//bean'找到文檔中所有bean節點,無視層級
//使用Xpath要導入jar包 jaxen
List<Element> list = read.selectNodes("//bean");
//遍歷元素集合
for (Element element : list) {
Config config = new Config();
//獲取到要訪問的類
String name = element.attributeValue("name");
String className = element.attributeValue("class");
String method = element.attributeValue("method");
config.setName(name);
config.setClassName(className);
config.setMethod(method);
**hashMap.put(name, config);** //將要訪問的資源名作爲key,解析結果封裝的對象作爲value裝入一個map中
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
將解析結果封裝到一個對象之中,這裏我自定義了一個用於封裝的對象,並且將要訪問的資源名作爲key,解析結果封裝的對象作爲value裝入一個map中。我自定義的資源類其字段有 類名,類完全限定名,類方法名,類具體結構如下:
public class Config {
//類名
private String name;
//類的完全限定名,用於反射創建對象
private String className;
//類中的方法名
private String method;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
@Override
public String toString() {
return "Config [name=" + name + ", className=" + className + ", method=" + method + "]";
}
}
訪問攔截處理
當用戶在前端發送請求之後,過濾器攔截到請求,在過濾器的過濾方法中,我們先將請求對象從ServletRequest轉爲HttpServletRequest因爲我們要使用這個Http請求對象的getRequestURI方法來獲取到用戶的請求路徑,通過字符串操作將路徑中用戶想要訪問的資源名給提取出來。這裏就只用遍歷我們之前在過濾器初始化方法中使用的map中是否存在用戶想要訪問的資源
- 如果不存在就直接放行
- 如果存在:通過資源名在map中拿到該資源名對應的資源類的完全限定名,通過完全限定名創建對象,之後我們再從map中拿到方法名,通過之前創建的對象執行該方法完成對該資源類的訪問
過濾器過濾方法:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
try {
//將請求對象轉爲httpServletRequest對象
HttpServletRequest request = (HttpServletRequest) req;
//獲取請求路徑
String uri = request.getRequestURI();
int index = uri.lastIndexOf("/");
//得到訪問的類
String name = uri.substring(index+1);
//請求到map中對應的name
if (hashMap.containsKey(name)) {
//拿到對應name的完全限定名
Config c = (Config) hashMap.get(name);
String className = c.getClassName();
//通過字節碼對象使用完全限定名創建對象
Class<?> clazz = Class.forName(className);
Object obj = clazz.newInstance();
//獲得方法
Method method = clazz.getMethod(c.getMethod());
method.invoke(obj);
}else {
//如果map中沒有對應的路徑,就放行
chain.doFilter(req, resp);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}