一、跟踪Spring MVC
请求
- 请求的第一站是
Spring
的DispatcherServlet
,DispatcherServlet
查询一个或多个处理器映射(Handler Mappering
),处理映射器会根据请求携带的URL信息来决策请求哪个控制器。 - 一旦选择了合适的控制器,
DispatcherServlet
会将请求发送给选中的控制器。到了控制器,请求会卸载其负载(用户提交的信息)并等待控制器处理这些信息。 - 控制器在完成逻辑处理后,通常会产生一些模型数据。控制器将这些模型数据打包,并且标记出用于渲染输出的视图名。接下来将请求连同模型和视图名发送回
DispatcherServlet
。 DispatcherServlet
使用视图解析器(View Resolver
)将逻辑视图名匹配为一个特定的视图实现。DispatcherServlet
交付模型数据,视图使用模型数据渲染输出,这个输出会通过响应对象传递给客户端。
二、搭建Spring MVC
DispatcherServlet
是Spirng MVC
的核心,在这里请求会第一次接触到框架,它要负责将请求路由到其他组件之中。
按照传统的方式,像DispatcherServlet
这样的Servlet
会配置在web.xml
中。这个文件会放到应用的war包中。当然这是配置DispatcherServlet
方法之一。借助于Servlet 3规范
和Spring 3.1
的功能增强,这种方式已经不是唯一的方案。
使用Java将DispatcherServlet
配置在Servlet
容器中,而不是使用web.xml
文件。
配置DispatcherServlet
public class SpitterWebInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected String[] getServletMappings() {
// 将DispatcherServlet映射到"/"
return new String[]{"/"};
}
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class<?> [] {RootConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
// 指定配置类
return new Class<?> [] { WebConfig.class};
}
}
我们只需要知道扩展AbstractAnnotationConfigDispatcherServletInitializer
的任意类都会自动的配置Dispatcherservlet
和Spring
应用上下文,Spirng
的应用上下文会位于应用程序的Servlet
上下文之中。
在Servlet3.0
环境中,容器会在类路径中 查找实现javax.servlet.ServletContainerInitialzer
接口的类,如果能发现的话,就会用它来配置Servlet
容器。
Spring
提供了这个接口的实现名为SpringServletContainnerInitialzer
,这个类反过来又会查找实现WebApplicationInitialzer
的类,并将配置的任务交给他们来完成。Spring 3.2
引入了一个便利的WebApplicationInitialzer
基础实现也就是AbstractAnnotationConfigDispatcherServletInitializer
因为我们的SpittrWebApplicationInitialzer
扩展了AbstractAnnotationConfigDispatcherServletInitializer
,(同时也就实现了WebApplicationInitialzer
),因此当部署Servlet3.0
容器的时候,容器会自动发现它,并用它来配置Servlet
上下文。
第一个方法getServletMappings()
,它会将一个或多个路径映射到DispatcherServle
t上,在本示例中,它映射的是”/“,表示它是应用默认的Servlet
,它会处理应用的所有请求。
为了理解其他两个方法,我们首先需要理解DispatcherServlet
和一个Servlet监听器
(也就是ContextLoaderListener
)的关系。
当DispatcherServlet
启动的时候,它会创建应用上下文,并加载配置文件或配置类中声明的bean。在上面那个程序中的getServletConfigClasses()
方法中,我们要求DispatcherServlet
加载应用上下文时,使用定义在WebConfig
配置类(使用Java配置)中的bean。
但在Spring Web
应用中,通常还会有另外一个应用上下文。另外这个就是由ContextLoaderListener
创建。
我们希望DispatcherServlet
加载包含Web组件的bean,如控制器,视图解析器,以及处理器映射。而ContextLoaderListener
要加载应用中的其他bean,这些bean通常是驱动应用后端的中间层和数据层组件。
实际上AbstractAnnotationConfigDispatcherServletInitializer
会同时创建DispatcherServlet
和ContextLoaderListener
。getServletConfigClasses()
方法会返回带有@Configuration
注解的类将会用来定义DispatcherServlet
应用上下文中的bean,getRootConfigClasses()
会返回带有@Configuration
注解的类将会用来配置ContextLoaderListener
创建的应用上下文。
启用Spring MVC
我们有多种方式来启动DispatcherServlet
,与之类似,启用Spring MVC
组件的方式也不止一种,以前Spring是XMl进行配置的,你可以选择<mvc:annotation-driver>
启用注解驱动的Spring MVC
。
我们所能创建最简单的Spring MVC
配置就是一个带有@EnableWebMvc
注解的类。
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@EnableWebMvc
public class WebConfig {
}
这可以运行起来,它的确能够启用Spring MVC
,但还有不少问题要解决。
没有配置视图解析器,如果这样的话,
Spring
默认会使用BeanNameView-Resolver
,这个视图解析器会查找ID与视图名称匹配的bean,并且查找的bean要实现View接口,它以这样的方式来解析视图。没有启用组件扫描。这样的结果就是,
Spring
只能找到显示声明在配置类中的控制器。这样配置的话,
DispatcherServlet
会映射为默认的Servlet
,所以他会处理所有的请求,包括对静态资源的请求,如图片 和样式表(在大多数情况下,这可能并不是你想要的结果)。
因此我们需要在WebConfig
这个最小的Spring MVC
配置上再加一些内容,从而让他变得真正实用。
@Configuration
@EnableWebMvc //启用Spring MVC
@ComponentScan("com.ql.controller") //启用组件扫描
public class WebConfig extends WebMvcConfigurerAdapter {
@Bean
public ViewResolver viewResolver () {
InternalResourceViewResolver resolver = new InternalResourceViewResolver();
//配置JSP视图解析器
resolver.setPrefix("/WEB-INF/views/");
resolver.setSuffix(".jsp");
resolver.setExposeContextBeansAsAttributes(true);
return resolver;
}
@Override
public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
//配置静态资源的处理
configurer.enable();
}
}
WebConfig
现在添加了@ComponentScan
注解,此时将会扫描com.ql.controller
包来查找组件。带有@Controller
注解控制器将会成为组件扫描时的候选bean。因此,我们不需要在配置类中显示声明任何的控制器。
添加了一个ViewResolver
bean,更具体的将是InternalResourceViewResolver
。我们只需要知道他会去查找jsp文件,在查找的时候,它会在视图名称上加一个特定的前缀和后缀。(例如:名为home的视图会被解析为/WEB-INF/views/home.jsp
)
WebConfig
类还扩展里WebMvcConfigurerAdapter
并重写了其configureDefaultServletHandling()
方法,通过调用DefaultServletHandlerConfigurer
的enable()
方法,我们要求DispatcherServlet
将静态资源的请求转发到Servlet
容器中默认的Servlet
上,而不是使用DispatcherServlet
本来来处理此类请求。
Web相关的配置通过DisPatcherServlet
创建的应用上下文都已经配好了,接下来配置RootConfig
:
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScan.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.FilterType;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
@Configuration
@ComponentScan(basePackages = {"com.ql"},
excludeFilters = {
@Filter(type = FilterType.ANNOTATION,value = EnableWebMvc.class)})
public class RootConfig {
}
RootConfig
使用了@ComponentScan
注解,这样的话,我们就有很多机会用非Web的组件来完善RootConfig
。