跟踪Spring MVC的请求
注:流程:
1)请求离开浏览器到达的第一站就是前端控制器,在这里一个单例的Servlet将请求委托给应用程序的其他组件来执行实际处理。DispatcherServlet就是Spring MVC中的前端控制器(front controller)。
2)DispatcherServlet将请求发送给Spring MVC的控制器(controller),控制器是用于处理请求的Spring组件,一个应用程序中会有一(多)个控制器。但是DispatcherServlet需要知道将请求发送给哪个控制器,所以DispatcherServlet会查询一(多)个处理器映射(handler Mapping)来确定下一站去哪里。处理器映射会根据请求所携带的URL信息来进行决策。
3)一旦选择了合适的控制器,DispatcherServlet会将请求发送给选中的控制器。到了控制器,请求会卸下其负载(携带的用户信息)并耐心等待控制器处理信息。实际上,设计良好的控制器本身只处理很少甚至不处理工作,而是将业务逻辑委托给一(多)个服务对象进行处理。
4)控制器在完成逻辑处理后,会产生信息,这些信息返回给用户并在浏览器上显示。这些信息称为模型(model),信息也会发送给一个视图(View)。
实际上如果采用前后端分离的话,那么后端是不会返回视图的,仅仅会返回json数据跟前端进行交互。
5)控制器做的最后一步是将数据模型打包,并且标示出用于渲染输出的视图名。它接下来会将请求同模型和视图名一起发给DispatcherServlet,这样控制器就不会与特定的视图耦合,传递给DispatcherServlet的视图名并不直接表示某个特定的JSP。实际上它并不确定视图就是jsp。相反仅仅是只传递一个逻辑名称,这个名字会查找并产生真正的视图。DispatcherServlet会使用视图解析器将逻辑名称转化为一个真正的视图。
7)DispatcherServlet已经成功渲染了视图,请求将交付给模型数据。请求的任务就完成了。视图将使用模型数据渲染输出,这个输出会通过响应对象传递给客户端。
搭建Spring MVC
配置DispatcherServlet
在Servlet3规范和Spring3.1的帮助下我们可以实现不使用web.xml,而是将DispatcherServlet配置在Servlet容器中。
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
//将DispatcherServlet映射到"/"
@Override
protected String[] getServletMappings() {
return new String[]{"/"};
}
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[]{RootConfig.class};
}
//指定配置类
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{Webconfig.class};
}
}
AbstractAnnotationConfigDispatcherServletInitializer剖析
在Servlet3.0环境,容器会在类路径中查找实现了javax.servlet.ServletContainerInitailizer接口的类。如果有的话就会用它来配置Servlet容器。
Spring提供了这个类的实现:SpringServletContainerInitializer,这个类又会查找实现WebApplicationInitializer的类并将配置的任务交给他们。
在Spring3.2又引入了一个WebApplicationInitializer基础实现,这就是AbstractAnnotationConfigDispatcherServletInitializer。
我们自己的WebAppInitializer实现了AbstractAnnotationConfigDispatcherServletInitializer,所以只要部署到Servlet3.0容器中,容器就会自动发现他,并使用它来发现Servlet上下文。
只需要重写其中的三个方法就ok了。
getServletMappings(),它会将一(多)个路径映射到DispatcherServlet。
getRootConfigClasses():加载
getServletConfigClasses():
两个应用上下文之间的关系
DispatcherServlet与ContextLoaderListener。
DispatcherServlet加载的上下文是当前Servlet的上下文。加载web组件的bean,如控制器、视图解析器、以及处理器映射器等。
而ContextLoaderListener加载的上下文是全局上下文。加载的通常是驱动应用后端的中间层或者数据层组件。
注:DispatcherServlet是可以多实现的,每一个DispatcherServlet都有自己的上下文,就是通过getServletConfigClasses()获取。而对于整个应用来说也有自己的上下文,这个上下文是由ContextLoaderListener进行加载的。
AbstractAnnotationConfigDispatcherServletInitializer会同时创建DispatcherServlet与ContextLoaderListener。
getServletConfigClasses()会返回带有@Configuration注解的类将用来定义DispatcherServlet应用上下文的bean。
getRootConfigClasses()会返回带有@Configuration注解的类将用来定义ContextLoaderListener创建的应用上下文的bean。
本例中,根配置定义在RootConfig中,DispatcherServlet的配置声明在WebConfig中。
启用Spring MVC
最简单的SpringMVC配置(带有@EnableWebMvc注解的类):
@Configuration
@EnableWebMvc
public class WebConfig {
}
可以通过它,运行SpringMVC,但是还有问题需要解决。
1)没有视图解析器。Spring会默认使用BeanNameView-Resolver,这个视图解析器会查找ID与视图名称匹配的bean,并且查找的bean要实现View接口,他以这样的方式解析视图。
2)没有启动组件扫描。Spring只能找到显示声明在配置类中的控制器。
3)DispatcherServlet会映射为应用默认的Servlet,它会处理所以请求,包括静态资源的请求。
修改一下:最小但可用的SpringMVC配置。
@Configuration
//启用SpringMVC
@EnableWebMvc
//启用组件扫描
@ComponentScan("com.learn.spring.five.dispatcherservlet.web")
public class WebConfig extends WebMvcConfigurerAdapter{
//配置jsp视图解析器
@Bean
public ViewResolver viewResolver() {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
resolver.setPrefix("WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
//配置静态资源的处理
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
configurer.enable();
}
}
我们添加了@ComponentScan注解,可以对对应包来查找组件。我们可以通过@Controller注解实现控制器组件。
添加了ViewResolver bean,其实是InternalResourceViewResolver,这个视图解析器会帮助我们查找jsp文件,且会帮我们加上前后缀。
扩展了WebMvcConfigurerAdapter并重写了configureDefaultServletHandling()方法,通过DefaultServletHandlerConfigurer的enable()方法,DispatcherServlet将对静态资源的请求转发到Servlet容器中默认的Servlet上,而不是DispatcherServlet本身处理此类方法。
本章节聚焦于web开发,RootConfig会相对简单:
@Configuration
@ComponentScan(basePackages = {"com"},
excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION,value = EnableWebMvc.class)
})
public class RootConfig {
}
模拟背景
User(应用用户)和state(用户发布的状态)。
类似于微博的一款应用背景。