上一次咱們介紹了session的實現,使web服務器(如今總以爲準確來講應該叫可獨立部署的web框架,稱不上服務器)具有了基本功能,可是仔細一想就會發現一個嚴重的問題:每當實現一個新的controller,那麼就須要在invokController方法裏邊增長判斷,以便url可以找到對應controller。對於一個web服務器(或是框架)而言,內部應該是封閉的,即便是爲了支持拓展也應該是提供外部接口實現,經過改代碼來修改,絕對是極其糟糕的設計。下邊咱們來看幾種實現方式: html
package org.eh.core.annotation; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * Controller註解 * @author guojing * @date 2014-3-5 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE) @Documented public @interface Controller { /** * controller名,暫時無用 */ public String name(); /** * 對應url請求路徑,如htp://127.0.0.1/test/list.do 則對應 controller爲:/test/,對應方法爲:list */ public String url(); }
/** * 方法映射註解 * @author guojing * @date 2014-3-13 */ @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) @Documented public @interface RequestMapping { }定義完了註解,再來實現一個 AnnocationHandler處理註解信息 ,該類主要實現三個功能:1. 獲取指定包下的全部類名(包含包名),2. 將全部註解Controller加入Constants.UrlClassMap,3. 獲取類的指定方法 ,其實現以下:
/** * 註解處理類 * @author guojing * @date 2014-3-5 */ public class AnnocationHandler { /** * 將全部註解Controller加入Constants.UrlClassMap * @param parkage 類名(包含包路徑) */ @SuppressWarnings({ "unchecked", "rawtypes" }) public void paserControllerAnnocation(String parkage) throws ClassNotFoundException { List<String> classlist = getPkgClass(parkage); for (String str : classlist) { Class c = Class.forName(str); if (c.isAnnotationPresent(Controller.class)) { Controller desc = (Controller) c.getAnnotation(Controller.class); Constants.UrlClassMap.put(desc.url(), str); } } } /** * 獲取指定包下的全部類名(包含包名) * @param parkage 指定包名 * @return */ public List<String> getPkgClass(String parkage) { String path = Constants.CLASS_PATH + parkage.replace(".", "/") + "/"; List<String> list = new ArrayList<String>(); File file = new File(path); for (String str : file.list()) { if (str.endsWith(".class")) { list.add(parkage + "." + str.replace(".class", "")); } else if (str.indexOf(".") == -1) { list.addAll(getPkgClass(parkage + "." + str)); } } return list; } /** * 獲取類的指定方法 * @param c * @param methodName */ @SuppressWarnings({ "unchecked", "rawtypes" }) public Method getMethod(Class c, String methodName) throws NoSuchMethodException, SecurityException { Method method = c.getMethod(methodName, Map.class); return method.isAnnotationPresent(RequestMapping.class) ? method : null; } }而後要在 EHHttpHandler的 invokController方法中經過反射( java反射 )調用方法,以下:
/** * 調用對應Controller處理業務 */ private ResultInfo invokController(HttpExchange httpExchange) { String path = httpExchange.getRequestURI().getPath(); String classPath = Constants.UrlClassMap.get(path.substring(0, path.lastIndexOf("/") + 1)); if (classPath == null || classPath.length() == 0) { return null; } Class controllerClass = Class.forName(classPath); Controller controller = (Controller) controllerClass.newInstance(); String methodName = path.substring(path.lastIndexOf("/") + 1, path.lastIndexOf(".")); // 經過反射獲取對應方法 AnnocationHandler annocationHandler = new AnnocationHandler(); Method method = annocationHandler .getMethod(controllerClass, methodName); Map<String, Object> map = null; // 參數 map = analysisParms(httpExchange); // 設置session HttpSession httpSession = ApplicationContext.getApplicationContext() .getSession(httpExchange); map.put("session", httpSession); return (ResultInfo) method.invoke(controller, new Object[] { map }); }最後還要在啓動時加載配置信息,在 EHServer的 startServer中添加
// 加載註解配置的controller AnnocationHandler annocationHandler = new AnnocationHandler(); try { annocationHandler .paserControllerAnnocation("org.eh.core.web.controller"); } catch (Exception e) { log.error("加載controller配置出錯!", e); return; }至此,這次功能完成。
/** * 主頁對應的contoller * @author guojing */ @org.eh.core.annotation.Controller(name = "session", url = "/session/") public class IndexController implements Controller{ @RequestMapping public ResultInfo process(Map<String, Object> map){ ResultInfo result =new ResultInfo(); // 這裏咱們判斷請求中是否有name參數,若是有則放入session,沒有則從session中取出name放入map HttpSession session = (HttpSession) map.get("session"); if (map.get("name") != null) { Object name = map.get("name"); session.addAttribute("name", name); } else { Object name = session.getAttribute("name"); if (name != null) { map.put("name", name); } } result.setView("index"); result.setResultMap(map); return result; } }而後瀏覽器打開 http://localhost:8899/session/process.do?name=guojing,結果又看到這個熟悉的頁面了,說明一切ok,你也能夠多寫幾個controller,直接加入註解就能訪問,不用改其餘任何代碼 。