SpringMVC之分析RequestMappingHandlerAdapter(一)

来源:互联网 发布:淘宝怎么评价卖家 编辑:程序博客网 时间:2024/06/08 03:42

RequestMappingHandlerAdapter请求映射处理适配器,在SpringMVC中它是一个非常重要的类,对请求处理方法的调用主要是通过这个类来完成的(这段代码mv = ha.handle(processedRequest, response, mappedHandler.getHandler());)。下面我们就简单的分析一下这个类。首先我们先看一下它的UML类图结构:


画红线的部分是需要我们注意的一些类,如果你对Spring的Bean生命在周期熟悉的话,你会发现这些都是Spring的Bean生命周期相关的一些类(Spring Bean的生命周期小析(一)和Spring Bean的生命周期小析(二))。我们分析RequestMappingHandlerAdapter的时候也会从这些接口的实现方法中开始。首先我们先看一下它的构造函数:

public RequestMappingHandlerAdapter() {StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();stringHttpMessageConverter.setWriteAcceptCharset(false);  // see SPR-7316this.messageConverters = new ArrayList<HttpMessageConverter<?>>(4);this.messageConverters.add(new ByteArrayHttpMessageConverter());this.messageConverters.add(stringHttpMessageConverter);this.messageConverters.add(new SourceHttpMessageConverter<Source>());this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());}
从上面的代码中我们可以看到这里默认添加了四种类型的Http数据转换器。其中我们需要关注的时候AllEncompassingFormHttpMessageConverter这个转换器,我们也去它的构造函数中看一下:

public AllEncompassingFormHttpMessageConverter() {addPartConverter(new SourceHttpMessageConverter<Source>());//如果Classpath下面有javax.xml.bind.Binder类,//没有com.fasterxml.jackson.dataformat.xml.XmlMapper类的话//则添加Jaxb2RootElementHttpMessageConverter转换器if (jaxb2Present && !jackson2XmlPresent) {addPartConverter(new Jaxb2RootElementHttpMessageConverter());}//如果Classpath下有com.fasterxml.jackson.databind.ObjectMapper//和com.fasterxml.jackson.core.JsonGenerator的话,则添加//MappingJackson2HttpMessageConverter转换器if (jackson2Present) {addPartConverter(new MappingJackson2HttpMessageConverter());}//如果Classpath下面有com.google.gson.Gson类的话,则添加//GsonHttpMessageConverter转换器else if (gsonPresent) {addPartConverter(new GsonHttpMessageConverter());}//如果Classpath下有com.fasterxml.jackson.dataformat.xml.XmlMapper类的话,//则添加MappingJackson2XmlHttpMessageConverter转换器if (jackson2XmlPresent) {addPartConverter(new MappingJackson2XmlHttpMessageConverter());}}
其实在它的父类中还添加了三种类型的Convert:

public FormHttpMessageConverter() {//application/x-www-form-urlencodedthis.supportedMediaTypes.add(MediaType.APPLICATION_FORM_URLENCODED);//multipart/form-datathis.supportedMediaTypes.add(MediaType.MULTIPART_FORM_DATA);//ByteArrayHttpMessageConverterthis.partConverters.add(new ByteArrayHttpMessageConverter());StringHttpMessageConverter stringHttpMessageConverter = new StringHttpMessageConverter();stringHttpMessageConverter.setWriteAcceptCharset(false);//StringHttpMessageConverterthis.partConverters.add(stringHttpMessageConverter);//ResourceHttpMessageConverterthis.partConverters.add(new ResourceHttpMessageConverter());applyDefaultCharset();}
按照Spring的Bean的生命周期的执行顺序,这里会先调用BeanFactoryAware#setBeanFactory方法、接着调用ApplicationContextAware#setApplicationContext方法,最后调用InitializingBean#afterPropertiesSet方法。我们先看一下重写之后的setBeanFactory方法的源码:

@Overridepublic void setBeanFactory(BeanFactory beanFactory) {if (beanFactory instanceof ConfigurableBeanFactory) {this.beanFactory = (ConfigurableBeanFactory) beanFactory;}}
这个方法的内容很简单,就是设置一下beanFactory的属性值(其实这里的beanFactory是DefaultListableBeanFactory的实例)。
下面我们看一下setApplicationContext这个方法的源码,这个方法藏的比较深,在它的父类ApplicationObjectSupport#setApplicationContext方法中。

public final void setApplicationContext(ApplicationContext context) throws BeansException {//isContextRequired()方法返回trueif (context == null && !isContextRequired()) {// Reset internal context state.this.applicationContext = null;this.messageSourceAccessor = null;}else if (this.applicationContext == null) {// Initialize with passed-in context.//所传入的context如果不能被实例化,则抛出异常if (!requiredContextClass().isInstance(context)) {throw new ApplicationContextException("Invalid application context: needs to be of type [" + requiredContextClass().getName() + "]");}this.applicationContext = context;//国际化this.messageSourceAccessor = new MessageSourceAccessor(context);//初始化ApplicationContextinitApplicationContext(context);}else {// Ignore reinitialization if same context passed in.if (this.applicationContext != context) {throw new ApplicationContextException("Cannot reinitialize with different application context: current one is [" +this.applicationContext + "], passed-in one is [" + context + "]");}}}
我们主要看一下WebApplicationObjectSupport#initApplicationContext这个方法的内容:

@Overrideprotected void initApplicationContext(ApplicationContext context) {//调用父类中的initApplicationContext方法super.initApplicationContext(context);if (this.servletContext == null && context instanceof WebApplicationContext) {this.servletContext = ((WebApplicationContext) context).getServletContext();if (this.servletContext != null) {//初始化ServletContextinitServletContext(this.servletContext);}}}
这里先调用父类中的initApplicationContext方法,然后会初始ServletContext。先看一下ApplicationObjectSupport#initApplicationContext方法:

protected void initApplicationContext(ApplicationContext context) throws BeansException {initApplicationContext();}
在我们的这个类的继承体系中,initApplicationContext();是一个空实现。同样initServletContext也是一个空实现。接下来我们要分析的一个重点InitializingBean#afterPropertiesSet的方法,它的源码内容如下,下面我们一点一点的分析这个类:

public void afterPropertiesSet() {// Do this first, it may add ResponseBody advice beansinitControllerAdviceCache();if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.initBinderArgumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}if (this.returnValueHandlers == null) {List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);}}
我们先看initControllerAdviceCache这个方法:

private void initControllerAdviceCache() {//通过上面的分析我们知道,这个方法的内容不为nullif (getApplicationContext() == null) {return;}//获取所有带ControllerAdvice注解的类List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());//对获取到的ControllerAdvice注解的类进行排序,排序的规则是基于实现PriorityOrdered接口或者带有Order注解AnnotationAwareOrderComparator.sort(beans);List<Object> requestResponseBodyAdviceBeans = new ArrayList<Object>();//循环获取到的ControllerAdviceBeanfor (ControllerAdviceBean bean : beans) {//获取所有ModelAttribute注解的方法,并且没有RequestMapping注解的方法Set<Method> attrMethods = MethodIntrospector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);if (!attrMethods.isEmpty()) {this.modelAttributeAdviceCache.put(bean, attrMethods);if (logger.isInfoEnabled()) {logger.info("Detected @ModelAttribute methods in " + bean);}}//获取所有带InitBinder注解的方法Set<Method> binderMethods = MethodIntrospector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);if (!binderMethods.isEmpty()) {this.initBinderAdviceCache.put(bean, binderMethods);if (logger.isInfoEnabled()) {logger.info("Detected @InitBinder methods in " + bean);}}//如果实现了RequestBodyAdvice接口if (RequestBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {requestResponseBodyAdviceBeans.add(bean);if (logger.isInfoEnabled()) {logger.info("Detected RequestBodyAdvice bean in " + bean);}}//如果实现了ResponseBodyAdvice接口if (ResponseBodyAdvice.class.isAssignableFrom(bean.getBeanType())) {requestResponseBodyAdviceBeans.add(bean);if (logger.isInfoEnabled()) {logger.info("Detected ResponseBodyAdvice bean in " + bean);}}}//添加到requestResponseBodyAdvice集合中if (!requestResponseBodyAdviceBeans.isEmpty()) {this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);}}
在这个方法里主要是获取了带ControllerAdvice注解的类,并从这些类中查找实现了RequestBodyAdvice或者ResponseBodyAdvice接口的类,添加到requestResponseBodyAdvice集合中,另外获取所有带ModelAttribute注解且没有RequestMapping注解的方法,放到modelAttributeAdviceCache集合中,获取所有带InitBinder注解的方法放到initBinderAdviceCache的集合中。下面我们再看这一段代码:

if (this.argumentResolvers == null) {List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);}
private HandlerMethodArgumentResolverComposite argumentResolvers;
argumentResolvers这个属性是HandlerMethodArgumentResolverComposite 类型的,如果我们在配置RequestMappingHandlerAdapter的时候设置了一系列HandlerMethodArgumentResolver的实现类的话,如下所示,

public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {if (argumentResolvers == null) {this.argumentResolvers = null;}else {this.argumentResolvers = new HandlerMethodArgumentResolverComposite();this.argumentResolvers.addResolvers(argumentResolvers);}}
它会先创建HandlerMethodArgumentResolverComposite对象,然后把配置的HandlerMethodArgumentResolver的实现类添加到HandlerMethodArgumentResolverComposite的对象中。这里我们是没有手工配置RequestMappingHandlerAdapter的,所以,会先调用getDefaultArgumentResolvers方法,获取一系列默认的HandlerMethodArgumentResolver的实现类,代码如下:

private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();// Annotation-based argument resolutionresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));resolvers.add(new RequestParamMapMethodArgumentResolver());resolvers.add(new PathVariableMethodArgumentResolver());resolvers.add(new PathVariableMapMethodArgumentResolver());resolvers.add(new MatrixVariableMethodArgumentResolver());resolvers.add(new MatrixVariableMapMethodArgumentResolver());resolvers.add(new ServletModelAttributeMethodProcessor(false));resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));resolvers.add(new RequestHeaderMapMethodArgumentResolver());resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));resolvers.add(new SessionAttributeMethodArgumentResolver());resolvers.add(new RequestAttributeMethodArgumentResolver());// Type-based argument resolutionresolvers.add(new ServletRequestMethodArgumentResolver());resolvers.add(new ServletResponseMethodArgumentResolver());resolvers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.requestResponseBodyAdvice));resolvers.add(new RedirectAttributesMethodArgumentResolver());resolvers.add(new ModelMethodProcessor());resolvers.add(new MapMethodProcessor());resolvers.add(new ErrorsMethodArgumentResolver());resolvers.add(new SessionStatusMethodArgumentResolver());resolvers.add(new UriComponentsBuilderMethodArgumentResolver());// Custom argumentsif (getCustomArgumentResolvers() != null) {resolvers.addAll(getCustomArgumentResolvers());}// Catch-allresolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));resolvers.add(new ServletModelAttributeMethodProcessor(true));return resolvers;}
SpringMVC为我们自动添加了大概26个HandlerMethodArgumentResolver的实现类。这里有一个方法需要我们注意一下:getCustomArgumentResolvers() 方法。这个方法是用来获取自定义的HandlerMethodArgumentResolver的实现类,也就是说如果我们有自己写的HandlerMethodArgumentResolver的方法的话,我们配置的属性是customArgumentResolvers,最好不要配置argumentResolvers这个属性。然后我们注意的一点是我们所添加的所以的HandlerMethodArgumentResolver的实现类,都是添加到了HandlerMethodArgumentResolverComposite这个类中,HandlerMethodArgumentResolverComposite也是HandlerMethodArgumentResolver的一个实现类。还有一点需要注意的是,这里创建了两个RequestParamMethodArgumentResolver的实例,一个传了true参数,一个传了false参数,这是用来解析不同的请求参数的,详情请看这里:SpringMVC之分析请求对应处理器方法参数的解析过程(一)

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 在飞机上要拍照怎么办 拍照片人闭眼了怎么办 偏头疼恶心想吐怎么办 健康证怎么办 在哪办 怎么办健康证去哪里办 公司合同不给我怎么办 孕妇吃了酸梅粉怎么办 婧氏牙膏没客源怎么办 乐视手机耗电快怎么办 乐视手机费电快怎么办 最爱最恨都是你怎么办 耳朵长疱疹很痛怎么办 刚怀孕就有霉菌怎么办 车牌选错了想换怎么办 足癣传染到身上怎么办 金鱼生病了立鳞怎么办 脸上有闭口痘痘怎么办 泰迪得了皮肤癣怎么办 qq截图发不出去怎么办 半夜2点肚子饿怎么办 半夜两三点饿了怎么办 科目三预约不上怎么办 心脏支架又堵了怎么办 车辆验车过期了怎么办 车辆年审过期2天怎么办 违停单子丢了怎么办 沙发弹簧包坏了怎么办 bt种子被和谐了怎么办 苹果手机id密码忘了怎么办 墙漆颜色太深了怎么办 油漆颜色太深了怎么办 墙面漆颜色太深怎么办 古代打仗牙掉了怎么办 乐视电视没声音怎么办 老公疑心病很重怎么办啊 被安装了尿道锁怎么办 狼青小狗腿罗圈怎么办 备孕期间有霉菌怎么办 虫子进皮肤里了怎么办 生完孩子肚子越来越大怎么办 怀孕8个月肚子小怎么办