1. 程式人生 > >自定義DispatcherServlet配置--非xml配置

自定義DispatcherServlet配置--非xml配置

AbstractAnnotationConfigDispatcherServletInitializer 類的繼承結構

WebApplicationInitializer > AbstractContextLoaderInitializer > AbstractDispatcherServletInitializer > AbstractAnnotationConfigDispatcherServletInitializer

載入過程:
在Servlet 3.0環境中,容器會在類路徑中查詢實現javax.servlet.ServletContainerInitializer

介面的類,如果能發現的話,就會用它來配置Servlet容器。

Spring提供了這個介面的實現,名為SpringServletContainerInitializer,這個類反過來又會查詢實現WebApplicationInitializer的類並將配置的任務交給它們來完成。Spring3.2引入了一個便利的WebApplicationInitializer基礎實現,也就是AbstractAnnotationConfigDispatcherServletInitializer

所以我們的配置類繼承自AbstractAnnotationConfigDispatcherServletInitializer 來實現配置, 如下

public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

  @Override  //用來配置ContextLoaderListener建立的應用上下文中的bean。
  protected Class<?>[] getRootConfigClasses() {
    return new Class<?>[] { RootConfig.class };
  }

  @Override  //用於定義DispatcherServlet應用上下文中的bean
protected Class<?>[] getServletConfigClasses() { return new Class<?>[] { WebConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } }

DispatcherServlet與ContextLoaderListener 兩個上下文的故事

在Spring Web應用中,通常會建立兩個上下文:DispatcherServle,和另一個由ContextLoaderListener建立的上下文。

我們希望DispatcherServlet載入包含Web元件的bean,如控制器、檢視解析器以及處理器對映,而ContextLoaderListener要載入應用中的其他bean。這些bean通常是驅動應用後端的中間層和資料層元件。

實際上,AbstractAnnotationConfigDispatcherServletInitializer會同時建立DispatcherServlet和ContextLoaderListener。GetServletConfigClasses()方法返回的帶有@Configuration註解的類將會用來定義DispatcherServlet應用上下文中的bean。
getRootConfigClasses()方法返回的帶有@Configuration註解的類將會用來配置ContextLoaderListener建立的應用上下文中的bean。

備註:
如果按照這種方式配置DispatcherServlet,而不是使用web.xml的話,那唯一問題在於它只能部署到支援Servlet 3.0的伺服器中才能正常工作,如Tomcat 7或更高版本。

DispatcherServlet配置

@Configuration
@EnableWebMvc   //啟動Spring MVC
@ComponentScan("spittr.web")  //啟動元件掃描
public class WebConfig extends WebMvcConfigurerAdapter {

  @Bean  //啟動JSP檢視解析器
  public ViewResolver viewResolver() {
    InternalResourceViewResolver resolver = new InternalResourceViewResolver();
    resolver.setPrefix("/WEB-INF/views/");
    resolver.setSuffix(".jsp");
    return resolver;
  }

  //配置靜態資源處理,
  // 我們要求DispatcherServlet將對靜態資源的請求轉發到Servlet容器中預設的Servlet上,
  // 而不是使用DispatcherServlet本身來處理此類請求。
  @Override  
  public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
    configurer.enable();
  }

  @Override
  public void addResourceHandlers(ResourceHandlerRegistry registry) {
    // TODO Auto-generated method stub
    super.addResourceHandlers(registry);
  }

}

異常處理

一, 手都處理
@ResponseStatus(value=HttpStatus.NOT_FOUND, reason="Spittle Not Found")
public class SpittleNotFoundException extends RuntimeException {
}


  @RequestMapping(value="/{spittleId}", method=RequestMethod.GET)
  public String spittle(
      @PathVariable("spittleId") long spittleId, 
      Model model) {
    Spittle spittle = spittleRepository.findOne(spittleId);
    if (spittle == null) {  
     //丟擲異常
      throw new SpittleNotFoundException();
    }
    model.addAttribute(spittle);
    return "spittle";
  }

二. 為Controller單獨處理
  @ExceptionHandler(DuplicateSpittleException.class)
  public String handleNotFound() {
    return "error/duplicate";
  }

三,為控制器新增通知

@ControllerAdvice
public class AppWideExceptionHandler {

  @ExceptionHandler(DuplicateSpittleException.class)
  public String handleNotFound() {
    return "error/duplicate";
  }

}

控制器通知(controller advice)是任意帶有@ControllerAdvice註解的類,這個類會包含一個或多個如下型別的方法:
@ExceptionHandler註解標註的方法;
@InitBinder註解標註的方法;
@ModelAttribute註解標註的方法。

在帶有@ControllerAdvice註解的類中,以上所述的這些方法會運用到整個應用程式所有控制器中帶有@RequestMapping註解的方法上。
@ControllerAdvice註解本身已經使用了@Component,因此@ControllerAdvice註解所標註的類將會自動被元件掃描獲取到,就像帶有@Component註解的類一樣。

@ControllerAdvice最為實用的一個場景就是將所有的@ExceptionHandler方法收集到一個類中,這樣所有控制器的異常就能在一個地方進行一致的處理。