1. 程式人生 > >Spring in Action——SpringMVC起步

Spring in Action——SpringMVC起步

跟蹤Spring MVC請求 每當使用者在Web瀏覽器中點選連結或者提交表單的時候,請求就開始工作了。

對請求的工作描述就像是快遞投送員。請求是一個非常繁忙的傢伙。從離開瀏覽器開始到獲取響應返回,它會經歷好多站。在每站會留下一些資訊的同時,也會帶上其他資訊。 在這裡插入圖片描述

在請求離開瀏覽器時①,會帶有使用者所請求內容的資訊,至少會包含請求的URL。還可能帶有其他資訊,如使用者提交的表單資訊。 請求的第一站是Spring的DispatcherServlet。與大多數基於Java的Web框架一樣,SpringMVC所有請求會通過前端控制器(front controller)Servlet。在這裡一個單例項的Servlet將請求委託給應用程式的其他元件來執行實際的處理。 DispatcherSevlet的任務是將請求傳送給Spring MVC 控制器。控制器controller是用於處理請求的Spring元件。在典型的應用程式中可能有多個控制器。DispatcherServlet需要知道應該將請求傳送給哪個控制器

。所以***DispatcherServlet以會查詢一個或多個處理器對映②來確定請求的下一站在哪裡***。處理器對映會根據請求所攜帶的URL資訊來進行決策。 一旦選擇了合適的控制器,DispatcherServlet會將請求傳送給選中的控制器③。到了控制器,請求會卸下其負載(使用者提交的資訊)並耐心等待控制器處理這些資訊。(實際上,設計良好的控制器本身只會處理很少的工作甚至不處理工作,而是將業務邏輯委託給一個或多個服務物件進行處理。) 控制器在完成邏輯處理後,通常會產生一些資訊,這些資訊需要返回給使用者並在瀏覽器上顯示。這些資訊被成為模型model。不過僅僅返回原始資訊是不夠的,這些資訊需要以HTML的形式返回。所以資訊需要傳送給一個識圖view,通常會是JSP。 控制器所做的最後一件事就是將模型資料打包
,並且表示出用於渲染輸出的識圖名。它接下來會***將請求連通模型和檢視名傳送回DispatcherServlet***④。 這樣控制器就不會與特定的識圖相耦合,傳遞給DispatcherServlet的檢視名並不直接表示某個特定的JSP。實際上,它甚至不能確定識圖就是JSP。它僅僅傳遞了一個邏輯名稱,這個名稱會用來查詢真正的識圖。DispatcherServlet會使用檢視解析器⑤來將檢視名匹配為一個特定的檢視實現。

搭建SpringMVC

DispatcherServlet是Spring MVC的核心。在這裡請求會第一次接觸到框架,它要負責將請求路由到其他的元件之中。 按照傳統的方式,像DispatcherServlet這樣的Servlet會配置在web.xml檔案中,這個檔案會放到應用的war包裡。當然這是其中的方法之一。 另一個方法是使用Java將DispatcherServlet配置在Servlet容器中,而不會使用web.xml檔案:

package spittr.config;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

public class SpittrWebAppInitializer
    extends AbstractAnnotationConfigDispatcherServletInitializer{
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[] {RootConfig.class};
    }

    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[] {WebConfig.class};
    }

    protected String[] getServletMappings() {
        //將dispathcer對映到“/”
        return new String[]{"/"};
    }
}

這裡需要知道3個方法分別代表什麼: 第一個是getServletMappings(),它會將一個或多個路徑對映到DispatvherServlet上。在本例中,它對映的是"/",表示它會應用預設Servlet。它會處理進入應用的所有請求。 要知道第二個第三個方法,我們還需要重新認識一下AbstractAnnotationConfigDispatcherServletInitializer:

AbstractAnnotationConfigDispatcherServletInitializer會同時建立兩個應用上下文,一個(DispatcherServlet)用於載入包含Web元件的bean,如控制器、檢視解析器以及處理器對映;另一個(ContextLoaderListener)用於載入其他bean,這些bean通常是驅動應用後端的中間層和資料層元件。

第二個方法getServletConfigClasses()會返回帶有@Configuration註解的類將會用來定義DispatcherServlet應用上下文中的bean。 第三個方法getRootConfigClasses()會返回帶有@Configuration註解的類將會用來配置ContextLoaderLisener建立的應用上下文中的bean。

啟動SpringMVC 我們所能建立的最簡單的SpringMVC配置就是一個帶有@EnableWebMvc註解的類,這個類是使用第二個方法getServletConfigClasses()返回的:

package spittr.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@EnableWebMvc
public class WebConfig {
}

雖然這個配置可以執行起來,也的確可以啟用SpringMVC,但是還有不少問題要解決:

  • 沒有配置檢視解析器。如果這樣的話,Spring會預設使用BeanNameViewResolver,這個檢視解析器會查詢ID與檢視名稱匹配的bean,並且查詢bean要實現的View介面,它以這樣的方式來解析檢視。
  • 沒有啟用元件掃描。所以需要顯示宣告。
  • DispatcherServlet會對映為預設的Servlert。這樣的話,它會處理所有的請求,包括對靜態資源的請求(大多數情況下,這可能不是你想要的效果)。

因此,我們需要在WebConfig中加入程式碼彌補以上三個缺點:

package spittr.config;
import ...
@Configuration
@EnableWebMvc//啟用springmvc
@ComponentScan("spittr.web")//掃描spittr.web包
public class WebConfig extends WebMvcConfigurerAdapter{
    @Bean
    public ViewResolver viewResolver(){
        //配置JSP檢視解析器
        InternalResourceViewResolver resolver=
                new InternalResourceViewResolver();
        resolver.setPrefix("/WEB-INF/views");//字首
        resolver.setSuffix(".jsp");//字尾為jsp檔案
        resolver.setExposeContextBeansAsAttributes(true);
        return resolver;
    }
    @Override
    public void configureDefaultServletHandling(
            DefaultServletHandlerConfigurer configurer){
        //配置靜態資源的處理
        configurer.enable();
    }
}

我們加入了@ComponentScan(包名)來查詢元件。稍後你就會看到,我們所編寫的控制器將會帶有@Controller註解,這會使其成為元件掃描時的候選bean。因此,我們不需要在配置類中顯示宣告任何的控制器。 另外除了在程式碼中詳細說明的檢視解析器。還有configureDefaultServletHandling (DefaultServletHandlerConfigurer configurer)方法, 它通過enable(),將對靜態資源的請求轉發的Servlet容器中預設的Servlet上,而不是使用DispatcherServlet本身來處理此類請求。

那麼RootConfig如何配置呢:

package spittr.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;

@Configuration
@ComponentScan(basePackages={"spittr"},
        excludeFilters={
                @Filter(type=FilterType.CUSTOM, value=EnableWebMvc.class)
        })
public class RootConfig {
}