上篇文章中我们看到了Spring官方对DispatcherServlet的几个特殊Bean的介绍,其中第二个就是HandlerAdapter,它是DispatcherServlet调用handler的关键,因为它会对DispatcherServlet屏蔽细节实现,让DispatcherServlet只关心调用结果而不需要去关心调用细节(例如参数类型转换及封装等)。下面我们来分析HandlerAdapter的初始化过程
我们已经知道DispatcherServlet有一个默认配置文件,里面配置了各种策略。对于HandlerAdapter来说存在3个默认配置,他们分别是
- HttpRequestHandlerAdapter 这个处理器是用来处理静态资源的
- SimpleControllerHandlerAdapter 这个处理器是用来处理在实现了Controller接口或者AbstractController等Spring的默认实现类的子类
- RequestMappingHandlerAdapter 这个处理器用来处理@RequestMapping注解的处理器,也是最常用,用的最多的一个,我们今天会着重分析这个
DispatcherServlet在初始化几个策略时遇到使用默认策略均会调用createDefaultStrategy方法,最终通过IOC容器创建对象并执行回调。通过RequestMappingHandlerAdapter的类关系图我们可以看出RequestMappingHandlerAdapter同样实现了InitializingBean接口,因此我们关注的重点一个是在实例化阶段,一个是在回调InitializingBean接口方法阶段。
首先我们来看实例化阶段
// 祖先类中会设置allowHeader为除了Trace外所有的HttpMethod但并未设置supportedMethods
public WebContentGenerator(boolean restrictDefaultSupportedMethods) {
if (restrictDefaultSupportedMethods) {
this.supportedMethods = new LinkedHashSet<>(4);
this.supportedMethods.add(METHOD_GET);
this.supportedMethods.add(METHOD_HEAD);
this.supportedMethods.add(METHOD_POST);
}
initAllowHeader();
}
private void initAllowHeader() {
Collection<String> allowedMethods;
// 这里配置allowedMethods
if (this.supportedMethods == null) {
allowedMethods = new ArrayList<>(HttpMethod.values().length - 1);
for (HttpMethod method : HttpMethod.values()) {
if (method != HttpMethod.TRACE) {
allowedMethods.add(method.name());
}
}
}
else if (this.supportedMethods.contains(HttpMethod.OPTIONS.name())) {
allowedMethods = this.supportedMethods;
}
else {
allowedMethods = new ArrayList<>(this.supportedMethods);
allowedMethods.add(HttpMethod.OPTIONS.name());
}
this.allowHeader = StringUtils.collectionToCommaDelimitedString(allowedMethods);
}
// 在自身的构造器中添加了StringHttpMessageConverter、ByteArrayHttpMessageConverter、
// SourceHttpMessageConverter、AllEncompassingFormHttpMessageConverter
public RequestMappingHandlerAdapter() {
StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();
stringHttpMessageConverter.setWriteAcceptCharset(false); // see SPR-7316
this.messageConverters = new ArrayList<>(4);
this.messageConverters.add(new ByteArrayHttpMessageConverter());
this.messageConverters.add(stringHttpMessageConverter);
try {
this.messageConverters.add(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
上述代码中初始化了4种消息转换器,针对不同的content-type,设置不同的消息转换器
- StringHttpMessageConverter 支持text/plain或所有/的请求类型
- ByteArrayHttpMessageConverter 支持application/octet-stream或所有/的请求类型
- SourceHttpMessageConverter 支持text/xml、application/xml、application/*-xml这三种类型的请求
- AllEncompassingFormHttpMessageConverter 支持application/x-www-form-urlencoded的读写(file upload)和multipart/form-data格式的写操作(RestTemplate或WebClient中发送数据)。同时会根据classpath是否包含class文件来决定是否添加对xml和json的处理。
/**
* jaxb2Present = ClassUtils.isPresent("javax.xml.bind.Binder", classLoader);
* jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) &&
ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader);
* jackson2XmlPresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", classLoader);
* jackson2SmilePresent = ClassUtils.isPresent("com.fasterxml.jackson.dataformat.smile.SmileFactory", classLoader);
* gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader);
* jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader);
*/
public AllEncompassingFormHttpMessageConverter() {
try {
addPartConverter(new SourceHttpMessageConverter<>());
}
catch (Error err) {
// Ignore when no TransformerFactory implementation is available
}
if (jaxb2Present && !jackson2XmlPresent) {
addPartConverter(new Jaxb2RootElementHttpMessageConverter());
}
if (jackson2Present) {
addPartConverter(new MappingJackson2HttpMessageConverter());
}
else if (gsonPresent) {
addPartConverter(new GsonHttpMessageConverter());
}
else if (jsonbPresent) {
addPartConverter(new JsonbHttpMessageConverter());
}
if (jackson2XmlPresent) {
addPartConverter(new MappingJackson2XmlHttpMessageConverter());
}
if (jackson2SmilePresent) {
addPartConverter(new MappingJackson2SmileHttpMessageConverter());
}
}
实例化阶段主要就做了这些操作,后面在分析DispatcherServlet处理过程时详细分析各转换器的作用。
下面我们再来关注初始化后的回调,开始之前我们先来看两张图
以上两张图来自Spring官方文档,介绍了SpringMVC的Controller中支持的请求参数类型和返回值类型。其中我们眼熟的@RequestParam、@PathVariable、@RequestBody、Map、Model、ServletRequest、ServletResponse、@ResponseBody、ModelAndView等等。下面就会针对上述的类型注册相关解析器或处理器。
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
// 在这个方法内会获取当前IOC容器中所有注有@ControllerAdvice的Bean,将他们包装成ControllerAdviceBean。
// 并将他们分条件缓存。条件包括注有@InitBinder的方法,注有@ModelAttribute但没有@RequestMapping的方法。
// 还会判断ControllerAdviceBean上是否注有RequestBodyAdvice或ResponseBodyAdvice,若存在也会缓存
initControllerAdviceCache();
// 这里会初始化参数解析器(包括内建解析器和通过setCustomArgumentResolvers方法自定义的解析器)
if (this.argumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
// 这里会初始化一系列解析@InitBinder的解析器
if (this.initBinderArgumentResolvers == null) {
List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
// 这里会初始化返回值处理器()包括内建处理器和通过setReturnValueHandlers自定义的处理器)
if (this.returnValueHandlers == null) {
List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
至此初始化过程基本结束,后面的异常处理、视图解析、本地化、样式、文件上传和请求转发处理我们以后在分析,下一篇我们会开始分析DispatcherServlet的处理过程。
在查看官方文档的时候发现Spring现在推荐使用MVC Config作为最佳入口,因为它申明了必须的Special Bean同时提供要给高等级的自定义配置回调API。
只有无法找到MVC Config时才会使用DispatcherServlet.properties进行默认配置。后面有机会我们会重新分析MVC Config方式下的操作流程。