HandlerAdapter初始化時,主要是進行注解解析器初始化注冊;返回值處理類初始化;全局注解@ControllerAdvice內容讀取並緩存.
目錄:
注解解析器初始化注冊:@ModelAttribute(往model中添加屬性)
注解解析器初始化注冊:@InitBinder(用於注冊校驗器,參數編輯器等)
返回值處理returnValueHandlers初始化
全局的@ControllerAdvice注解使用類的@ModelAttribute 和 @InitBinder信息讀取並緩存
注:具體解析器的分析還是看後續文章吧,要不文章跟裹腳布似的.
注解@ModelAttritue解析器初始化並注冊
我們先看下@ModelAttribute注解的使用吧:
1. 在注解中定義屬性名,方法返回值
2. 通過model直接設置
3. 暫時沒搞定
1 // 在注解中定義屬性名,方法返回值
2 @ModelAttribute("clazzName")
3 public String setModel() {
4 return this.getClass().getName();
5 }
6 // 通過model直接設置
7 @ModelAttribute
8 public void setModel1(Model model){
9 model.addAttribute("movie", "who");
10 }
11 // 暫時沒搞定
12 @ModelAttribute()
13 public String setModel2(){
14 return "actor";
15 }
新建解析器時的邏輯:
1 如果沒有配置,直接讀取默認實現
這邊默認實現多達24種+自定義實現,主要分為4類解析器:基於注解,基於類型,自定義,號稱解析全部
2 通過Composite封裝,並注冊
解析策略實在太多,這邊封裝一個HandlerMethodArgumentResolverComposite,迭代解析器委托處理(有點責任鏈的味道)
先看初始化解析器,並注冊的代碼:
1 package org.springframework.web.servlet.mvc.method.annotation;
2 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware,
3 InitializingBean {
4 public void afterPropertiesSet() {
5 if (this.argumentResolvers == null) {
6 List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
7 this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
8 }
9 // ...
10 }
11 private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
12 List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
13
14 // Annotation-based argument resolution 基於注解的解析器
15 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
16 resolvers.add(new RequestParamMapMethodArgumentResolver());
17 resolvers.add(new PathVariableMethodArgumentResolver());
18 resolvers.add(new PathVariableMapMethodArgumentResolver());
19 resolvers.add(new MatrixVariableMethodArgumentResolver());
20 resolvers.add(new MatrixVariableMapMethodArgumentResolver());
21 resolvers.add(new ServletModelAttributeMethodProcessor(false));
22 resolvers.add(new RequestResponseBodyMethodProcessor(getMessageConverters()));
23 resolvers.add(new RequestPartMethodArgumentResolver(getMessageConverters()));
24 resolvers.add(new RequestHeaderMethodArgumentResolver(getBeanFactory()));
25 resolvers.add(new RequestHeaderMapMethodArgumentResolver());
26 resolvers.add(new ServletCookieValueMethodArgumentResolver(getBeanFactory()));
27 resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
28
29 // Type-based argument resolution 基於類型的解析器
30 resolvers.add(new ServletRequestMethodArgumentResolver());
31 resolvers.add(new ServletResponseMethodArgumentResolver());
32 resolvers.add(new HttpEntityMethodProcessor(getMessageConverters()));
33 resolvers.add(new RedirectAttributesMethodArgumentResolver());
34 resolvers.add(new ModelMethodProcessor());
35 resolvers.add(new MapMethodProcessor());
36 resolvers.add(new ErrorsMethodArgumentResolver());
37 resolvers.add(new SessionStatusMethodArgumentResolver());
38 resolvers.add(new UriComponentsBuilderMethodArgumentResolver());
39
40 // Custom arguments 自定義解析器
41 if (getCustomArgumentResolvers() != null) {
42 resolvers.addAll(getCustomArgumentResolvers());
43 }
44
45 // Catch-all 全能的解析器
46 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
47 resolvers.add(new ServletModelAttributeMethodProcessor(true));
48
49 return resolvers;
50 }
51 }
然後是HandlerMethodArgumentResolverComposite迭代具體解析器委托處理的代碼:
GOF對責任鏈意圖的定義是:
使多個對象都有機會iu處理請求,從而避免請求的發送者和接受者直接的耦合關系.將這些對象連成一條鏈,並沿這條鏈傳遞該請求,直到有一個對象處理它為止.
從百度百科盜了個類圖,來類比下:

上面是標准的責任鏈,下面是HandlerMethodReturnValueHandler部分類圖

1 package org.springframework.web.method.support;
2 public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver {
3 public Object resolveArgument(
4 MethodParameter parameter, ModelAndViewContainer mavContainer,
5 NativeWebRequest webRequest, WebDataBinderFactory binderFactory)
6 throws Exception {
7
8 HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter);
9 return resolver.resolveArgument(parameter, mavContainer, webRequest, binderFactory);
10 }
11 public boolean supportsParameter(MethodParameter parameter) {
12 return getArgumentResolver(parameter) != null;
13 }
14 private HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) {
15 HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter);
16 if (result == null) {
17 for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) {
18 if (methodArgumentResolver.supportsParameter(parameter)) {
19 result = methodArgumentResolver;
20 this.argumentResolverCache.put(parameter, result);
21 break;
22 }
23 }
24 }
25 return result;
26 }
27 // ...
28 }
注解@InitBinder解析器初始化
先看@InitBinder注解的使用吧:
1 /**
2 * 使用WebDataBinder實現日期校驗
3 * @param dataBinder
4 */
5 @InitBinder
6 public void dateFormat(WebDataBinder dataBinder){
7 SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
8 dateFormat.setLenient(false);
9 dataBinder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat,false));
10 }
這邊的代碼邏輯其實跟@ModelAttribute解析器初始化的邏輯是一樣的,就不具體分析,只是這邊初始化使用的解析器是不一樣的.至於差異的原因,暫時還不知道,哪位有興趣可以幫忙科普科普.
1 package org.springframework.web.servlet.mvc.method.annotation;
2 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware,
3 InitializingBean {
4 private List<HandlerMethodArgumentResolver> getDefaultInitBinderArgumentResolvers() {
5 List<HandlerMethodArgumentResolver> resolvers = new ArrayList<HandlerMethodArgumentResolver>();
6
7 // Annotation-based argument resolution 基於注解的解析器
8 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false));
9 resolvers.add(new RequestParamMapMethodArgumentResolver());
10 resolvers.add(new PathVariableMethodArgumentResolver());
11 resolvers.add(new PathVariableMapMethodArgumentResolver());
12 resolvers.add(new MatrixVariableMethodArgumentResolver());
13 resolvers.add(new MatrixVariableMapMethodArgumentResolver());
14 resolvers.add(new ExpressionValueMethodArgumentResolver(getBeanFactory()));
15
16 // Type-based argument resolution 基於類型的解析器
17 resolvers.add(new ServletRequestMethodArgumentResolver());
18 resolvers.add(new ServletResponseMethodArgumentResolver());
19
20 // Custom arguments 自定義解析器
21 if (getCustomArgumentResolvers() != null) {
22 resolvers.addAll(getCustomArgumentResolvers());
23 }
24
25 // Catch-all 全能的解析器
26 resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
27
28 return resolvers;
29 }
30 // ...
31 }
返回值處理returnValueHandlers初始化
用於將handler處理器的返回值封裝成ModelAndView.
這邊的處理邏輯跟@ModelAttribute注解解析器的初始化高度雷同,我們還是看看使用的HandlerMethodReturnValueHandler接口
接口的定義方式也是高度雷同,一個api問是否支持,一個api進行具體處理.
1 package org.springframework.web.method.support;
2 public interface HandlerMethodReturnValueHandler {
3 boolean supportsReturnType(MethodParameter returnType);
4 void handleReturnValue(Object returnValue,
5 MethodParameter returnType,
6 ModelAndViewContainer mavContainer,
7 NativeWebRequest webRequest) throws Exception;
8
9 }
分類貌似也有一定的相似.
1 package org.springframework.web.servlet.mvc.method.annotation;
2 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware,
3 InitializingBean {
4 private List<HandlerMethodReturnValueHandler> getDefaultReturnValueHandlers() {
5 List<HandlerMethodReturnValueHandler> handlers = new ArrayList<HandlerMethodReturnValueHandler>();
6
7 // Single-purpose return value types 單一目的
8 handlers.add(new ModelAndViewMethodReturnValueHandler());
9 handlers.add(new ModelMethodProcessor());
10 handlers.add(new ViewMethodReturnValueHandler());
11 handlers.add(new HttpEntityMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
12 handlers.add(new CallableMethodReturnValueHandler());
13 handlers.add(new DeferredResultMethodReturnValueHandler());
14 handlers.add(new AsyncTaskMethodReturnValueHandler(this.beanFactory));
15
16 // Annotation-based return value types 基於注解
17 handlers.add(new ModelAttributeMethodProcessor(false));
18 handlers.add(new RequestResponseBodyMethodProcessor(getMessageConverters(), this.contentNegotiationManager));
19
20 // Multi-purpose return value types 多目的
21 handlers.add(new ViewNameMethodReturnValueHandler());
22 handlers.add(new MapMethodProcessor());
23
24 // Custom return value types 自定義
25 if (getCustomReturnValueHandlers() != null) {
26 handlers.addAll(getCustomReturnValueHandlers());
27 }
28
29 // Catch-all 又是全能
30 if (!CollectionUtils.isEmpty(getModelAndViewResolvers())) {
31 handlers.add(new ModelAndViewResolverMethodReturnValueHandler(getModelAndViewResolvers()));
32 }
33 else {
34 handlers.add(new ModelAttributeMethodProcessor(true));
35 }
36
37 return handlers;
38 }
39 }
全局的@ControllerAdvice注解使用類的@ModelAttribute 和 @InitBinder信息讀取並緩存
@ControllerAdvice注解主要是為了解決以下的場景問題:
如果@ModelAttribute或@InitBinder注解如果需要在很多地方使用,怎麼辦?
使用集成的話,由於java的單繼承會限制父類,不夠靈活.
使用時只需要如下添加注解就可以
1 @ControllerAdvice()
2 public class AdviceController {
3 // ...
4 }
源碼解析時,是通過InitializingBean的afterPropertiesSet調用initControllerAdviceCache初始化的解析器.
查找使用注解@ControllerAdivce的類時,通過spring迭代容器過濾容器中全部的類,找到使用ControllerAdvice.class的類並注冊.
1 package org.springframework.web.servlet.mvc.method.annotation;
2 public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter implements BeanFactoryAware,
3 InitializingBean {
4 public void afterPropertiesSet() {
5 // ...
6 initControllerAdviceCache();
7 }
8 private void initControllerAdviceCache() {
9 // ...
10 // 掃描方式找到使用@ControllerAdvice的類
11 List<ControllerAdviceBean> beans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
12 Collections.sort(beans, new OrderComparator());
13
14 for (ControllerAdviceBean bean : beans) {
15 Set<Method> attrMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), MODEL_ATTRIBUTE_METHODS);
16 if (!attrMethods.isEmpty()) {
17 this.modelAttributeAdviceCache.put(bean, attrMethods);
18 }
19 Set<Method> binderMethods = HandlerMethodSelector.selectMethods(bean.getBeanType(), INIT_BINDER_METHODS);
20 if (!binderMethods.isEmpty()) {
21 this.initBinderAdviceCache.put(bean, binderMethods);
22 }
23 }
24 }
25 }
這邊掃描獲取類的方式跟之前的有所不同,我們可以細看下.
1 package org.springframework.web.method;
2 public class ControllerAdviceBean implements Ordered {
3 /**
4 * Find the names of beans annotated with
5 * {@linkplain ControllerAdvice @ControllerAdvice} in the given
6 * ApplicationContext and wrap them as {@code ControllerAdviceBean} instances.
7 */
8 public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
9 List<ControllerAdviceBean> beans = new ArrayList<ControllerAdviceBean>();
10 for (String name : applicationContext.getBeanDefinitionNames()) {
11 if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
12 beans.add(new ControllerAdviceBean(name, applicationContext));
13 }
14 }
15 return beans;
16 }
17 }
看下兩個api在接口中定義:
1. getBeanDefinitionNames 返回容器中定義的全部bean 的 name
2. findAnnotationOnBean 查找類上定義的注解,包括父類與接口
1 package org.springframework.beans.factory;
2 public interface ListableBeanFactory extends BeanFactory {
3 // ...
4 /**
5 * Return the names of all beans defined in this factory.
6 * <p>Does not consider any hierarchy this factory may participate in,
7 * and ignores any singleton beans that have been registered by
8 * other means than bean definitions.
9 * @return the names of all beans defined in this factory,
10 * or an empty array if none defined
11 */
12 String[] getBeanDefinitionNames();
13 /**
14 * Find a {@link Annotation} of {@code annotationType} on the specified
15 * bean, traversing its interfaces and super classes if no annotation can be
16 * found on the given class itself.
17 * @param beanName the name of the bean to look for annotations on
18 * @param annotationType the annotation class to look for
19 * @return the annotation of the given type found, or {@code null}
20 */
21 <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType);
22
23 }
順便關心下@ControllerAdvice信息是如何保存的,就是對應的pojo ControllerAdviceBean:
這邊只是記錄bean,還沒有添加添加根據類,包進行過濾匹配類的功能.
這邊值得說的有3個:
1. 根據類是否實現Ordered接口,設置排序順序
2. 掃描應用下使用ControllerAdvice注解的類就是上面說的api
3. 實現getBeanType獲取類的類型 和resolveBean獲取類實例 ,這個算是advice的行為吧.作為容器的行為吧.
1 package org.springframework.web.method;
2
3 public class ControllerAdviceBean implements Ordered {
4 // 使用注解的類
5 private final Object bean;
6 private final int order;
7 private final BeanFactory beanFactory;
8
9 public ControllerAdviceBean(String beanName, BeanFactory beanFactory) {
10 // ...
11 }
12
13 public ControllerAdviceBean(Object bean) {
14 // ...
15 }
16
17 // 如果類實現Ordered,根據order的值設置排序
18 private static int initOrderFromBeanType(Class<?> beanType) {
19 Order annot = AnnotationUtils.findAnnotation(beanType, Order.class);
20 return (annot != null) ? annot.value() : Ordered.LOWEST_PRECEDENCE;
21 }
22
23 private static int initOrderFromBean(Object bean) {
24 return (bean instanceof Ordered) ? ((Ordered) bean).getOrder() : initOrderFromBeanType(bean.getClass());
25 }
26
27 /**
28 * 掃描應用下使用ControllerAdvice注解的類
29 */
30 public static List<ControllerAdviceBean> findAnnotatedBeans(ApplicationContext applicationContext) {
31 List<ControllerAdviceBean> beans = new ArrayList<ControllerAdviceBean>();
32 for (String name : applicationContext.getBeanDefinitionNames()) {
33 if (applicationContext.findAnnotationOnBean(name, ControllerAdvice.class) != null) {
34 beans.add(new ControllerAdviceBean(name, applicationContext));
35 }
36 }
37 return beans;
38 }
39
40 public Class<?> getBeanType() {
41 Class<?> clazz = (this.bean instanceof String)
42 ? this.beanFactory.getType((String) this.bean) : this.bean.getClass();
43
44 return ClassUtils.getUserClass(clazz);
45 }
46
47 public Object resolveBean() {
48 return (this.bean instanceof String) ? this.beanFactory.getBean((String) this.bean) : this.bean;
49 }
50 // ...
51 }