Spring MVC学习笔记之Spring MVC组件HandlerAdapter

1、HandlerAdapter简介

  HandlerAdapter是具体使用Handler来干活的,每个HandlerAdapter封装了一种Handler的具体使用方法。在这里插入图片描述

  通过上面的类图,我们可以发现HandlerAdapter接口一共只有六个子类,而且只有AbstractHandlerMethodAdapter和RequestMappingHandlerAdapter是两层,其实这两个类也是在实际工作中使用最多的,我们后面具体分析。

其实,在Spring4.3之前,还有一个AnnotationMethodHandlerAdapter 实现类,在4.3版本被打上弃用标签,在5.0之后就移除了该类。而HandlerFunctionAdapter类则是从Spring5.2开始引入的,主要用来处理响应式的请求。

2、HandlerAdapter接口

  HandlerAdapter 接口非常简单,只有三个方法,各个方法的含义如下所示:

public interface HandlerAdapter {
	//判断是否对应的handler处理器
	boolean supports(Object handler);
	//核心方法,实际调用handler处理器,并返回处理结果的方法
	@Nullable
	ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
	//获取HttpServletRequest对象上一次修改时间的方法
	long getLastModified(HttpServletRequest request, Object handler);
}
3、SimpleControllerHandlerAdapter类

  在HandlerAdapter类结构中,SimpleControllerHandlerAdapter、SimpleServletHandlerAdapter、HttpRequestHandlerAdapter三个类主要用来适配哪些处理情况比较单一的情况,所以这几个类的实现也比较简单。这里以SimpleControllerHandlerAdapter为例,分析它的实现逻辑。

public class SimpleControllerHandlerAdapter implements HandlerAdapter {

	//只支持实现了org.springframework.web.servlet.mvc.Controller接口的处理器
	@Override
	public boolean supports(Object handler) {
		return (handler instanceof Controller);
	}
	//调用处理器中的方法进行处理
	@Override
	@Nullable
	public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {

		return ((Controller) handler).handleRequest(request, response);
	}
	//返回HttpServletRequest上一次更新时间
	@Override
	public long getLastModified(HttpServletRequest request, Object handler) {
		if (handler instanceof LastModified) {
			return ((LastModified) handler).getLastModified(request);
		}
		return -1L;
	}
}

  在SimpleControllerHandlerAdapter 实现类中,代码非常简单。其中核心的handle()方法,因为实现Controller接口的处理器方法,只有handleRequest()一个方法,且参数和返回值也非常明确,所以可以直接调用处理器的方法即可。其中,Controller 接口的代码如下:

@FunctionalInterface
public interface Controller {

	@Nullable
	ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;

}

  SimpleServletHandlerAdapter用来处理Servlet类型的处理器,HttpRequestHandlerAdapter用来处理实现了HttpRequestHandler接口的处理器,处理逻辑和SimpleControllerHandlerAdapter基本上是一样的,这里不在具体分析了。

4、RequestMappingHandlerAdapter类

  RequestMappingHandlerAdapter类继承于AbstractHandlerMethodAdapter抽象类,是在实际工作中使用最多的一种方式,这种方式主要用来处理通过 @RequestMapping注解实现的HandlerMethod类型的处理器。这种方式的处理器,支持自定义的参数和返回值,所以处理器的参数的个数、类型都不确定,返回值的类型也可以由用户来自定义。灵活多变的用法,就带来了这种底层实现的复杂度,其中定义了很多参数解析、返回值解析的方法,用来保证这种灵活性。这一节内容我们先学习RequestMappingHandlerAdapter类的核心处理逻辑:

4.1、AbstractHandlerMethodAdapter抽象类

  抽象类AbstractHandlerMethodAdapter继承了WebContentGenerator类,并实现了HandlerAdapter和Ordered两个接口。其中,WebContentGenerator类提供了一些遍历的通用方法,比如设置或获取当前request支持的方法集合,初始化或者获取设置“Cache-Control”的方法等。HandlerAdapter是核心接口,Ordered接口主要是为了给多个HandlerAdapter类提供一个排序的依据。类定义如下:

public abstract class AbstractHandlerMethodAdapter extends WebContentGenerator implements HandlerAdapter, Ordered {
	//内容省略
}

  在抽象类中,实现了HandlerAdapter接口的三个方法,同时又定义了另外三个方法,交由子类实现了。首先,supports()方法实现:判断是否是HandlerMethod类型,然后再调用定义的抽象方法supportsInternal()进行判断,实现如下:

@Override
public final boolean supports(Object handler) {
	return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}

  然后,handle()方法的实现:直接调用定义的抽象方法handleInternal(),交由子类进行实现,其中第三个参数,代表处理器,直接转化成了HandlerMethod类型。

@Override
@Nullable
public final ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
		throws Exception {

	return handleInternal(request, response, (HandlerMethod) handler);
}

  最后,getLastModified()方法的实现:也是直接交给了抽象方法getLastModifiedInternal(),并把处理器转换成了HandlerMethod类型。实现如下:

@Override
public final long getLastModified(HttpServletRequest request, Object handler) {
	return getLastModifiedInternal(request, (HandlerMethod) handler);
}

  所以,在抽象类AbstractHandlerMethodAdapter中没有实现实际的业务处理,只是通过继承WebContentGenerator类提供了一些便利的方法,然后提供了排序功能,其他逻辑都交由了子类RequestMappingHandlerAdapter来实现,下面我们开始分析RequestMappingHandlerAdapter类的实现逻辑。

4.2、RequestMappingHandlerAdapter类

  RequestMappingHandlerAdapter类是HandlerAdapter结构中最复杂的类,尤其是其中使用到了很多的参数解析、返回值解析、Model构建、HttpMessageConverter等组件。这里我们先分析RequestMappingHandlerAdapter类的设计和实现:

属性字段:

//过滤有注解@InitBinder的方法
public static final MethodFilter INIT_BINDER_METHODS = method ->
		AnnotatedElementUtils.hasAnnotation(method, InitBinder.class);
//过滤有注解@ModelAttribute且没有@RequestMapping注解的方法
public static final MethodFilter MODEL_ATTRIBUTE_METHODS = method ->
		(!AnnotatedElementUtils.hasAnnotation(method, RequestMapping.class) &&
				AnnotatedElementUtils.hasAnnotation(method, ModelAttribute.class));
//自定义参数参数解析器集合
@Nullable
private List<HandlerMethodArgumentResolver> customArgumentResolvers;
//该类是一个特殊的参数解析类,其中保存了所有参数解析器的集合,并提供了参数解析的方法(实际上由其他解析器实现)。
@Nullable
private HandlerMethodArgumentResolverComposite argumentResolvers;
//处理注解@InitBinder中参数的解析器集合
@Nullable
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
//自定义返回值解析器集合
@Nullable
private List<HandlerMethodReturnValueHandler> customReturnValueHandlers;
//特殊的返回值解析器,类似HandlerMethodArgumentResolverComposite
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
//视图解析器集合
@Nullable
private List<ModelAndViewResolver> modelAndViewResolvers;
//判断request请求的MediaType类型处理解析处理工具,包括了文件扩展名处理的相关方法
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
//HttpMessageConverter结合,用来处理器request和response之间消息的转换
private List<HttpMessageConverter<?>> messageConverters;
//保存@RequestBodyAdvice或@ResponseBodyAdvice注解的方法,用来预处理请求或返回值
private List<Object> requestResponseBodyAdvice = new ArrayList<>();
//初始化WebDataBinder的回调接口,WebDataBinder类用于绑定request参数到JavaBean对象
@Nullable
private WebBindingInitializer webBindingInitializer;

//异步或响应式处理参数省略……

//设置redirect场景下,是否使用默认的Model
private boolean ignoreDefaultModelOnRedirect = false;
//缓存注解@SessionAttributes的策略或时间
private int cacheSecondsForSessionAttributeHandlers = 0;

//Session属性处理器的工具
private SessionAttributeStore sessionAttributeStore = new DefaultSessionAttributeStore();
//参数名称处理器
private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
//beanFactory
@Nullable
private ConfigurableBeanFactory beanFactory;

//缓存Session属性处理工具类SessionAttributesHandler
private final Map<Class<?>, SessionAttributesHandler> sessionAttributesHandlerCache = new ConcurrentHashMap<>(64);
//缓存处理器类中@InitBinder注解的方法
private final Map<Class<?>, Set<Method>> initBinderCache = new ConcurrentHashMap<>(64);
//缓存全局的@InitBinder注解的方法
private final Map<ControllerAdviceBean, Set<Method>> initBinderAdviceCache = new LinkedHashMap<>();
//缓存处理器类中@ModelAttribute注解且没有@RequestMapping注解的方法
private final Map<Class<?>, Set<Method>> modelAttributeCache = new ConcurrentHashMap<>(64);
//缓存全局的@ModelAttribute注解且没有@RequestMapping注解的方法
private final Map<ControllerAdviceBean, Set<Method>> modelAttributeAdviceCache = new LinkedHashMap<>();

属性字段的初始化:

  • 构造函数
    其中,messageConverters属性的初始化是通过构造函数实现,默认初始化了四个HttpMessageConverter实现类,代码如下:
public RequestMappingHandlerAdapter() {
	this.messageConverters = new ArrayList<>(4);
	this.messageConverters.add(new ByteArrayHttpMessageConverter());
	this.messageConverters.add(new StringHttpMessageConverter());
	try {
		this.messageConverters.add(new SourceHttpMessageConverter<>());
	}
	catch (Error err) {
		// Ignore when no TransformerFactory implementation is available
	}
	this.messageConverters.add(new AllEncompassingFormHttpMessageConverter());
}
  • afterPropertiesSet()方法
    因为RequestMappingHandlerAdapter实现了InitializingBean接口,所以通过afterPropertiesSet()方法实现了一些参数的初始化工作。代码如下:
@Override
public void afterPropertiesSet() {
	//初始化全局的三个变量modelAttributeAdviceCache、initBinderAdviceCache、requestResponseBodyAdvice
	initControllerAdviceCache();
	//初始化argumentResolvers参数,其中getDefaultArgumentResolvers()中定义了一些常用的参数解析器,并提供了自定义参数解析器的载入。
	if (this.argumentResolvers == null) {
		List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers();
		this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
	}
	//初始化initBinderArgumentResolvers,通过getDefaultInitBinderArgumentResolvers()获取默认的解析器
	if (this.initBinderArgumentResolvers == null) {
		List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers();
		this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
	}
	//初始化returnValueHandlers,通过getDefaultReturnValueHandlers()方法获取返回值解析器
	if (this.returnValueHandlers == null) {
		List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers();
		this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
	}
}

  在initControllerAdviceCache()方法中,完成了modelAttributeAdviceCache、initBinderAdviceCache、requestResponseBodyAdvice三个属性的初始化。其中,initBinderAdviceCache属性缓存了全局定义的@InitBinder注解方法;modelAttributeAdviceCache属性缓存了全局定义的@ModelAttribute注解且没有@RequestMapping注解方法;而requestResponseBodyAdvice缓存了实现了RequestBodyAdvice或ResponseBodyAdvice接口的类。

private void initControllerAdviceCache() {
	if (getApplicationContext() == null) {
		return;
	}
	//首先,获取带有注解 @ControllerAdvice的所有的类
	List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());

	List<Object> requestResponseBodyAdviceBeans = new ArrayList<>();
	//遍历带有注解 @ControllerAdvice的类
	for (ControllerAdviceBean adviceBean : adviceBeans) {
		Class<?> beanType = adviceBean.getBeanType();
		if (beanType == null) {
			throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
		}
		//查询beanType类中,所有的带有@ModelAttribute注解且没有@RequestMapping注解的方法,并存储到对应的属性中
		Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS);
		if (!attrMethods.isEmpty()) {
			this.modelAttributeAdviceCache.put(adviceBean, attrMethods);
		}
		//查询beanType类中,所有带有@InitBinder注解的方法,并存储到对应的属性中
		Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS);
		if (!binderMethods.isEmpty()) {
			this.initBinderAdviceCache.put(adviceBean, binderMethods);
		}
		//判断当前beanType是否实现了RequestBodyAdvice或ResponseBodyAdvice接口,并存储到定义的局部变量中,后面在存储到requestResponseBodyAdvice变量的最前面
		if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
			requestResponseBodyAdviceBeans.add(adviceBean);
		}
	}

	if (!requestResponseBodyAdviceBeans.isEmpty()) {
		this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans);
	}
	//日志处理
	if (logger.isDebugEnabled()) {
		int modelSize = this.modelAttributeAdviceCache.size();
		int binderSize = this.initBinderAdviceCache.size();
		int reqCount = getBodyAdviceCount(RequestBodyAdvice.class);
		int resCount = getBodyAdviceCount(ResponseBodyAdvice.class);
		if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) {
			logger.debug("ControllerAdvice beans: none");
		}
		else {
			logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize +
					" @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice");
		}
	}
}

  其他属性字段,大都通过getXXX/setXXX方法实现对应属性的获取和设置,这里不再粘贴代码。

核心方法实现

  在该类中实现了抽象类中定义的三个方法,其中getLastModifiedInternal()和supportsInternal()方法,只是进行了简单的返回,代码如下:

@Override
protected boolean supportsInternal(HandlerMethod handlerMethod) {
	return true;
}

protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
	return -1;
}

  在RequestMappingHandlerAdapter类中,handleInternal()方法是用来实现逻辑处理的方法,真正的处理请求又通过调用invokeHandlerMethod()方法进行实现,我们下面逐步分析该方法的实现逻辑:

@Override
protected ModelAndView handleInternal(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

	ModelAndView mav;
	//判断是否支持当前request的方法,该方法在WebContentGenerator类中定义,通过内置的supportedMethods属性来进行判断
	checkRequest(request);

	// 判断是否需要在同一会话上启用序列化调用,即序列化来自同一客户端的并行调用。
	if (this.synchronizeOnSession) {
		HttpSession session = request.getSession(false);
		if (session != null) {
			Object mutex = WebUtils.getSessionMutex(session);
			synchronized (mutex) {//需要序列化执行
				//核心的处理方法,后面详细分析
				mav = invokeHandlerMethod(request, response, handlerMethod);
			}
		}
		else {//不需要序列化执行
			mav = invokeHandlerMethod(request, response, handlerMethod);
		}
	}
	else {//不需要序列化执行
		mav = invokeHandlerMethod(request, response, handlerMethod);
	}
	//处理请求是否缓存的配置,通过设置响应的HTTP Header实现,实际的实现方法都是在WebContentGenerator类中定义的
	if (!response.containsHeader(HEADER_CACHE_CONTROL)) {
		if (getSessionAttributesHandler(handlerMethod).hasSessionAttributes()) {
			applyCacheSeconds(response, this.cacheSecondsForSessionAttributeHandlers);
		}
		else {
			prepareResponse(response);
		}
	}
	return mav;
}

  invokeHandlerMethod()方法实现了真正的请求处理逻辑,这个方法是该类中最核心最复杂的方法,因为在这里真正实现了从参数解析、处理器逻辑调用、返回值处理等工作,同时为了实现这一系列的功能,使用了很多的组件。代码如下:

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
		HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
	//封装了request, response两个对象
	ServletWebRequest webRequest = new ServletWebRequest(request, response);
	try {
		//获取创建WebDataBinder实例的工厂类,其中WebDataBinder类用于绑定request参数到JavaBean对象
		WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
		//获取创建Model实例的工厂类,用于Model对象的初始化工作
		ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
		//根据handlerMethod创建ServletInvocableHandlerMethod实例,而ServletInvocableHandlerMethod本来就继承于HandlerMethod类,只是增加了参数解析、处理器方法调用、返回值解析等逻辑,后续专门分析该类
		ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
		//为invocableMethod实例设置参数解析器
		if (this.argumentResolvers != null) {
			invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
		}
		//为invocableMethod实例设置返回值解析器
		if (this.returnValueHandlers != null) {
			invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
		}
		//设置前面获取的创建WebDataBinder实例的工厂类
		invocableMethod.setDataBinderFactory(binderFactory);
		//设置参数名称处理器
		invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);

		//创建ModelAndViewContainer对象,该对象承担着整个请求过程中数据的传递工作
		ModelAndViewContainer mavContainer = new ModelAndViewContainer();
		//初始化"input" flash 属性
		mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
		//初始化Model的属性
		modelFactory.initModel(webRequest, mavContainer, invocableMethod);
		//设置Redirect场景下,是否使用默认的Model
		mavContainer.setIgnoreDefaultModelOnRedirect(this.ignoreDefaultModelOnRedirect);

		//异步处理相关代码
		AsyncWebRequest asyncWebRequest = WebAsyncUtils.createAsyncWebRequest(request, response);
		asyncWebRequest.setTimeout(this.asyncRequestTimeout);

		WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
		asyncManager.setTaskExecutor(this.taskExecutor);
		asyncManager.setAsyncWebRequest(asyncWebRequest);
		asyncManager.registerCallableInterceptors(this.callableInterceptors);
		asyncManager.registerDeferredResultInterceptors(this.deferredResultInterceptors);

		if (asyncManager.hasConcurrentResult()) {
			Object result = asyncManager.getConcurrentResult();
			mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
			asyncManager.clearConcurrentResult();
			LogFormatUtils.traceDebug(logger, traceOn -> {
				String formatted = LogFormatUtils.formatValue(result, !traceOn);
				return "Resume with async result [" + formatted + "]";
			});
			invocableMethod = invocableMethod.wrapConcurrentResult(result);
		}
		//调用处理器的处理逻辑,在分析ServletInvocableHandlerMethod时,再详细分析,这里只需要知道通过该方法,实现了处理逻辑,并把返回结果存储到了mavContainer实例中了。
		invocableMethod.invokeAndHandle(webRequest, mavContainer);
		if (asyncManager.isConcurrentHandlingStarted()) {
			return null;
		}
		//构建通用的返回对象ModelAndView实例
		return getModelAndView(mavContainer, modelFactory, webRequest);
	}
	finally {
		//设置当前请求已被处理的标识
		webRequest.requestCompleted();
	}
}

  前面,介绍了invokeHandlerMethod()方法的核心处理逻辑,下面,我们开始分析其中一些组件的初始化或创建方式。首先,我们分析WebDataBinderFactory创建方法getDataBinderFactory()。

private WebDataBinderFactory getDataBinderFactory(HandlerMethod handlerMethod) throws Exception {
	//获取处理器对应的beanType
	Class<?> handlerType = handlerMethod.getBeanType();
	//首先从缓存中获取beanType对应类中,带有注解@InitBinder的方法的集合
	Set<Method> methods = this.initBinderCache.get(handlerType);
	if (methods == null) {//没有的话,然后通过MethodIntrospector.selectMethods()方法获取,并设置到缓存中
		methods = MethodIntrospector.selectMethods(handlerType, INIT_BINDER_METHODS);
		this.initBinderCache.put(handlerType, methods);
	}
	List<InvocableHandlerMethod> initBinderMethods = new ArrayList<>();
	// 遍历全局的带有注解@InitBinder的方法的集合(在afterPropertiesSet()已经初始化),把符合要求的方法通过createInitBinderMethod()方法创建成InvocableHandlerMethod实例并设置相关属性,并将实例对象添加到局部变量initBinderMethods中
	this.initBinderAdviceCache.forEach((clazz, methodSet) -> {
		if (clazz.isApplicableToBeanType(handlerType)) {
			Object bean = clazz.resolveBean();
			for (Method method : methodSet) {
				initBinderMethods.add(createInitBinderMethod(bean, method));
			}
		}
	});
	//遍历当前beanType类中带有注解@InitBinder的方法的集合,进行类似全局变量的处理,
	for (Method method : methods) {
		Object bean = handlerMethod.getBean();
		initBinderMethods.add(createInitBinderMethod(bean, method));
	}
	//创建ServletRequestDataBinderFactory实例对象,通过构造函数创建
	return createDataBinderFactory(initBinderMethods);
}

  然后,ModelFactory工厂类的创建和WebDataBinderFactory类的创建思路,基本一直,这里不再重复分析。然后,是创建ServletInvocableHandlerMethod实例的方法createInvocableHandlerMethod(),直接通过构造函数实现,代码如下:

protected ServletInvocableHandlerMethod createInvocableHandlerMethod(HandlerMethod handlerMethod) {
	return new ServletInvocableHandlerMethod(handlerMethod);
}

  最后,又通过调用getModelAndView()方法,实现了根据ModelAndViewContainer实例,创建保存视图和模型对象的ModelAndView实例。具体实现如下:

@Nullable
private ModelAndView getModelAndView(ModelAndViewContainer mavContainer,
		ModelFactory modelFactory, NativeWebRequest webRequest) throws Exception {
	//更新参数到Model
	modelFactory.updateModel(webRequest, mavContainer);
	//判断是否已经被处理完成
	if (mavContainer.isRequestHandled()) {
		return null;
	}
	ModelMap model = mavContainer.getModel();
	//创建ModelAndView 实例
	ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, mavContainer.getStatus());
	if (!mavContainer.isViewReference()) {//当不是使用引用时(即不是视图名称的字符串时),设置视图
		mav.setView((View) mavContainer.getView());
	}
	if (model instanceof RedirectAttributes) {//判断是否是Redirect类型
		//设置flash相关属性
		Map<String, ?> flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
		HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
		if (request != null) {
			RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
		}
	}
	return mav;
}

  至此,我们基本上分析了完了RequestMappingHandlerAdapter类中的相关代码,其中涉及到的ServletInvocableHandlerMethod类及其处理过程调用、WebDataBinder类、ModelAndViewContainer类和Model类、参数解析器、返回值解析器等组件并没有深入组件内部展开进行分析,后续我们将逐步分析这些组件的初始化过程和使用的逻辑。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

姠惢荇者

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值