JavaWeb之搭建自己的MVC框架
. 介紹
MVC全名是Model View Controller,是模型(model)-檢視(view)-控制器(controller)的縮寫,一種軟體設計典範,用一種業務邏輯、資料、介面顯示分離的方法組織程式碼,將業務邏輯聚集到一個部件裡面,在改進和個性化定製介面及使用者互動的同時,不需要重新編寫業務邏輯。MVC被獨特的發展起來用於對映傳統的輸入、處理和輸出功能在一個邏輯的圖形化使用者介面的結構中。
我們今天就來搭建一個自己的從URL訪問到JAVA控制器的簡單框架。
2. 準備
首先,我們應該對JAVA的反射機制、Servlet最基本原理、以及JAVA註解有所瞭解。不瞭解的讀著可先學習:
3. 實現思路
- 首先,我們要將URL訪問請求和我們的後臺JAVA方法形成一一對應關係。
- 然後,我們需要在tomcat容器啟動網站載入的時候獲取上一步驟中記錄的一一對應關係,並記錄下來。
- 最後,完成URL訪問到後臺JAVA方法的跳轉。
4. 實現方式:
(1)通過註解來標記出URL到後臺JAVA方法的對映關係。
A. 定義註解:這個註解可以標註在類和方法上。
package com.mvc.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; @Target({ElementType.METHOD, ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface URLMapping { public String url() default ""; }
B. JAVA後臺中針對URI的相應方法:
package com.mvc.controller; import com.mvc.annotation.URLMapping; @URLMapping(url="/Say") public class SayController{ @URLMapping(url="/Hello") public String SayHello(){ System.out.println("Hello"); return "Hello"; } @URLMapping(url="/Hi") public String SayHi(){ System.out.println("Hi"); return "Hi"; } }
通過註解,我們可以看出我們擬定將來用SayController這個類來相應/Say/***的URL。其中SayHello方法相應的URL是/Say/Hello,SayHi方法相應的URL是/Say/Hi。
這樣,我們就定義好了URL和JAVA後臺方法的對應關係。接下來我們要實現如果完成這種對應關係的跳轉
。
(2)實現URL到JAVA後臺方法的跳轉
A. 定義我們用來儲存URL到JAVA後臺方法對應關係的基礎資料型別:
package com.mvc.base; public class MVCBase { private String url; //我們將來要訪問的URL private String controller; //這個URL對應的後臺JAVA類 private String method; //這個URL對應的後臺方法名稱 public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getController() { return controller; } public void setController(String controller) { this.controller = controller; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } }
B. 通過監聽器在tomcat啟動的時候收集URL到JAVA後臺方法的對應關係並存儲起來:
package com.mvc.listener; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import com.mvc.annotation.URLMapping; import com.mvc.base.MVCBase; public class UrlMappingCollection implements ServletContextListener { //被註解了URLMapper的類方法列表 private static List<MVCBase> mvcBases; //我們要掃描的Controller列表 private final String[] controllerList = {"com.mvc.controller.SayController"}; @Override public void contextDestroyed(ServletContextEvent sce) { // TODO Auto-generated method stub } @Override public void contextInitialized(ServletContextEvent sce) { mvcBases = new ArrayList<MVCBase>(); try { //迴圈所有需要掃描的Controller for (int i = 0; i < controllerList.length; i++) { String controllerName = controllerList[i]; String classURL = ""; String methodURL = ""; Class<?> clazz = Class.forName(controllerName); //獲取Controller類 if (clazz.isAnnotationPresent(URLMapping.class)) { //class被標記了URLMapping註解 classURL = ((URLMapping) clazz.getAnnotation(URLMapping.class)).url(); } //獲取method列表 Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(URLMapping.class)) {
//method被標記了URLMapping註解 methodURL = ((URLMapping) method.getAnnotation(URLMapping.class)).url(); MVCBase mvcBase = new MVCBase(); mvcBase.setUrl(classURL+methodURL); mvcBase.setController(controllerName); mvcBase.setMethod(method.getName()); mvcBases.add(mvcBase); } } } } catch (Exception e) { } } public static List<MVCBase> getMvcBases() { return mvcBases; } }
-
- mvcBases就是將來我們要將URL到JAVA後臺方法對應關係儲存到的地方。我們將他定義為靜態方法,以便後續我們直接訪問獲取。
- controllerList是用來告訴監聽器,我們要從哪些類中獲取URL到JAVA後臺方法的對應關係。因為在程式設計期我們就已經知道了,所以定義為final屬性。
- 然後在監聽器初始化的時候,迴圈controllerList中的class,根據註解的資訊來收集URL到JAVA後臺方法的對應關係並存儲到mvcBases中。
C. 配置Servlet訪問:
首先我們要準備我們的Servlet類,將來所有的URL請求都要跳轉到這個Servlet中。
package com.mvc.servlet; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.mvc.base.MVCBase; import com.mvc.listener.UrlMappingCollection; public class ServletCenter extends HttpServlet { private static final long serialVersionUID = -1163369749078348562L; private void doTransfer(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException, ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException,
InstantiationException { for (MVCBase mvcBase : UrlMappingCollection.getMvcBases()) { if (req.getRequestURI().equals(
req.getServletContext().getContextPath()+mvcBase.getUrl())) { Class<?> clazz = Class.forName(mvcBase.getController()); Method method = clazz.getMethod(mvcBase.getMethod()); method.invoke(clazz.newInstance()); } } } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException { try { doTransfer(req, resp); } catch (Exception e) { } } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException { try { doTransfer(req, resp); } catch (Exception e) { } } }
可以看到,doGet和doPost裡面都執行的是doTransfer方法。相應的web.xml配置為:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> <listener> <listener-class>com.mvc.listener.UrlMappingCollection</listener-class> </listener> <servlet> <servlet-name>main</servlet-name> <servlet-class>com.mvc.servlet.ServletCenter</servlet-class> </servlet> <servlet-mapping> <servlet-name>main</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
在web.xml中,我們將所有的請求都跳轉到com.mvc.servlet.ServletCenter中。