知識庫 / Spring / Spring MVC RSS 訂閱

Spring DispatcherServlet 入門指南

Architecture,Spring MVC
HongKong
9
02:34 PM · Dec 06 ,2025

1. 引言

簡單來説,在 前端控制器設計模式中,單個控制器負責將傳入的 HTTP 請求引導到應用程序中的所有其他控制器和處理器。

Spring 的 DispatcherServlet 實現了該模式,因此負責正確地協調 HTTP 請求到其正確的處理器。

在本文中,我們將 分析 Spring DispatcherServlet 的請求處理工作流程 以及如何實現參與該工作流程的多個接口。

2. DispatcherServlet 請求處理

本質上,DispatcherServlet 處理傳入的 HttpRequest,並委託請求,以及根據 Spring 應用中配置的 HandlerAdapter 接口來處理該請求,同時伴隨的註解指定了處理器、控制器端點和響應對象。

讓我們更深入地瞭解 DispatcherServlet 如何處理一個組件:

  • DispatcherServlet 關聯的 WebApplicationContext,通過鍵 DispatcherServlet.WEB_APPLICATION_CONTEXT_ATTRIBUTE 搜索並提供給流程中的所有元素
  • DispatcherServlet 查找配置在你的 dispatcher 中所有 HandlerAdapter 接口的實現——每個找到並配置的實現通過 handle() 處理請求,貫穿整個流程
  • LocaleResolver 可選地綁定到請求,以便流程中的元素可以解析區域設置
  • ThemeResolver 可選地綁定到請求,允許元素(如視圖)確定使用哪個主題
  • 如果指定了 MultipartResolver,則檢查請求是否存在 MultipartFiles,任何找到的都包裝在 MultipartHttpServletRequest 中進行進一步處理
  • WebApplicationContext 中聲明的 HandlerExceptionResolver 實現會捕獲在請求處理過程中拋出的異常

你可以在這裏瞭解如何註冊和設置 DispatcherServlet 的所有方法。

3. HandlerAdapter 接口

<em>HandlerAdapter</em> 接口使得通過多個特定接口,控制器、Servlet、<em>HttpRequests</em> 和 HTTP 路徑得以使用。<em>HandlerAdapter</em> 接口因此在 <em>DispatcherServlet</em> 請求處理流程的許多階段中發揮着關鍵作用。

首先,每個 <em>HandlerAdapter</em> 實現都會被放置到你的 Dispatcher 的 <em>getHandler()</em> 方法中。然後,這些實現會 <em>handle()</em> <em>HttpServletRequest</em> 對象,隨着執行鏈的推進。

在後面的部分,我們將更詳細地探討一些最重要的和常用的 <em>HandlerAdapters</em>

3.1. 映射關係

為了理解映射關係,我們首先需要了解如何註解控制器,因為控制器對於 <em >HandlerMapping</em> 接口至關重要。

<em >SimpleControllerHandlerAdapter</em> 允許在不使用 <em >@Controller</em> 註解的情況下明確實現控制器。

<em >RequestMappingHandlerAdapter</em> 支持使用帶有 <em >@RequestMapping</em> 註解的方法。

我們將重點關注 <em >@Controller</em> 註解,但一個包含多個示例,並使用 <em >SimpleControllerHandlerAdapter</em> 的有用的資源也可用。

<em >@RequestMapping</em> 註解定義了處理程序將在與它關聯的 <em >WebApplicationContext</em> 中提供的特定端點。

讓我們來看一個暴露並處理 <em >/user/example</em> 端點的 <em >Controller</em> 的示例:

@Controller
@RequestMapping("/user")
@ResponseBody
public class UserController {
 
    @GetMapping("/example")
    public User fetchUserExample() {
        // ...
    }
}

@RequestMapping 註解指定的路徑,通過 HandlerMapping 接口進行內部管理。

URL 結構與 DispatcherServlet 本身相關聯,並由 Servlet 映射確定。

因此,如果 DispatcherServlet 被映射到 ‘/’,則所有映射都將覆蓋該映射。

但是,如果 Servlet 映射為 ‘/dispatcher‘,則所有 @RequestMapping 註解都將相對於該根 URL 進行處理。

請記住,‘/’ 與 ‘/*’ 是不一樣的! 對於 Servlet 映射,‘/’ 是默認映射,並暴露所有 URL 到 Dispatcher 的責任區域。

‘/*’ 對許多新開發的 Spring 開發者來説都比較困惑。它並不能指定具有相同 URL 上下文的所有路徑都位於 Dispatcher 的責任區域下。相反,它會覆蓋並忽略其他 Dispatcher 映射。因此,‘/example’ 將返回 404 錯誤!

因此,不應該在非常有限的情況下使用 ‘/*’(例如,配置過濾器)。

3.2. HTTP 請求處理

DispatcherServlet 的核心職責是分發接收到的 HttpRequests 到通過 @Controller@RestController 註解指定的正確處理程序。

順便説明的是,@Controller@RestController 之間的主要區別在於響應生成方式——@RestController 默認定義了 @ResponseBody

關於 Spring 控制器更深入的説明,請參閲此處。

3.3. <em>ViewResolver</em> 接口

<em>ViewResolver</em> 是作為 <em>ApplicationContext</em> 對象上的配置設置,附加到 <em>DispatcherServlet</em> 上的。

<em>ViewResolver</em> 確定了分發器所提供的視圖類型以及它們是從何處提供的。

以下是一個我們將放置在我們的 <i >AppConfig</i> 中用於渲染 JSP 頁面的示例:

@Configuration
@EnableWebMvc
@ComponentScan("com.baeldung.springdispatcherservlet")
public class AppConfig implements WebMvcConfigurer {

    @Bean
    public UrlBasedViewResolver viewResolver() {
        UrlBasedViewResolver resolver
          = new UrlBasedViewResolver();
        resolver.setPrefix("/WEB-INF/view/");
        resolver.setSuffix(".jsp");
        resolver.setViewClass(JstlView.class);
        return resolver;
    }
}

非常直觀! 這主要分為三個部分:

  1. 設置前綴,這會設置默認 URL 路徑以查找已設置的視圖
  2. 默認視圖類型,通過後綴設置
  3. 在解析器上設置視圖類,允許諸如 JSTL 或 Tiles 之類的技術與渲染視圖相關聯

一個常見的問題是調度器 ViewResolver 和整個項目目錄結構之間的關係如何。 讓我們先了解一下基本內容。

以下是使用 Spring 的 XML 配置的 InternalViewResolver 的示例路徑配置:

<property name="prefix" value="/jsp/"/>

為了便於我們舉例説明,我們假設我們的應用程序正在託管在:

http://localhost:8080/

這是本地託管的 Apache Tomcat 服務器的默認地址和端口。

假設我們的應用程序名為 dispatcherexample-1.0.0,我們的 JSP 視圖將可以通過以下地址訪問:

http://localhost:8080/dispatcherexample-1.0.0/jsp/

在普通 Spring 項目中使用 Maven 的這些視圖的路徑如下:

src -|
     main -|
            java
            resources
            webapp -|
                    jsp
                    WEB-INF

默認的視圖位置位於 WEB-INF 目錄下。上述片段中 InternalViewResolver 指定的路徑決定了您的視圖將在 ‘src/main/webapp’ 目錄下的哪個子目錄中可用。

3.4. <em>LocaleResolver</em> 接口

通過 LocaleResolver 接口,我們可以自定義 dispatcher 的會話、請求或 Cookie 信息。

CookieLocaleResolver 是一個實現,允許使用 Cookie 配置無狀態應用程序屬性。讓我們將其添加到 AppConfig

@Bean
public CookieLocaleResolver cookieLocaleResolverExample() {
    CookieLocaleResolver localeResolver 
      = new CookieLocaleResolver();
    localeResolver.setDefaultLocale(Locale.ENGLISH);
    localeResolver.setCookieName("locale-cookie-resolver-example");
    localeResolver.setCookieMaxAge(3600);
    return localeResolver;
}

@Bean 
public LocaleResolver sessionLocaleResolver() { 
    SessionLocaleResolver localeResolver = new SessionLocaleResolver(); 
    localeResolver.setDefaultLocale(Locale.US); 
    localResolver.setDefaultTimeZone(TimeZone.getTimeZone("UTC"));
    return localeResolver; 
}

SessionLocaleResolver 允許在狀態化應用程序中進行會話特定的配置。

setDefaultLocale 方法代表地理、政治或文化區域,而 setDefaultTimeZone(時區) 則確定應用程序中特定 時區 Bean 的相關 時區 對象。時區

這兩個方法均可在上述所有 LocaleResolver 實現中找到。

3.5. <em>ThemeResolver</em> 接口

Spring 提供視圖樣式主題功能。

讓我們看看如何配置 Dispatcher 以處理主題。

首先,讓我們設置所有必要的配置,以便查找和使用我們的靜態主題文件。 我們需要為我們的 ThemeSource 設置靜態資源位置,以便配置實際的 ThemesTheme 對象包含來自這些文件中規定的所有配置信息)。 將以下內容添加到 AppConfig

@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
    registry.addResourceHandler("/resources/**")
      .addResourceLocations("/", "/resources/")
      .setCachePeriod(3600)
      .resourceChain(true)
      .addResolver(new PathResourceResolver());
}

@Bean
public ResourceBundleThemeSource themeSource() {
    ResourceBundleThemeSource themeSource
      = new ResourceBundleThemeSource();
    themeSource.setDefaultEncoding("UTF-8");
    themeSource.setBasenamePrefix("themes.");
    return themeSource;
}

DispatcherServlet 管理的請求可以通過傳遞到 ThemeChangeInterceptor 對象的 setParamName() 方法中指定的參數來修改主題。在 AppConfig 中添加:

@Bean
public CookieThemeResolver themeResolver() {
    CookieThemeResolver resolver = new CookieThemeResolver();
    resolver.setDefaultThemeName("example");
    resolver.setCookieName("example-theme-cookie");
    return resolver;
}

@Bean
public ThemeChangeInterceptor themeChangeInterceptor() {
   ThemeChangeInterceptor interceptor
     = new ThemeChangeInterceptor();
   interceptor.setParamName("theme");
   return interceptor;
}

@Override
public void addInterceptors(InterceptorRegistry registry) {
    registry.addInterceptor(themeChangeInterceptor());
}

以下 JSP 標籤已添加到我們的視圖中,以確保正確的樣式顯示:

<link rel="stylesheet" href="${ctx}/<spring:theme code='styleSheet'/>" type="text/css"/>

以下 URL 請求使用“theme”參數通過我們配置的 ThemeChangeIntercepter 渲染 example 主題:

http://localhost:8080/dispatcherexample-1.0.0/?theme=example

3.6. <em>MultipartResolver</em> 接口

<em>MultipartConfigElement</em> 配置用於簡單地配置 <em>MultipartHttpServletRequest</em> 的最大大小,以便在其他元素處理過程中進一步使用,如果找到多部分內容。添加到 <em>AppConfig</em> 中:

@Bean
public MultipartConfigElement multipartConfigElement() {
    MultipartConfigFactory factory = new MultipartConfigFactory();
    factory.setMaxFileSize(DataSize.ofBytes(10000000L));
    factory.setMaxRequestSize(DataSize.ofBytes(10000000L));
    return factory.createMultipartConfig();
}

現在我們已經配置了 MultipartResolver Bean,接下來我們將設置一個控制器來處理 MultipartFile 請求:

@Controller
public class MultipartController {

    @Autowired
    ServletContext context;

    @PostMapping("/upload")
    public ModelAndView FileuploadController(
      @RequestParam("file") MultipartFile file) 
      throws IOException {
        ModelAndView modelAndView = new ModelAndView("index");
        InputStream in = file.getInputStream();
        String path = new File(".").getAbsolutePath();
        FileOutputStream f = new FileOutputStream(
          path.substring(0, path.length()-1)
          + "/uploads/" + file.getOriginalFilename());
        int ch;
        while ((ch = in.read()) != -1) {
            f.write(ch);
        }
        f.flush();
        f.close();
        in.close();
        modelAndView.getModel()
          .put("message", "File uploaded successfully!");
        return modelAndView;
    }
}

我們可以使用標準形式向指定端點提交文件。上傳的文件將在 ‘CATALINA_HOME/bin/uploads’ 目錄下可用。

3.7. <em>HandlerExceptionResolver</em> 接口

Spring 的 <em>HandlerExceptionResolver</em> 提供了一種統一的錯誤處理機制,適用於整個 Web 應用程序、單個控制器或一組控制器。

為了提供應用程序級別的自定義異常處理,請創建一個標註了 <em>@ControllerAdvice</em> 註解的類。

@ControllerAdvice
public class ExampleGlobalExceptionHandler {

    @ExceptionHandler
    @ResponseBody 
    public String handleExampleException(Exception e) {
        // ...
    }
}

該類中標記了 @ExceptionHandler 註解的所有方法將在調度器負責區域內的每個控制器中可用。

HandlerExceptionResolver 接口的實現,在 DispatcherServlet 的 ApplicationContext 中可用,以便 攔截特定的控制器,在調度器負責區域內 當使用 @ExceptionHandler 註解時,並正確地將類作為參數傳遞:

@Controller
public class FooController{

    @ExceptionHandler({ CustomException1.class, CustomException2.class })
    public void handleException() {
        // ...
    }
    // ...
}

handleException() 方法現在將作為 FooController 在我們上面的示例中用於處理異常,如果發生CustomException1CustomException2 異常。

以下是一篇文章深入探討了 Spring Web 應用程序中異常處理的詳細信息。

4. 結論在本教程中,我們回顧了 Spring 的 DispatcherServlet 以及配置它的各種方法。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.