Spring MVC处理流程
- DispatcherServlet收到用户请求
- DS调用处理映射器来找到具体Controller
- DS将请求发送给具体Controller
- Controller返回ViewAndModel给DS
- DS通过视图解析器解析出具体视图位置
- DS用Model来渲染视图
- 被渲染的视图最终响应给用户
Servelet 3规范下的Spring MVC配置
创建继承AbstractAnnotationConfigDispatcherServletInitializer的配置类,通过覆盖该类方法来配置Servlet容器中的类容。
- getServletMappings 配置DispatcherServlet映射
- getRootConfigClasses 配置Root上下文配置类
- getServletConfigClasses 配置MVC上下文配置类
注意:
在Servlet 3规范下我们可以不用配置web.xml文件,所有配置全部在java中完成。
具体实现原理是:
Servlet 3容器会在类路径下查找实现javax.servlet.ServletContainerInitializer接口的类,如果发现的话,就会用他来配置Servlet容器。
Spring提供了实现该接口的SpringServletContainerInitializer类,此类又会查找实现WebApplicationInitializer接口的类来完成配置。
Spring为WebApplicationInitializer提供了一个便利的实现类AbstractAnnotationConfigDispatcherServletInitializer。
所以但我们提供一个继承AbstractAnnotationConfigDispatcherServletInitializer的配置类,SpringServletContainerInitializer将会发现它,并完成所有的Servlet容器的配置。
两个上下文的区别
- ContextLoaderListener创建的上下文用来配置后端的中间件和数据层
- DispatcherServlet启动时候创建的上下文用来配置MVC的组件,控制器,视图解析器等
MVC上下文配置类
基于java-based方式的spring mvc配置,需要创建一个配置类并实现WebMvcConfigurer 接口,WebMvcConfigurerAdapter 抽象类是对WebMvcConfigurer 接口的简单抽象(增加了一些默认实现),所以上面配置代码选择直接继承WebMvcConfigurerAdapter 。然后根据项目的需要实现接口中特定的方法,最后要注意的是,要在配置类上标注@EnableWebMvc。
但是这是远不够的,需要丰富更多的配置。
视图解析器配置示例
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
return resolver;
}
该示例配置了一个InternalResourceViewResolver解析器,通过根据逻辑视图名称,以及自定义的前后缀名来定义具体视图位置。
静态资源处理配置
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
MVC配置类需要继承WebMvcConfigurerAdapter类,通过重写父方法来完善MVC配置。
WebMvcConfigurerAdapter一些主要方法
- configureViewResolvers(ViewResolverRegistry registry);
通过调用ViewResolverRegistry的方法来进行ViewResolvers的配置。
ViewResolverRegistry常用方法有:
->public UrlBasedViewResolverRegistration jsp(String prefix, String suffix) 用来注册InternalResourceViewResolver ,作用类似与直接在上下文中装配一个InternalResourceViewResolver的Bean。
->public void beanName() 用来注册BeanNameViewResolver,该解析器视图名称解析成对应的bean。什么意思呢?假如返回的视图名称是example,它会到spring容器中找有没有一个叫example的bean,并且这个bean是View.class类型的?如果有,返回这个bean。
->public void viewResolver(ViewResolver viewResolver)用来注册各种其他的,或者是自定义的视图解析器。
- addViewControllers(ViewControllerRegistry registry)
此方法可以很方便的实现一个请求到视图的映射,而无需书写controller,例如:
@Override
public void addViewControllers(ViewControllerRegistry registry){
registry.addViewController("/login").setViewName("login");
}
- addResourceHandlers(ResourceHandlerRegistry registry)
此方法用来专门注册一个Handler,来处理静态资源的,例如:图片,js,css等。举例:
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/resource/**").addResourceLocations("/WEB-INF/static/");
}
当你请求http://localhost:8083/resource/1.png时,会把/WEB-INF/static/1.png返回。注意:这里的静态资源是放置在WEB-INF目录下的。
- configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer)
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
此时会注册一个默认的Handler:DefaultServletHttpRequestHandler,这个Handler也是用来处理静态文件的,它会尝试映射/*。当DispatcherServelt映射/时(/ 和/* 是有区别的),并且没有找到合适的Handler来处理请求时,就会交给DefaultServletHttpRequestHandler 来处理。注意:这里的静态资源是放置在web根目录下,而非WEB-INF 下。
简单举个例子,例如在webroot目录下有一个图片1.png。由于DispatcherServlet配置了映射路径是:/ ,它几乎把所有的请求都拦截了,从而导致1.png 访问不到,这时注册一个DefaultServletHttpRequestHandler 就可以解决这个问题。其实可以理解为DispatcherServlet破坏了Servlet的一个特性(根目录下的文件可以直接访问),DefaultServletHttpRequestHandler是帮助回归这个特性的。