1. 程式人生 > >JavaWeb之搭建自己的MVC框架

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中。

5. 測試

6. 目錄結構

QQ截圖20160323214532