通過上一篇博客:Servlet 的詳解 http://www.cnblogs.com/ysocean/p/6912191.html,我們大致知道了 Servlet 的基本用法。但是稍微分析一下 Servlet 的用法,我們還是發現其存在很多缺點:
①、一個請求對應一個 Servlet,即每一個請求我們都需要在 web.xml 文件中配置映射。如果項目大,請求很多,那麼會造成 web.xml 很大,很難維護。
②、即便在好幾個請求對應一個 Servlet,即在 service 方法中,通過 if--else 語句來判斷執行的代碼塊。那這樣就會造成 service 方法很擁擠。
③、一個項目只存在一個 web.xml 文件,如果一個項目是多人開發,那麼整合代碼開發過程中會有很多問題。不適合團隊開發。
④、Servlet中doGet方法和doPost方法中的兩個參數reqeust,response擁有嚴重的容器依賴性。
⑤、如果頁面上表單中的元素比較複雜,則在Servlet的方法中獲取表單元素的數據比較繁瑣。
⑥、Servlet是單線程的,只要在Servlet中的聲明一個實例變量,那麼該變量在多線程訪問時就會有線程安全問題。
⑦、在Servlet中處理異常,如果Servlet中有N個方法,則這N個方法必須都要try--catch。因為子類拋的異常不能大於父類。
那麼接下來我們用一個例子來解決上面的問題。
1、新建一個 Web 工程,名為 ServletIncreased。並在 web.xml 中配置一個過濾器 ServletFilter,這個過濾器會過濾所有以 .do 結尾的 URL 鏈接
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <filter> <filter-name>ServletFilter</filter-name> <filter-class>com.ys.filter.ServletFilter</filter-class> </filter> <filter-mapping> <filter-name>ServletFilter</filter-name> <url-pattern>*.do</url-pattern> </filter-mapping> </web-app>
2、創建一個 UserServlet,裡面有兩個方法,insert和update方法,調用 insert 方法會跳轉到 insert.jsp 頁面,調用 update 方法會調轉到 update.jsp 頁面
package com.ys.servlet; import java.io.IOException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class UserServlet { //用戶插入方法 public void insert(HttpServletRequest req,HttpServletResponse resp) throws Exception, IOException{ req.getRequestDispatcher("insert.jsp").forward(req, resp); } //用戶更新方法 public void update(HttpServletRequest req,HttpServletResponse resp) throws Exception, IOException{ req.getRequestDispatcher("update.jsp").forward(req, resp); } }
3、創建一個配置文件類,裡面存放配置文件的關係,通過一個 Map 集合,保存 Servlet 的類名和全類名
package com.ys.config; import java.util.HashMap; import java.util.Map; public class ServletNameConfig { //定義一個 Servlet 配置文件,Map<key,value> //key:表示 Servlet 的類名 //value:表示 Servlet 類名的全稱 public static Map<String, String> servletMap = new HashMap<>; static { servletMap.put("UserServlet", "com.ys.servlet.UserServlet"); } }
4、回頭看我們配置的過濾器,ServletFilter
package com.ys.filter; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.ys.config.ServletNameConfig; public class ServletFilter implements Filter{ @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse resp = (HttpServletResponse) response; String reqURL = req.getRequestURI; // /ServletIncreased/UserServlet.do String strs = reqURL.split("/"); //定義 Servlet 的全類名 String servletAllName = null; if(strs[2] != null){ //得到 請求路徑的 servlet 類名 String servletName = strs[2].substring(0, strs[2].indexOf(".")); //根據獲取的 Servlet 類名,由配置文件 ServletNameConfig 裡面的map 得到 全類名 servletAllName = ServletNameConfig.servletMap.get(servletName); } //獲取請求方法名 String methodName = req.getParameter("method"); System.out.println(servletAllName+"---"+methodName); try { //通過反射調用執行方法 Class obj = Class.forName(servletAllName); Method method = obj.getDeclaredMethod (methodName, HttpServletRequest.class,HttpServletResponse.class); method.invoke(obj.newInstance, req,resp); } catch (Exception e) { e.printStackTrace; } } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy { } }
整體的項目結構如下:
然後將整個項目發佈到 tomcat 服務器運行,發佈的方法可以如下:
然後我們在瀏覽器輸入如下鏈接:http://localhost:8080/ServletIncreased/UserServlet.do?method=insert
那麼就會調用 UserServlet 的 insert 方法,進而跳轉到 insert.jsp 頁面
如果我們在瀏覽器輸入如下鏈接:將 insert 改為 update
http://localhost:8080/ServletIncreased/UserServlet.do?method=update
那麼就會調用 UserServlet 的update 方法,進而調轉到 update.jsp 頁面
分析:這個改進主要是配置了一個過濾器,然後通過過濾器的 doFilter 方法,我們可以通過請求路徑獲得請求URL,然後通過字符串的截取方法得到 Servlet 的名稱。通過配置文件保存的 Servlet類名和全類名的對應關係得到全類名;然後利用反射的原理,通過 invoke 方法來動態調用方法。這裡我們並沒有解決上面所有的問題,比如嚴重的容器依賴性我們這裡還有。如果想真正解決,請看下一篇博客:Struts2 詳解