SpringMVC
SpringMVC是一个基于三层处理架构的表现层处理框架。三层架构是指:表现层、业务层、数据访问层。表现层对应于业务中的视图、数据和servlet。MVC旨在对视图和servlet解耦,使控制器的逻辑与view分离,降低开发难度。
1. Spring容器启动
通常如果我们希望通过注解的方式来进行Spring MVC开发,会在***-servlet.xml中加入<mvc:annotation-driven/>标签来告诉Spring我们的目的。
该标签被org.springframework.web.servlet.config.AnnotationDrivenBeanDefinitionParser
解析,向Spring容器中注册了以下8个Bean实例。
-
1.1
RequestMappingHandlerMapping
对象创建
RequestMappingHandlerMapping
实现了InitializingBean
接口,在Spring创建RequestMappingHandlerMapping
对象实例之后,调用初始化方法时,会调用其afterPropertiesSet()
方法。
protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
throws Throwable {
boolean isInitializingBean = (bean instanceof InitializingBean);
if (isInitializingBean &&
(mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
if (System.getSecurityManager() != null) {
//....
}
else {//调用InitializingBean的afterPropertiesSet方法。
((InitializingBean) bean).afterPropertiesSet();
}
}
//调用自定义初始化方法。。。省略,不关心
}
在afterPropertiesSet()
中直接调用initHandlerMethods()
方法,去遍历beanDefinitionName
中注册的bean, 判断是否被@Controller
注解,如果是,则检查被标注为@RequestMapping
的方法,创建RequestMappingInfo
对象并将RequestMappingInfo
:处理器方法(controller中的对应方法)注册到HandlerMapping。同时将url:RequestMappingInfo注册到HandlerMapping
。 当获取到请求url时,通过url找到mapping,然后通过mapping找到method。
public void afterPropertiesSet() {
initHandlerMethods();
}
//Scan beans in the ApplicationContext, detect and register handler methods.
protected void initHandlerMethods() {
//扫描所有注册的Bean
String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(),
Object.class) : getApplicationContext().getBeanNamesForType(Object.class));
//遍历这些Bean,依次判断是否是处理器,并检测其HandlerMethod
for (String beanName : beanNames) {
if (isHandler(getApplicationContext().getType(beanName))){
detectHandlerMethods(beanName);
}
}
//这个方法是个空实现,不管他
handlerMethodsInitialized(getHandlerMethods());
}
2. 请求处理流程
从DispatcherServlet
接收到请求doDispatch开始说起。
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ... 省略
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// 获取拦截器链和handler
mappedHandler = this.getHandler(processedRequest);
// ... 省略诸多代码
// 遍历interceptors, 调用interceptor.postHandle()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 由于需要支持众多第三方的handler,如:servlet, controller, HttpRequestHandler
// 不能去修改人家的源码,要实现灵活的支持就可以考虑使用适配器模式
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// ... 省略诸多代码
// 遍历interceptors, 调用interceptor.postHandle()
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
// 捕获异常并处理
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
// 遍历interceptors, 调用interceptor.afterCompletion()
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
// ...
}
}
3. 拦截器 原理与实现
-
3.1 拦截器的实现
实现HandlerInterceptor接口
public class AuthInterceptor implements HandlerInterceptor {
//...
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
-
3.2 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Autowired
private TimeInterceptor timeInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(timeInterceptor).excludePattern("pattern");
}
}
-
3.3 拦截器是如何生效的
- 向容器中创建
RequestMappingHandlerMapping
对象时,通过WebMvcConfigurationSupport::requestMappingHandlerMapping()
方法实现,对mapping注册拦截器,然后获取WebMvcConfigurer
对象,执行其addInterceptors()
方法,将拦截器注册。 -
DispatcherServlet.doDispatch()
方法中getHandler()
会根据request对象从HandlerMapping获取拦截器链和HanderMethod。 -
dispatcherServlet
对象的applyPreHandle()
等方法调用拦截器的pre/postHandle等方法。
@Bean
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
RequestMappingHandlerMapping mapping = this.createRequestMappingHandlerMapping();
//...
mapping.setInterceptors(this.getInterceptors());
// ...
}
4. 异常处理机制
-
4.1 定义异常处理方式
-
@ExceptionHandler
在特定Controller上处理(not recommend) - 实现
HandlerExceptionResolver
接口完成全局异常处理 -
@ControllerAdvice
+@ExceptionHandler
全局异常处理(recommend)
这三种方式实现起来都比较简单,就不举例了。
-
4.2 解析异常处理advice
第一张图提到SpringMVC向容器注入ExceptionHandlerExceptionResolver
,这个类同样实现了InitializingBean
接口,在afterpropertiesSet()
方法中,initExceptionhandlerAdviceCache()
时,从容器中获取被标注为ControllerAdvice
的bean,创建ExceptionHandlerMethodResolver
并缓存起来。
public void afterPropertiesSet() {
//从容器中获取被标注为ControllerAdvice的bean,创建ExceptionHandlerMethodResolver并缓存
this.initExceptionHandlerAdviceCache();
List handlers;
if (this.argumentResolvers == null) {
handlers = this.getDefaultArgumentResolvers();
this.argumentResolvers = (new HandlerMethodArgumentResolverComposite()).addResolvers(handlers);
}
if (this.returnValueHandlers == null) {
handlers = this.getDefaultReturnValueHandlers();
this.returnValueHandlers = (new HandlerMethodReturnValueHandlerComposite()).addHandlers(handlers);
}
}
private void initExceptionHandlerAdviceCache() {
if (this.getApplicationContext() != null) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Looking for exception mappings: " + this.getApplicationContext());
}
List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(this.getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
Iterator var2 = adviceBeans.iterator();
while(var2.hasNext()) {
ControllerAdviceBean adviceBean = (ControllerAdviceBean)var2.next();
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
if (resolver.hasExceptionMappings()) {
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
}
if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
this.responseBodyAdvice.add(adviceBean);
}
}
}
}
-
4.3 异常处理流程
doDispatch()方法中,当HandlerMethod抛出异常时,异常被catch,然后作为参数传入processDispatchResult()方法
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// 获取拦截器链和handler
mappedHandler = this.getHandler(processedRequest);
// ... 省略诸多代码
// 遍历interceptors, 调用interceptor.postHandle()
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// ... 省略诸多代码
// 遍历interceptors, 调用interceptor.postHandle()
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
// 捕获异常并处理
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
processDispatchResult()方法通过processHandlerException完成异常处理,逻辑是:遍历handlerExceptionResolvers,依次处理。
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response, HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
mv = ((ModelAndViewDefiningException)exception).getModelAndView();
} else {
Object handler = mappedHandler != null ? mappedHandler.getHandler() : null;
mv = this.processHandlerException(request, response, handler, exception);
errorView = mv != null;
}
}
}
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
ModelAndView exMv = null;
Iterator var6 = this.handlerExceptionResolvers.iterator();
while(var6.hasNext()) {
HandlerExceptionResolver handlerExceptionResolver = (HandlerExceptionResolver)var6.next();
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
// ...
}
每个resolver的解决思路是:判断当前resolver是否适用于该handler和request,适用则进行解析。
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (this.shouldApplyTo(request, handler)) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Resolving exception from handler [" + handler + "]: " + ex);
}
this.prepareResponse(ex, response);
ModelAndView result = this.doResolveException(request, response, handler, ex);
if (result != null) {
this.logException(ex, request);
}
return result;
} else {
return null;
}
}
doResolveException()根据当前handler对象和exception对象去exceptionHandlerCache中找到对应的ExceptionHandlerMethodResolver,拿到要被调用的Method对象,然后完成该方法的调用,返回modelAndView.
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
if (handler != null) {
Method handlerMethod = this.findBestExceptionHandlerMethod(handler, ex);
if (handlerMethod != null) {
ServletWebRequest webRequest = new ServletWebRequest(request, response);
try {
Object[] args = this.resolveHandlerArguments(handlerMethod, handler, webRequest, ex);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Invoking request handler method: " + handlerMethod);
}
Object retVal = this.doInvokeMethod(handlerMethod, handler, args);
return this.getModelAndView(handlerMethod, retVal, webRequest);
} catch (Exception var9) {
this.logger.error("Invoking request method resulted in exception : " + handlerMethod, var9);
}
}
}
return null;
}
整体流程简单来说就是:
1.在启动过程中扫描ExceptionHandlerExceptionResolver接口的实现和被@ControllerAdvice注解的Bean,创建ExceptioResolver对象。
2.捕获到controller中某方法的异常,遍历所有的ExceptioResolver,根据出现异常的方法handler和exception从异常处理Bean的缓存中找到相对应的methodResolver,解析出与(handler和exception)相对应的异常处理方法,并调用该方法对象完成异常处理。
参考:
SpringMVC解读系列