模擬SpringMvc的實現(一)

SpringMvc是一個很優秀的框架,做web開發的基本都會用到。它整個框架的本質其實就是一個servlet。
關於mvc的整體介紹可以參考我之前的另一篇博客
http://blog.csdn.net/z344310362/article/details/51387724

準備環境

需要servlet3.0以上的依賴包。
web容器需要需用支持servlet3.0的(如tomcat7以上)

技術路線

  1. servlet
    mvc的本質是一個servlet,放到mvc的框架裏面,核心就是一個DispatcherServlet類。
    這個類的繼承關係如下:

    DispatcherServlet->FrameworkServlet->HttpServlet->HttpServlet

    從最後的一個HttpServlet中我們可以看到,雖然它層層隱藏,但是最終就是一個servlet。
  2. mvc核心業務
    mvc的核心業務就是servlet的核心,它主要就是:

    • 初始化業務:init()->initStrategies()
      在初始化中mvc主要是在initStrategies() 這個方法裏面完成了9個核心組件的註冊。
    • 請求處理:service()->doDispatch()
      在處理請求中mvc的入口是在doDispatch() 方法裏面的。這個方法中主要是根據請求匹配到對應的handler,進行具體的業務處理。
  3. servlet註冊
    在這個demo中我們用到了servlet3.0以上纔有的一個接口ServletContainerInitializer
    繼承了這個接口後我們容器啓動後會調用這個接口裏面提供的onStartup 方法。這樣我們的一些初始化動作就可以在裏面完成,包括servlet的動態註冊。
    我們的DispatcherServlet也就可以在裏面註冊了(以前註冊一個servlet我們是在web.xml裏面配置的),在這個demo中我們可以摒棄掉web.xml。

具體實現

  1. 結構預覽
    這裏寫圖片描述
    目前只是實現了一個大體的結構,所以很簡單。紅色矩形的2個類就是這個demo的核心類。

  2. MyContainerInitializer介紹

    @HandlesTypes({WebApplicationInitializer.class})
    public class MyContainerInitializer implements ServletContainerInitializer {
    @Override
    public void onStartup(Set<Class<?>> webAppInitializerClasses, ServletContext servletContext) throws ServletException {
        System.out.println("MyContainerInitializer start");
        LinkedList initializers = new LinkedList();
        Iterator var4;
        if(webAppInitializerClasses != null) {
            var4 = webAppInitializerClasses.iterator();
            while(var4.hasNext()) {
                Class initializer = (Class)var4.next();
                if(!initializer.isInterface() && !Modifier.isAbstract(initializer.getModifiers()) && WebApplicationInitializer.class.isAssignableFrom(initializer)) {
                    try {
                        initializers.add((WebApplicationInitializer)initializer.newInstance());
                    } catch (Throwable var7) {
                        throw new ServletException("Failed to instantiate WebApplicationInitializer class", var7);
                    }
                }
            }
            if(initializers.isEmpty()) {
                servletContext.log("No WebApplicationInitializer types detected on classpath");
            } else {
                servletContext.log(initializers.size() + "WebApplicationInitializers detected on classpath");
                var4 = initializers.iterator();
                while(var4.hasNext()) {
                    WebApplicationInitializer initializer1 = (WebApplicationInitializer)var4.next();
                    initializer1.onStartup(servletContext);
                }
    
            }
        }
    }
    }

    這個類實現了ServletContainerInitializer這個接口,所以容器在啓動時會調用裏面的onStartup 方法。
    類聲明的上面有一個@HandlesTypes({WebApplicationInitializer.class})
    它標明瞭WebApplicationInitializer這個類的子類在啓動時會添加到onStartup 方法裏面的一個參數Set<Class<?>> webAppInitializerClasses 裏面。這樣我們通過這個參數就可以對這些類進行調用。

    public interface WebApplicationInitializer {
    void onStartup(ServletContext var1) throws ServletException;
    }
  3. DispatcherServletInitializer介紹
    這個類的主要用途是將DispatcherServlet動態註冊到我們的容器裏面。

    public class DispatcherServletInitializer implements WebApplicationInitializer{
    public static final String DEFAULT_SERVLET_NAME = "dispatcher";
    @Override
    public void onStartup(ServletContext var1) throws ServletException {
        this.registerDispatcherServlet(var1);
    
    }
    protected void registerDispatcherServlet(ServletContext servletContext) {
        ServletRegistration.Dynamic registration = servletContext.addServlet(DEFAULT_SERVLET_NAME, new DispatcherServlet());
        registration.setLoadOnStartup(1);
        registration.addMapping("*.do");
    }
    }

    這個類實現了WebApplicationInitializer 這個接口,所以它在啓動的時候會被添加到2中的MyContainerInitializer 裏面的onstartup裏面。Set<Class<?>>
    這個類裏面用到了ServletRegistration.Dynamic 來動態註冊servlet。

  4. DispatcherServlet介紹
    看似複雜的springmvc被剝光後就剩下一個DispatcherServlet類。

    /**
    * @Author:zhourj
    * @Description:
    * @Date Create in 12:01 2016/11/13 0013
    */
    public class DispatcherServlet extends FrameworkServlet {
    
    protected void onRefresh() {
        this.initStrategies();
    }
    
    /**
     * 初始化MVC的9個組件
     */
    protected void initStrategies() {
        /*this.initMultipartResolver(context);
        this.initLocaleResolver(context);
        this.initThemeResolver(context);
        this.initHandlerMappings(context);
        this.initHandlerAdapters(context);
        this.initHandlerExceptionResolvers(context);
        this.initRequestToViewNameTranslator(context);
        this.initViewResolvers(context);
        this.initFlashMapManager(context);*/
    }
    protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
        this.doDispatch(request,response);
    }
    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("mvc 分配請求");
        PrintWriter writer = null;
        try {
            writer = response.getWriter();
            // response.setContentLength(responseContent.length());
            writer.write("mvc處理結果");
            writer.flush();
            writer.close();
        } catch (Exception e) {
    
        } finally {
            if (writer != null) {
                writer.close();
            }
        }
    }
    }

    目前我基本沒有實現這個類的什麼方法,就是展示他的大體結構。
    從3中的代碼registration.addMapping("*.do"); 我們知道這個servlet將所有”.do”的請求的包含給自己處理了。所有要讓我們的mvc響應到的請求就必需以“.do”結尾。然後具體請求的分配就交給doDispatch 這個方法處理,它具體會利用HandlerMappings組件來幫忙尋找具體的handler。

  5. pom.xml

    <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    </dependencies>
    <build>
    <finalName>test</finalName>
    <resources>
      <resource>
        <directory>src/main/java</directory>
        <excludes>
          <exclude>**/*.java</exclude>
        </excludes>
      </resource>
    </resources>
    </build>

    這裏我只用到了servlet3.1的一個依賴。裏面resource的配置是將根路徑裏面的配置文件拷貝到class裏面。這裏主要是我用IDEA的一個坑,沒自動幫我拷貝進去。
    這裏寫圖片描述
    這裏需要注意到的要有這個META-INF,裏面要有這個一個文件。這樣啓動的時候2中的MyContainerInitializer onstartup方法才能進去。這是servlet中規定的。


本文章主要是個人學習歸納
源代碼下載:http://download.csdn.net/detail/z344310362/9694233

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章