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方法收集到一个类中,这样所有控制器的异常就能在一个地方进行一致的处理。