一、概述
1.问题
之前我们讲到了通过请求找到了对应的映射器,也就是找到了对应的方法,那么如何调用这个方法呢,因为不同的接收请求方法有不同的方式,最简单的就是方法名就不一样,那么如何调用,这里采用了设计模式中的适配器模式,通过适配,进行判断,然后通过不同的方式调用方法,这就是HandlerAdpater 的主要功能
2.请求流程
之前介绍了HandlerMapping,我们通过请求找到了对应的handler,那么我们继续来看doDispatch() 下面的代码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = HttpMethod.GET.matches(method);
if (isGet || HttpMethod.HEAD.matches(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
我们先看没有找到对应的Handler 的对象,如果没有找到Handler对象的话,就执行noHandlerFound(processedRequest, response); 的方法,具体的代码如下:
protected void noHandlerFound(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (pageNotFoundLogger.isWarnEnabled()) {
pageNotFoundLogger.warn("No mapping for " + request.getMethod() + " " + getRequestUri(request));
}
if (this.throwExceptionIfNoHandlerFound) {
throw new NoHandlerFoundException(request.getMethod(), getRequestUri(request),
new ServletServerHttpRequest(request).getHeaders());
}
else {
response.sendError(HttpServletResponse.SC_NOT_FOUND);
}
}
上面的代码其实很简单,判断是否需要打印日志,如果需要,就直接打印日志,如果在查找Handler 的时候出错了,就直接抛出异常,如果没有出现异常,就直接将状态码设置成404,然后返回。
然后就是根据对应的Handler 获取对应的HandlerAdapter 。这个时候会调用getHandlerAdapter(mappedHandler.getHandler()); 方法,具体的代码如下:
protected HandlerAdapter getHandlerAdapter(Object handler) throws ServletException {
if (this.handlerAdapters != null) {
for (HandlerAdapter adapter : this.handlerAdapters) {
if (adapter.supports(handler)) {
return adapter;
}
}
}
throw new ServletException("No adapter for handler [" + handler +
"]: The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler");
}
上面运行到了 HandlerAdapter ,下面会先介绍一下HandlerAdapter 这个接口和其常用子类
二、HandlerAdapter
1.HandlerAdapter接口
该接口中有3个方法
public interface HandlerAdapter {
boolean supports(Object handler);
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
@Deprecated
long getLastModified(HttpServletRequest request, Object handler);
}
2.子类
HttpRequestHandlerAdapter
SimpleControllerHandlerAdapter
RequestMappingHandlerAdapter
RequestMappingHandlerAdapter
3.初始化
HandlerAdapter 和 之前介绍的 HandlerMapping 初始化思路是一样,调用链:
org.springframework.web.servlet.HttpServletBean#init()
--> org.springframework.web.servlet.FrameworkServlet#initServletBean()
--> org.springframework.web.servlet.FrameworkServlet#initWebApplicationContext()
--> org.springframework.web.servlet.DispatcherServlet#onRefresh()
--> org.springframework.web.servlet.DispatcherServlet#initStrategies()
--> org.springframework.web.servlet.DispatcherServlet#initHandlerAdapters()
通过上面的调用链,我们会调用初始化处理适配器方法 initHandlerAdapters
private void initHandlerAdapters(ApplicationContext context) {
this.handlerAdapters = null;
if (this.detectAllHandlerAdapters) {
Map<String, HandlerAdapter> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerAdapter.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerAdapters = new ArrayList<>(matchingBeans.values());
AnnotationAwareOrderComparator.sort(this.handlerAdapters);
}
}
else {
try {
HandlerAdapter ha = context.getBean(HANDLER_ADAPTER_BEAN_NAME, HandlerAdapter.class);
this.handlerAdapters = Collections.singletonList(ha);
}
catch (NoSuchBeanDefinitionException ex) {
}
}
if (this.handlerAdapters == null) {
this.handlerAdapters = getDefaultStrategies(context, HandlerAdapter.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerAdapters declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
上面的代码就是如果加了@EnableWebMvc 注解的话,就直接能从spring容器中取出对应的三个HandlerMapping ,如果没有加,则调用 getDefaultStrategies() 方法,
protected <T> List<T> getDefaultStrategies(ApplicationContext context, Class<T> strategyInterface) {
if (defaultStrategies == null) {
try {
ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, DispatcherServlet.class);
defaultStrategies = PropertiesLoaderUtils.loadProperties(resource);
}
catch (IOException ex) {
throw new IllegalStateException("Could not load '" + DEFAULT_STRATEGIES_PATH + "': " + ex.getMessage());
}
}
String key = strategyInterface.getName();
String value = defaultStrategies.getProperty(key);
if (value != null) {
String[] classNames = StringUtils.commaDelimitedListToStringArray(value);
List<T> strategies = new ArrayList<>(classNames.length);
for (String className : classNames) {
try {
Class<?> clazz = ClassUtils.forName(className, DispatcherServlet.class.getClassLoader());
Object strategy = createDefaultStrategy(context, clazz);
strategies.add((T) strategy);
}
catch (ClassNotFoundException ex) {
throw new BeanInitializationException(
"Could not find DispatcherServlet's default strategy class [" + className +
"] for interface [" + key + "]", ex);
}
catch (LinkageError err) {
throw new BeanInitializationException(
"Unresolvable class definition for DispatcherServlet's default strategy class [" +
className + "] for interface [" + key + "]", err);
}
}
return strategies;
}
else {
return Collections.emptyList();
}
}
上面的代码说如果spring容器中取不到,那么会从默认的配置文件中取,这个配置在通过IDEA中查看jar包 spring-webmvc-5.3.9.jar ,打开可以看到其中的一段配置
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter,\
org.springframework.web.servlet.function.support.HandlerFunctionAdapter
三、适配调用
上面我们通过doDispatch() 方法调用到了 HandlerAdapter ,上面我们介绍了这个接口及其子类,那么接下来我们按照类图来介绍一下接口
1.HttpRequestHandlerAdapter
我们先看一下这个类的结构,看看有没有利用spring的一些扩展点,来完成一些数据的初始化的工作,具体如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FOTL2A80-1634225752237)(HandlerAdapter.assets/image-20211013205417528.png)]
可以发现这个类没有实现spring的扩展点,就是实现了HandlerAdapter 这个接口
public class HttpRequestHandlerAdapter implements HandlerAdapter {
@Override
public boolean supports(Object handler) {
return (handler instanceof HttpRequestHandler);
}
@Override
@Nullable
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
((HttpRequestHandler) handler).handleRequest(request, response);
return null;
}
}
我们可以从上面的内容中看到,这个类很简单,调用supports 方法来进行适配,这个方法也只是进行了强转判断,并且在适配调用逻辑方法的适合,也很好理解,
测试代码
@Component("/name")
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("实现Controller接口方式");
return null;
}
}
在 getHandlerAdapter() 这个方法里面,会遍历 handlerAdapter 集合,来查找这个handlerAdapter 是否匹配这个Handler ,找到1个就直接返回,所以这个匹配是存在顺序的,第一步就是BeanNameUrlHandlerMapping ,也就是Bean的name是以“/”开头的都是会匹配到,并且这个类是实现了HttpRequestHandler 接口的类,才会返回这种适配器
2.SimpleControllerHandlerAdapter
有了上面的介绍,我们看这个就很好理解了,和上面的类似
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S8JJCjrB-1634225752238)(HandlerAdapter.assets/image-20211013210811874.png)]
对应的类内容:
public class SimpleControllerHandlerAdapter implements HandlerAdapter {
@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);
}
}
测试代码
@Component("/name")
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("实现Controller接口方式");
return null;
}
}
3.RequestMappingHandlerAdapter
首先看一下这个类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mzQtuNzz-1634225752240)(HandlerAdapter.assets/image-20211013211502256.png)]
这儿实现了ApplicationContextAware ,BeanFactoryAware ,InitializingBean 接口,这3个接口实现的方法都是在容器启动时运行的,其中ApplicationContextAware 中的接口的方法不用看的,因为是父类实现了而且也没有调用这个类中的方法,接下来看下BeanFactoryAware 接口中的setBeanFactory 的方法,具体的代码如下:
@Override
public void setBeanFactory(BeanFactory beanFactory) {
if (beanFactory instanceof ConfigurableBeanFactory) {
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
}
}
可以发现这个类就是Spring的bean的工厂设置给属性beanFactory,接下来我们继续看实现InitializingBean 中的afterPropertiesSet 方法,具体的代码如下:
public void afterPropertiesSet() {
initControllerAdviceCache();
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);
}
}
上面就是完成了一系列的初始化的工作,同时添加了处理方法的参数的处理器,以及一些处理方法返回值的处理器。这些在调用调用指定的方法和处理返回值的时候有用,后面笔者会讲到。看完了这些扩展点,我们需要看下这个类中的supports的方法,具体的代码如下
public final boolean supports(Object handler) {
return (handler instanceof HandlerMethod && supportsInternal((HandlerMethod) handler));
}
判断类型是否是HandlerMethod,而第二个方法默认是返回true,所以这种的匹配的规则就是,加了@RequestMapping或者@Controller的类,同时这个类中的所有加@RequestMapping的方法都是会被解析成HandlerMethod类型的handler我们再来看最后一个HandlerAdapter类
4.HandlerFunctionAdapter
类图
可以发现这个类实现Ordered 接口,没有实现其他的特殊的接口,可以得出没有利用Spring的扩展点,接下来我们就看这个类的supports的方法,具体的代码如下:
@Override
public boolean supports(Object handler) {
return handler instanceof HandlerFunction;
}
只要handler的类型是HandlerFunction就返回true,所以在什么时候会返回true呢?由上一篇博客,我们可以得出,如果查找出来的Handler是RouterFunctionMapping,这个方法就会返回true。至此SpringMVC的三大HandlerMapping,四大HandlerAdapter,笔者到此就讲完了。
er类
4.HandlerFunctionAdapter
类图
可以发现这个类实现Ordered 接口,没有实现其他的特殊的接口,可以得出没有利用Spring的扩展点,接下来我们就看这个类的supports的方法,具体的代码如下:
@Override
public boolean supports(Object handler) {
return handler instanceof HandlerFunction;
}
只要handler的类型是HandlerFunction就返回true,所以在什么时候会返回true呢?由上一篇博客,我们可以得出,如果查找出来的Handler是RouterFunctionMapping,这个方法就会返回true。至此SpringMVC的三大HandlerMapping,四大HandlerAdapter,笔者到此就讲完了。
|