自定義DispatcherServlet配置--非xml配置
AbstractAnnotationConfigDispatcherServletInitializer 類的繼承結構
WebApplicationInitializer > AbstractContextLoaderInitializer > AbstractDispatcherServletInitializer > AbstractAnnotationConfigDispatcherServletInitializer
載入過程:
在Servlet 3.0環境中,容器會在類路徑中查詢實現javax.servlet.ServletContainerInitializer
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方法收集到一個類中,這樣所有控制器的異常就能在一個地方進行一致的處理。