简介
在这里介绍spring-mvc处理请求相关的源码
使用案例
编写一个spring-mvc项目。
1、使用maven,新建一个webapp项目
<!--注意,打包方式是war包-->
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--servlet依赖-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!--JSP依赖-->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>javax.servlet.jsp-api</artifactId>
<version>2.3.3</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--spring整合junit-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--引入spring-mvc框架-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.5.RELEASE</version>
</dependency>
<!--用于解析前端上传的文件-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
<build>
<finalName>spring-mvc-learn</finalName>
<plugins>
<!-- maven自带的tomcat插件,因为IDEA社区版不支持tomcat -->
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
<uriEncoding>UTF-8</uriEncoding>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
</build>
2、webapp/WEB-INF/web.xml,web项目的配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.1">
<display-name>spring-mvc-learn</display-name>
<!--配置前端控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--配置全局过滤器,处理中文乱码-->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<error-page>
<location>/error/error.jsp</location>
</error-page>
</web-app>
3、resources/spring-mvc.xml,spring-mvc的配置文件
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--扫描注解-->
<context:component-scan base-package="org.wyj" />
<!--开启注解驱动,使得mvc能够识别@Controller等注解,注册默认的处理器映射器和适配器-->
<mvc:annotation-driven />
<!--配置静态资源的访问-->
<mvc:default-servlet-handler />
</beans>
配置完成之后,就可以像在springboot中一样正常编写代码了。这种方式应该已经被淘汰了,现在基本都是直接使用springboot,不过用它来学习spring-mvc的源码应该还是很不错的,因为springboot中默认做了很多,不知道哪些才是spring-mvc相关的东西。
整体流程
在这里案例中,项目会打一个war包,部署到服务器上。
项目的启动流程:
- tomcat在启动web项目时,会解析项目中的web.xml,用户在web.xml中配置了DispatcherServlet,并且把spring的配置文件作为初始化参数传递给了它
- tomcat会实例化DispatcherServlet,然后调用它的生命周期方法,在DispatcherServlet内部,它会根据spring-mvc的配置文件,启动spring容器
- spring容器启动后,就可以正常处理用户请求了
DispatcherServlet的启动
DispatcherServlet启动过程中做的事情:
- 启动spring容器
- 初始化DispatcherServlet中处理请求的组件
DispatcherServlet的继承体系
HttpServletBean:HttpServlet的简单扩展,实现了它的生命周期方法
public abstract class HttpServletBean extends HttpServlet implements EnvironmentCapable, EnvironmentAware {
/** Logger available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
@Nullable
private ConfigurableEnvironment environment;
private final Set<String> requiredProperties = new HashSet<>(4);
// 初始化方法,在这个方法里启动spring容器
@Override
public final void init() throws ServletException {
}
/* 交给子类实现的扩展点 */
// 初始化dispatcherServlet实例
protected void initBeanWrapper(BeanWrapper bw) throws BeansException {
}
// 初始化spring容器
protected void initServletBean() throws ServletException {
}
}
FrameworkServlet:定义了servlet和spring容器相关的逻辑,存储了spring容器的实例
public abstract class FrameworkServlet extends HttpServletBean implements ApplicationContextAware {
// spring-mvc默认使用的spring容器 XmlWebApplicationContext
public static final Class<?> DEFAULT_CONTEXT_CLASS = XmlWebApplicationContext.class;
// spring配置文件的位置
@Nullable
private String contextConfigLocation;
// spring容器
@Nullable
private WebApplicationContext webApplicationContext;
}
DispatcherServlet:存储了spring-mvc相关的组件,处理请求的逻辑就是写在这里的
public class DispatcherServlet extends FrameworkServlet {
}
初始化方法
DispatcherSeervlet的实例是由服务器创建的,服务器同时还会调用它的生命周期相关的方法。
DispatcherServlet的初始化方法:定义在父类中,主要的逻辑是启动spring容器,
@Override
public final void init() throws ServletException {
// Set bean properties from init parameters.
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
if (!pvs.isEmpty()) {
try {
// 封装DispatcherServlet的实例
BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this);
ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext());
bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment()));
// 扩展点
initBeanWrapper(bw);
bw.setPropertyValues(pvs, true);
}
catch (BeansException ex) {
if (logger.isErrorEnabled()) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
}
throw ex;
}
}
// 初始化spring容器
// Let subclasses do whatever initialization they like.
initServletBean();
}
spring容器的创建
spring-mvc使用的spring容器:XmlWebApplicationContext
XmlWebApplicationContext的继承体系
AbstractRefreshableWebApplicationContext:这是spring mvc扩展出来的,它的父类和ClasspathXmlApplicationContext等spring容器的父类是一样的。
public abstract class AbstractRefreshableWebApplicationContext extends AbstractRefreshableConfigApplicationContext
implements ConfigurableWebApplicationContext, ThemeSource {
// 存储了servletContext和servletConfig,
@Nullable
private ServletContext servletContext;
/** Servlet config that this context runs in, if any. */
@Nullable
private ServletConfig servletConfig;
/** Namespace of this context, or {@code null} if root. */
@Nullable
private String namespace;
/** the ThemeSource for this ApplicationContext. */
@Nullable
private ThemeSource themeSource;
}
XmlWebApplicationContext:这个就是spring mvc使用的spring容器
public class XmlWebApplicationContext extends AbstractRefreshableWebApplicationContext { }
spring容器的启动
这里只介绍几个不一样的点:
1、创建spring容器的实例
protected WebApplicationContext createWebApplicationContext(@Nullable ApplicationContext parent) {
// 获取spring容器的类对象
Class<?> contextClass = getContextClass();
if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
throw new ApplicationContextException(
"Fatal initialization error in servlet with name '" + getServletName() +
"': custom WebApplicationContext class [" + contextClass.getName() +
"] is not of type ConfigurableWebApplicationContext");
}
// 创建spring容器的实例
ConfigurableWebApplicationContext wac =
(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);
// 设置环境信息
wac.setEnvironment(getEnvironment());
// 设置父容器
wac.setParent(parent);
// 保存配置文件的位置
String configLocation = getContextConfigLocation();
if (configLocation != null) {
wac.setConfigLocation(configLocation);
}
// 配置并且刷新spring容器
configureAndRefreshWebApplicationContext(wac);
return wac;
}
2、配置并且刷新spring容器
protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac) {
if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
// The application context id is still set to its original default value
// -> assign a more useful id based on available information
if (this.contextId != null) {
wac.setId(this.contextId);
}
else {
// Generate default id...
wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
ObjectUtils.getDisplayString(getServletContext().getContextPath()) + '/' + getServletName());
}
}
// 保存servletContext、servletConfig
wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
// 手动添加一个监听器,这个监听器随后用于实例化spring-mvc中处理请求的组件
wac.addApplicationListener(new SourceFilteringListener(wac, new ContextRefreshListener()));
// The wac environment's #initPropertySources will be called in any case when the context
// is refreshed; do it eagerly here to ensure servlet property sources are in place for
// use in any post-processing or initialization that occurs below prior to #refresh
ConfigurableEnvironment env = wac.getEnvironment();
if (env instanceof ConfigurableWebEnvironment) {
((ConfigurableWebEnvironment) env).initPropertySources(getServletContext(), getServletConfig());
}
// 扩展点
postProcessWebApplicationContext(wac);
// 添加初始化器
applyInitializers(wac);
// refresh方法
wac.refresh();
}
总结:spring容器的启动和普通spring容器的启动几乎一致,只是多了关于servlet相关组件的处理
DispatcherServlet的初始化
在spring容器启动时调用的refresh方法中,在最后一步,finishRefresh方法中,会发布上下文刷新完成(ContextRefreshedEvent)事件,spring mvc预先设置了监听器(SourceFilteringListener)来处理这个事件,最终,会调用DispatcherServlet中的initStrategies方法,来初始化DispatcherServlet
// DispatcherServlet类
protected void initStrategies(ApplicationContext context) {
// 初始化spring mvc中的九大组件,每个组件的初始化逻辑都差不多,如果spring容器中有这个组件,
// 就使用spring容器中的组件,否则使用默认的。
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
// 初始化处理器映射器
private void initHandlerMappings(ApplicationContext context) {
this.handlerMappings = null;
if (this.detectAllHandlerMappings) {
// 从spring容器中获取处理器映射器,这个映射器是之前解析配置文件的时候注册的
// Find all HandlerMappings in the ApplicationContext, including ancestor contexts.
Map<String, HandlerMapping> matchingBeans =
BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerMapping.class, true, false);
if (!matchingBeans.isEmpty()) {
// 把处理器映射器保存到DispatcherServlet中
this.handlerMappings = new ArrayList<>(matchingBeans.values());
// 排序
// We keep HandlerMappings in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerMappings);
}
}
else {
try {
HandlerMapping hm = context.getBean(HANDLER_MAPPING_BEAN_NAME, HandlerMapping.class);
this.handlerMappings = Collections.singletonList(hm);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, we'll add a default HandlerMapping later.
}
}
// 使用默认的处理器映射器,默认的处理器映射器配置在DispatcherServlet.properties中,
// 这个文件不对外暴漏,它配置了mvc使用的默认组件,在DispatcherServlet的静态代码块中
// 被加载。
// Ensure we have at least one HandlerMapping, by registering
// a default HandlerMapping if no other mappings are found.
if (this.handlerMappings == null) {
this.handlerMappings = getDefaultStrategies(context, HandlerMapping.class);
if (logger.isTraceEnabled()) {
logger.trace("No HandlerMappings declared for servlet '" + getServletName() +
"': using default strategies from DispatcherServlet.properties");
}
}
}
总结:DispatcherServlet启动的基本步骤:
- DispatcherServlet的实例化:由服务器来创建DispatcherServlet的实例,然后调用它的init方法,这是生命周期相关的方法
- spring容器的创建:在init方法中,会启动spring容器。web-mvc中的spring容器和普通的spring容器大致相同,只是会额外存储servletContext和servletConfig,同时,还会手动注册一个监听器,当前容器启动完成后,触发监听器,监听器来初始化DispatcherServlet中的组件
- DispatcherServlet中的组件:这些组件都是从spring容器中获取的,如果获取不到,使用默认配置。
处理请求的相关组件
处理器映射器 HandlerMapping
把请求中的url路径和处理器映射在一起。
HandlerMapping有多个实现,这里只了解默认实现 RequestMappingHandlerMapping,它会解析@RequestMapping注解,根据它来决定处理方法可以处理的请求。
RequestMappingHandlerMapping的继承体系:
HandlerMapping:
public interface HandlerMapping {
// 根据request对象,获取处理器的执行链
HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
AbstractHandlerMapping:HandlerMapping接口的核心实现
public abstract class AbstractHandlerMapping extends WebApplicationObjectSupport
implements HandlerMapping, Ordered, BeanNameAware { // 执行顺序上是最后执行,
// 交给子类扩展,获取处理请求的处理器。父类中负责拦截器、跨域相关的流程,最终生成一个调用链
protected abstract Object getHandlerInternal(HttpServletRequest request) throws Exception;
}
AbstractHandlerMethodMapping:
public abstract class AbstractHandlerMethodMapping<T> extends AbstractHandlerMapping implements InitializingBean {
// bean初始化时执行的方法,检测和注册处理器
@Override
public void afterPropertiesSet() {
initHandlerMethods();
}
// 交给子类实现的扩展点:处理器相关
protected abstract boolean isHandler(Class<?> beanType);
@Nullable
protected abstract T getMappingForMethod(Method method, Class<?> handlerType);
protected abstract Set<String> getMappingPathPatterns(T mapping);
@Nullable
protected abstract T getMatchingMapping(T mapping, HttpServletRequest request);
protected abstract Comparator<T> getMappingComparator(HttpServletRequest request);
}
RequestMappingInfoHandlerMapping:泛型实参RequestMappingInfo
public abstract class RequestMappingInfoHandlerMapping extends AbstractHandlerMethodMapping<RequestMappingInfo> {
}
RequestMappingHandlerMapping:实现类
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements MatchableHandlerMapping, EmbeddedValueResolverAware {
// 判断当前bean是否是一个处理器:检测bean有没有被@Controller或@RequestMapping标注
@Override
protected boolean isHandler(Class<?> beanType) {
return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
}
// 解析@RequestMapping注解,生成路径和方法之间的映射
@Override
@Nullable
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
// 解析方法上的@RequestMapping注解
RequestMappingInfo info = createRequestMappingInfo(method);
if (info != null) {
// 解析类上的@RequestMapping注解
RequestMappingInfo typeInfo = createRequestMappingInfo(handlerType);
if (typeInfo != null) {
// 将方法上的注解信息和类上的注解信息合并
info = typeInfo.combine(info);
}
// 这里的前缀,如果没有特殊配置,就是没有
String prefix = getPathPrefix(handlerType);
if (prefix != null) {
info = RequestMappingInfo.paths(prefix).build().combine(info);
}
}
return info;
}
}
总结:handlerMapping负责在spring容器启动时,解析@RequestMapping注解,生成url路径和处理器之间的映射,处理是一个方法,通过反射调用。
处理器 HandlerMethod
处理器:内部封装了被@RequestMapping标注的方法,负责处理用户请求
HandlerMethod:
public class HandlerMethod {
/** Logger that is available to subclasses. */
protected final Log logger = LogFactory.getLog(getClass());
// 处理器方法对应的bean
private final Object bean;
@Nullable
private final BeanFactory beanFactory;
// 方法的反射信息
private final Class<?> beanType;
private final Method method;
private final Method bridgedMethod;
private final MethodParameter[] parameters;
@Nullable
private volatile List<Annotation[][]> interfaceParameterAnnotations;
}
处理器适配器 HandlerAdapter
DispatcherServlet使用处理器适配器来调用处理器,实现解耦
HandlerAdapter:
public interface HandlerAdapter {
// 判断处理器适配器是否支持某个处理器
boolean supports(Object handler);
// 调用处理器
@Nullable
ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception;
// 用于支持响应头中的Last-Modified字段,它用于指示资源的最后修改时间。
// 通常用于缓存机制和条件请求,以提高性能和减少不必要的数据传输。
long getLastModified(HttpServletRequest request, Object handler);
}
RequestMappingHandlerAdapter:支持@RequestMapping注解的处理器适配器
public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter
implements BeanFactoryAware, InitializingBean {
// 参数解析器的集合
@Nullable
private HandlerMethodArgumentResolverComposite initBinderArgumentResolvers;
// 返回值解析器的集合
@Nullable
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
// 初始化方法:初始化参数解析器、返回值解析器
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBody advice beans
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);
}
}
}
spring mvc默认提供的参数解析器:所以spring mvc可以解析多种类型的参数
private List<HandlerMethodArgumentResolver> getDefaultArgumentResolvers() {
List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
// Annotation-based argument resolution
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), false)); // @RequestParam
resolvers.add(new RequestParamMapMethodArgumentResolver());
resolvers.add(new PathVariableMethodArgumentResolver()); // @PathVariable
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 resolution
resolvers.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 arguments
if (getCustomArgumentResolvers() != null) {
resolvers.addAll(getCustomArgumentResolvers());
}
// Catch-all
resolvers.add(new RequestParamMethodArgumentResolver(getBeanFactory(), true));
resolvers.add(new ServletModelAttributeMethodProcessor(true));
return resolvers;
}
参数解析器
参数解析器是解析处理器方法上的参数,根据解析出的key从request实例中取值。
HandlerMethodArgumentResolver:
public interface HandlerMethodArgumentResolver {
// 判断当前解析器是否支持指定解析指定参数,例如,被@RequestParam标注的参数,只可以被特定的解析器解析。
boolean supportsParameter(MethodParameter parameter);
// 解析参数
@Nullable
Object resolveArgument(MethodParameter parameter, @Nullable ModelAndViewContainer mavContainer,
NativeWebRequest webRequest, @Nullable WebDataBinderFactory binderFactory) throws Exception;
}
RequestParamMethodArgumentResolver:支持@RequestParam的参数解析器
public class RequestParamMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
implements UriComponentsContributor {
// 解析方法上的@RequestParam注解
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
RequestParam ann = parameter.getParameterAnnotation(RequestParam.class);
return (ann != null ? new RequestParamNamedValueInfo(ann) : new RequestParamNamedValueInfo());
}
}
PathVariableMethodArgumentResolver:支持@PathVariable的参数解析器
public class PathVariableMethodArgumentResolver extends AbstractNamedValueMethodArgumentResolver
implements UriComponentsContributor {
private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class);
// 判断当前解析器是否支持解析指定参数
@Override
public boolean supportsParameter(MethodParameter parameter) {
// 判断方法的形参上是否被@PathVariable注解标注
if (!parameter.hasParameterAnnotation(PathVariable.class)) {
return false;
}
if (Map.class.isAssignableFrom(parameter.nestedIfOptional().getNestedParameterType())) {
PathVariable pathVariable = parameter.getParameterAnnotation(PathVariable.class);
return (pathVariable != null && StringUtils.hasText(pathVariable.value()));
}
return true;
}
// 解析参数上的@PathVariable注解
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
PathVariable ann = parameter.getParameterAnnotation(PathVariable.class);
Assert.state(ann != null, "No PathVariable annotation");
return new PathVariableNamedValueInfo(ann);
}
}
返回值解析器
HandlerMethodReturnValueHandler:顶层接口
public interface HandlerMethodReturnValueHandler {
// 方法是否支持处理返回值
boolean supportsReturnType(MethodParameter returnType);
// 处理返回值
void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception;
}
ViewNameMethodReturnValueHandler: 返回值是一个字符串
public class ViewNameMethodReturnValueHandler implements HandlerMethodReturnValueHandler {
@Nullable
private String[] redirectPatterns;
// 判断当前处理器是否支持指定类型的返回值:
@Override
public boolean supportsReturnType(MethodParameter returnType) {
Class<?> paramType = returnType.getParameterType();
// 如果返回值是一个字符串,就使用当前返回值处理器
return (void.class == paramType || CharSequence.class.isAssignableFrom(paramType));
}
// 处理返回值:
@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
// 将视图名称设置到ModelAndView中,用于随后渲染
if (returnValue instanceof CharSequence) {
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null){
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
// 如果视图名称以“redirect:”开头,证明视图是一个重定向请求
protected boolean isRedirectViewName(String viewName) {
return (PatternMatchUtils.simpleMatch(this.redirectPatterns, viewName) || viewName.startsWith("redirect:"));
}
}
RequestResponseBodyMethodProcessor:处理@RequestBody和@ResponseBody注解。
public class RequestResponseBodyMethodProcessor extends AbstractMessageConverterMethodProcessor {
// 判断当前处理器是否支持处理指定参数,这里是寻找方法或类上有没有@RequestBody注解
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(RequestBody.class);
}
// 判断当前处理器是否支持处理指定返回值,这里是寻找方法或类上有没有@ResponseBody注解
@Override
public boolean supportsReturnType(MethodParameter returnType) {
return (AnnotatedElementUtils.hasAnnotation(returnType.getContainingClass(), ResponseBody.class) ||
returnType.hasMethodAnnotation(ResponseBody.class));
}
}
ModelAndView
数据和视图:数据是一个map,视图是一个url路径。spring-mvc会根据url路径,加载页面,然后把数据渲染到页面中。
public class ModelAndView {
// 视图
/** View instance or view name String. */
@Nullable
private Object view;
// 数据
/** Model Map. */
@Nullable
private ModelMap model;
}
处理请求的整体流程
spring-mvc使用DispatcherServlet来处理请求,当有请求到来时,web容器首先会调用servlet中的service方法,这是servlet规范中的生命周期方法。
这里直接看处理请求的核心流程:doDispatch方法。
doDispatch方法:这个方法就是mvc框架处理请求的整体流程,在这个方法中,会调用spring mvc中的组件来处理请求。
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 {
// 判断是不是一个上传文件请求,如果是,这里会进一步解析request实例。要处理上传文件请求,需要用户
// 手动配置解析器。随后介绍
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 获取处理器,这里会根据请求中的url路径,获取可以处理这个路径的处理器。
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
// 获取处理器适配器,由处理器适配器来调用处理器,处理用户请求
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 这里是支持last-modified响应头,它是缓存相关的,使用head方法或get方法发送请求,判断如果
// 资源没有发生变化,证明客户端的缓存没有失效,否则,客户端就需要重新获取资源作为本地缓存。
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
// 拦截器的preHandle方法,在调用处理器之前执行
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 使用处理器适配器,调研处理器,处理用户请求
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 处理返回的视图
applyDefaultViewName(processedRequest, mv);
// 在这个方法中,在解析完视图之后,会调用拦截器的afterCompletion方法
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
catch (Throwable err) {
// As of 4.3, we're processing Errors thrown from handler methods as well,
// making them available for @ExceptionHandler methods and other scenarios.
dispatchException = new NestedServletException("Handler dispatch failed", err);
}
// 发布请求处理完成事件
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Throwable err) {
triggerAfterCompletion(processedRequest, response, mappedHandler,
new NestedServletException("Handler processing failed", err));
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
doDispatch方法的详细流程:
第一步 检查是不是上传文件的请求
检查是不是上传文件的请求,如果是,解析请求。这一步需要用户配置解析器,由解析器来检查是不是上传文件请求,然后把请求重新解析,把文件封装到MultiPart对象中。随后介绍文件上传请求时在详细讲解。
第二步:调用处理器映射器,获取处理器
getHandler方法:
@Nullable
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
// 遍历处理器映射器,由处理器映射器来判断使用哪个处理器来处理当前请求。
for (HandlerMapping mapping : this.handlerMappings) {
// 获取处理器,处理器映射器内部还会获取拦截器,判断拦截器是否要拦截当前请求,最终返回一个调用链。
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
默认使用的处理器映射器是RequestMappingHandlerMapping,用于支持基于@RequestMapping的处理器。在spring容器启动时,它会检测bean中的@RequestMapping注解,生成url路径和处理器方法之间的映射关系。
RequestMappingHandlerMapping中的根据request获取处理器的方法:getHandler
@Override
@Nullable
public final HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
// 获取处理器,每个处理器都有自己可以处理的路径,根据路径和处理器之间的映射关系来判断处理器是否可以处理当前请求。
// 获取当前请求的路径:request.getRequestURI()
Object handler = getHandlerInternal(request);
if (handler == null) {
handler = getDefaultHandler();
}
if (handler == null) {
return null;
}
// Bean name or resolved handler?
if (handler instanceof String) {
String handlerName = (String) handler;
handler = obtainApplicationContext().getBean(handlerName);
}
// 如果当前请求有可以应用的拦截器,在这里返回拦截器加处理器的调用链
HandlerExecutionChain executionChain = getHandlerExecutionChain(handler, request);
if (logger.isTraceEnabled()) {
logger.trace("Mapped to " + handler);
}
else if (logger.isDebugEnabled() && !request.getDispatcherType().equals(DispatcherType.ASYNC)) {
logger.debug("Mapped to " + executionChain.getHandler());
}
if (CorsUtils.isCorsRequest(request)) {
CorsConfiguration globalConfig = this.corsConfigurationSource.getCorsConfiguration(request);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, request);
CorsConfiguration config = (globalConfig != null ? globalConfig.combine(handlerConfig) : handlerConfig);
executionChain = getCorsHandlerExecutionChain(request, executionChain, config);
}
return executionChain;
}
第三步:获取处理器适配器
getHandlerAdapter方法:
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");
}
默认使用的处理器适配器是 RequestMappingHandlerAdapter,调用基于@RequestMapping注解的处理器
第四步:处理器适配器
底层是通过反射,调用可以处理请求的方法
默认的处理器适配器(RequestMappingHandlerAdapter)中处理请求的方法
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
WebDataBinderFactory binderFactory = getDataBinderFactory(handlerMethod);
ModelFactory modelFactory = getModelFactory(handlerMethod, binderFactory);
// 创建处理器实例
ServletInvocableHandlerMethod invocableMethod = createInvocableHandlerMethod(handlerMethod);
if (this.argumentResolvers != null) {
// 向处理器实例中添加参数解析器
invocableMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
// 向处理器实例中添加返回值解析器
invocableMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
invocableMethod.setDataBinderFactory(binderFactory);
invocableMethod.setParameterNameDiscoverer(this.parameterNameDiscoverer);
// ModelAndView
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
mavContainer.addAllAttributes(RequestContextUtils.getInputFlashMap(request));
modelFactory.initModel(webRequest, mavContainer, invocableMethod);
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);
}
// 调用处理器来处理请求
invocableMethod.invokeAndHandle(webRequest, mavContainer);
if (asyncManager.isConcurrentHandlingStarted()) {
return null;
}
// 设置modelAndView
return getModelAndView(mavContainer, modelFactory, webRequest);
}
finally {
webRequest.requestCompleted();
}
}
第五步:处理器
在处理器中,调用参数解析器,解析处理器方法上的参数,然后从request实例中获取参数的值,然后使用这些参数值来调用处理器方法,方法执行完成后,使用返回值解析器来解析方法的返回值,把返回值设置的ModelAndView中,随后会把这里面的数据设置到response实例中。
invokeAndHandle:
public void invokeAndHandle(ServletWebRequest webRequest, ModelAndViewContainer mavContainer,
Object... providedArgs) throws Exception {
// 执行处理器,获取返回值
Object returnValue = invokeForRequest(webRequest, mavContainer, providedArgs);
setResponseStatus(webRequest);
// 如果返回值为null
if (returnValue == null) {
if (isRequestNotModified(webRequest) || getResponseStatus() != null || mavContainer.isRequestHandled()) {
mavContainer.setRequestHandled(true);
return;
}
}
else if (StringUtils.hasText(getResponseStatusReason())) {
mavContainer.setRequestHandled(true);
return;
}
mavContainer.setRequestHandled(false);
Assert.state(this.returnValueHandlers != null, "No return value handlers");
try {
// 调用返回值解析器,解析返回值
this.returnValueHandlers.handleReturnValue(
returnValue, getReturnValueType(returnValue), mavContainer, webRequest);
}
catch (Exception ex) {
if (logger.isTraceEnabled()) {
logger.trace(formatErrorForReturnValue(returnValue), ex);
}
throw ex;
}
}
参数解析器
支持@RequestMapping的参数解析器,根据参数名从request实例中获取参数值 RequestParamMethodArgumentResolver
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
HttpServletRequest servletRequest = request.getNativeRequest(HttpServletRequest.class);
// 如果请求是文件上传请求
// ...
if (arg == null) {
// 从request实例中获取参数
String[] paramValues = request.getParameterValues(name);
if (paramValues != null) {
arg = (paramValues.length == 1 ? paramValues[0] : paramValues);
}
}
return arg;
}
返回值解析器
ViewNameMethodReturnValueHandler
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {
if (returnValue instanceof CharSequence) {
// 将返回值作为一个视图名称,写到ModelAndView中
String viewName = returnValue.toString();
mavContainer.setViewName(viewName);
if (isRedirectViewName(viewName)) {
mavContainer.setRedirectModelScenario(true);
}
}
else if (returnValue != null){
// should not happen
throw new UnsupportedOperationException("Unexpected return type: " +
returnType.getParameterType().getName() + " in method: " + returnType.getMethod());
}
}
第六步:处理视图
主要是把ModelAndView中的数据设置到response实例中。
总结
处理请求的大致流程:
- 根据请求,获取可以处理请求的处理器
- 根据处理器,获取处理器适配器
- 使用处理器适配器,调用处理器,来处理请求,处理器返回视图和数据
- 调用视图解析器来处理之前返回的视图和数据
其它
处理上传文件的请求
判断请求头Content-Type是不是以”multipart/“开头,如果是,证明这是一个上传文件请求。如果是一个上传文件请求,会调用专用的解析器来解析请求,把文件封装到特定对象中
配置文件解析器的案例:
<!--配置文件解析器-->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver" >
<property name="defaultEncoding" value="UTF-8" />
<property name="maxUploadSize" value="17367648787" />
</bean>
判断当前请求是否是一个上传文件请求:核心逻辑是判断请求头中content-type字段是不是以”multipart/“开头
// StandardServletMultipartResolver类
@Override
public boolean isMultipart(HttpServletRequest request) {
// 判断请求头中content-type字段是不是以”multipart/“开头
return StringUtils.startsWithIgnoreCase(request.getContentType(), "multipart/");
}
进一步解析上传文件请求:
// CommonsMultipartResolver类
@Override
public MultipartHttpServletRequest resolveMultipart(final HttpServletRequest request) throws MultipartException {
Assert.notNull(request, "Request must not be null");
// 是否延迟解析
if (this.resolveLazily) {
return new DefaultMultipartHttpServletRequest(request) {
@Override
protected void initializeMultipart() {
MultipartParsingResult parsingResult = parseRequest(request);
setMultipartFiles(parsingResult.getMultipartFiles());
setMultipartParameters(parsingResult.getMultipartParameters());
setMultipartParameterContentTypes(parsingResult.getMultipartParameterContentTypes());
}
};
}
else {
// 解析出上传的文件和其它普通的参数,这里依赖第三方库 commons-fileupload
MultipartParsingResult parsingResult = parseRequest(request);
return new DefaultMultipartHttpServletRequest(request, parsingResult.getMultipartFiles(),
parsingResult.getMultipartParameters(), parsingResult.getMultipartParameterContentTypes());
}
}
把上传文件的请求解析为何普通请求一样,就可以使用正常流程来处理请求了
拦截器
案例
第一步:编写一个拦截器
// 拦截器需要实现HandlerInterceptor接口
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request
, HttpServletResponse response
, Object handler) throws Exception {
System.out.println("-----执行MyInterceptor拦截器的preHandle方法-----");
return true; // 返回true表示后面的处理器正常执行
}
@Override
public void postHandle(HttpServletRequest request
, HttpServletResponse response
, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("-----执行MyInterceptor拦截器的postHandle方法-----");
}
@Override
public void afterCompletion(HttpServletRequest request
, HttpServletResponse response
, Object handler, Exception ex) throws Exception {
System.out.println("-----执行MyInterceptor拦截器的afterCompletion方法-----");
}
}
第二步:在spring-mvc.xml中配置拦截器
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<!--拦截器拦截的路径,可配置多个mapping标签-->
<mvc:mapping path="/user-intercept"/>
<!--拦截器实例-->
<bean class="org.wyj.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
源码分析
拦截器的核心接口 HandlerInterceptor:
public interface HandlerInterceptor {
// 在请求被处理之前进行操作
default boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler)
throws Exception {
return true;
}
// 在请求被处理之后,但结果还没有渲染前进行操作,可以改变响应结果
default void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
@Nullable ModelAndView modelAndView) throws Exception {
}
// 所有的请求响应结束后执行善后工作,清理对象,关闭资源
default void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler,
@Nullable Exception ex) throws Exception {
}
}
拦截器在容器中的实例:MappedInterceptor,解析配置文件时,每个mvc:interceptor标签都会被解析为一个MappedInterceptor实例,它封装了拦截器实例和它要拦截的路径。
public final class MappedInterceptor implements HandlerInterceptor {
// 拦截器要拦截的路径
@Nullable
private final String[] includePatterns;
// 拦截器不拦截的路径
@Nullable
private final String[] excludePatterns;
// 拦截器实例
private final HandlerInterceptor interceptor;
// 路径匹配器
@Nullable
private PathMatcher pathMatcher;
}
拦截器的创建:以RequestMappingHandlerMapping为例,它会获取spring容器中所有的拦截器实例,然后保存起来
protected void detectMappedInterceptors(List<HandlerInterceptor> mappedInterceptors) {
// 从spring容器中获取所有MappedInterceptor类型的bean
mappedInterceptors.addAll(
BeanFactoryUtils.beansOfTypeIncludingAncestors(
obtainApplicationContext(),
MappedInterceptor.class, true, false).values());
}
拦截器是如何拦截请求的:在寻找处理器时,会遍历拦截器,如果拦截器需要拦截指定请求,会把拦截器编织到调用链中,然后在之前介绍的整体流程中,依次调用拦截器中的方法
protected HandlerExecutionChain getHandlerExecutionChain(Object handler, HttpServletRequest request) {
// 创建调用链实例
HandlerExecutionChain chain = (handler instanceof HandlerExecutionChain ?
(HandlerExecutionChain) handler : new HandlerExecutionChain(handler));
// 获取请求路径
String lookupPath = this.urlPathHelper.getLookupPathForRequest(request);
// 遍历拦截器
for (HandlerInterceptor interceptor : this.adaptedInterceptors) {
// 如果拦截器的类型是MappedInterceptor,会根据拦截器中指定的路径,判断当前拦截器是否
// 需要拦截当前请求, 如果需要,就把拦截器添加到调用链中
if (interceptor instanceof MappedInterceptor) {
MappedInterceptor mappedInterceptor = (MappedInterceptor) interceptor;
if (mappedInterceptor.matches(lookupPath, this.pathMatcher)) {
chain.addInterceptor(mappedInterceptor.getInterceptor());
}
}
else {
// 如果拦截器的类型不是MappedInterceptor,直接添加到调用链中
chain.addInterceptor(interceptor);
}
}
return chain;
}
@ControllerAdvice注解的处理
@ControllerAdvice注解用于统一处理控制器层的异常。
案例:
@ControllerAdvice
public class ControllerExceptionHandler {
@ExceptionHandler
@ResponseBody
public String exceptionHandler(Exception exception){
return "happened exception";
}
}
spring mvc内部使用HandlerExceptionResolver来处理异常
异常解析器 HandlerExceptionResolver
它是DispatcherServlet中的组件,当处理器出现异常时,DispatcherServlet会调用它来处理异常。
ExceptionHandlerExceptionResolver
HandlerExceptionResolver的一种实现,用来处理@ControllerAdvice、@ExceptionHandler注解,spring容器启动的时候,它会被注入到容器中。
1、ExceptionHandlerExceptionResolver的初始化方法:这里直接看解析@ControllerAdvice、@ExceptionHandler注解的逻辑
// 解析@ControllerAdvice、@ExceptionHandler
private void initExceptionHandlerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
// 解析@ControllerAdvice,获取所有被@ControllerAdvice标注的类,这里是根据bean信息来
// 获取beanName,因为此时可能还有bean没有创建完成,所以只能操作bean信息,调用方法时再
// 根据beanName来获取bean的实例
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
for (ControllerAdviceBean adviceBean : adviceBeans) {
Class<?> beanType = adviceBean.getBeanType();
if (beanType == null) {
throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean);
}
// 获取bean中被@ExceptionHandler标注的方法
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(beanType);
if (resolver.hasExceptionMappings()) {
// 如果有这样的方法,将方法添加到缓存中
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(beanType)) {
this.responseBodyAdvice.add(adviceBean);
}
}
if (logger.isDebugEnabled()) {
int handlerSize = this.exceptionHandlerAdviceCache.size();
int adviceSize = this.responseBodyAdvice.size();
if (handlerSize == 0 && adviceSize == 0) {
logger.debug("ControllerAdvice beans: none");
}
else {
logger.debug("ControllerAdvice beans: " +
handlerSize + " @ExceptionHandler, " + adviceSize + " ResponseBodyAdvice");
}
}
}
总结:它实现了spring的初始化扩展点,在扩展点中,解析@ControllerAdvice、@ExceptionHandler注解
2、ExceptionHandlerExceptionResolver的调用:
在DispatcherServlet中,获取容器中所有HandlerExceptionResolver类型的bean
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
// 获取容器中所有HandlerExceptionResolver类型的bean
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
AnnotationAwareOrderComparator.sort(this.handlerExceptionResolvers);
}
}
// ...
}
调用时机:请求处理完之后,如果有异常,会调用processHandlerException方法,在这个方法中调用异常处理器
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
@Nullable Object handler, Exception ex) throws Exception {
// Success and error responses may use different content types
request.removeAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
if (this.handlerExceptionResolvers != null) {
for (HandlerExceptionResolver resolver : this.handlerExceptionResolvers) {
// 处理异常
exMv = resolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
}
// ...
}
处理异常的方法:
@Nullable
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, @Nullable HandlerMethod handlerMethod, Exception exception) {
// 获取类中被@ExceptionHandler标注的方法
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
// 如果没有,返回null
if (exceptionHandlerMethod == null) {
return null;
}
if (this.argumentResolvers != null) {
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
}
if (this.returnValueHandlers != null) {
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
}
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
try {
if (logger.isDebugEnabled()) {
logger.debug("Using @ExceptionHandler " + exceptionHandlerMethod);
}
// 调用类中被@ExceptionHandler标注的方法来处理异常
Throwable cause = exception.getCause();
if (cause != null) {
// Expose cause as provided argument as well
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, cause, handlerMethod);
}
else {
// Otherwise, just the given exception as-is
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, exception, handlerMethod);
}
}
catch (Throwable invocationEx) {
// Any other than the original exception is unintended here,
// probably an accident (e.g. failed assertion or the like).
if (invocationEx != exception && logger.isWarnEnabled()) {
logger.warn("Failure in @ExceptionHandler " + exceptionHandlerMethod, invocationEx);
}
// Continue with default processing of the original exception...
return null;
}
// ...
}
要注意的是,spring mvc中的异常解析器有一个处理链,如果异常解析器本身出了异常,还会链条上的下一个节点来处理异常,直到异常被处理完成,此时,给前端返回的异常是异常处理器中的异常。
总结
spring处理请求的整体流程:
- 第一步:服务器 -> 前端控制器:用户发送请求到服务器,服务器调用前端控制器,获取请求信息
- 第二步:前端控制器 -> 处理映射器(HandlerMapping):前端控制器收到请求后,调用处理映射器,处理器映射器返回用户编写的处理器,就是使用@RequestMapping注解的方法
- 第三步:前端控制器 -> 处理适配器(HandlerAdapter):前端控制器调用处理适配器来执行用户编写的处理器,并且返回ModelAndView,也就是处理器指定的视图
- 第四步:处理器,处理用户请求,在它的内部,会调用参数解析器、返回值解析器来处理请求
- 第五步:前端控制器 -> 视图解析器(ViewReslover):前端控制器将ModelAndView传递给视图解析器,视图解析器解析后返回具体的view
- 第六步:前端控制器 -> 返回响应:前端控制器根据view,将模型数据填充至视图中,然后把视图作为响应返回。