Spring MVC 소스 코드 (4) - 통일 이상 처리 원리 분석
SpringMVC 의 모든 이상 처 리 는 인터페이스 Handler Exception Resolver 를 통 해 이 루어 집 니 다. 인터페이스 에 한 가지 방법 만 정의 되 어 있 습 니 다.
public interface HandlerExceptionResolver {
ModelAndView resolveException(
HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex);
}
방법 에 서 는 request 와 response 정 보 를 받 아들 이 고 현재 처리 Handler 와 이상 한 대상 을 던 집 니 다.또한 추상 적 인 클래스 AbstractHandler Exception Resolver 를 제공 하여 resolve Exception 방법 을 실현 하고 선행 판단 과 처 리 를 지원 하 며 실제 처 리 를 doResolve Exception 방법 으로 하위 클래스 에서 추상 화 합 니 다.
@ Controller Advice 와 @ ExceptionHandler 의 간단 한 사용
@ControllerAdvice
public class ExceptionAdvice {
@ExceptionHandler({ArrayIndexOutOfBoundsException.class})
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseDTO handleArrayIndexOutOfBoundsException(ArrayIndexOutOfBoundsException e) {
// TODO log
e.printStackTrace();
ResponseDTO responseDTO = new ResponseDTO();
responseDTO.wrapResponse(ServiceCodeEnum.E999997, " ");
return responseDTO;
}
@ExceptionHandler(value = ParamException.class)
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseDTO handleParamException(ParamException e) {
// TODO log
e.printStackTrace();
ResponseDTO responseDTO = new ResponseDTO();
responseDTO.wrapResponse(ServiceCodeEnum.E999998, " ");
return responseDTO;
}
@ExceptionHandler({Exception.class})
@ResponseBody
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResponseDTO handleException(Exception e) {
// TODO log
e.printStackTrace();
ResponseDTO responseDTO = new ResponseDTO();
responseDTO.wrapResponse(ServiceCodeEnum.E999999, " ");
return responseDTO;
}
}
어디 보 자. @ControllerAdvice
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class>[] basePackageClasses() default {};
Class>[] assignableTypes() default {};
Class extends Annotation>[] annotations() default {};
}
ControllerAdvice @Component , @ControllerAdvice
Spring mvc 의 설정 은 다음 과 같 습 니 다.
"1.0" encoding="UTF-8"?>
"http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:cxf="http://cxf.apache.org/core"
xmlns:p="http://cxf.apache.org/policy" xmlns:ss="http://www.springframework.org/schema/security"
xmlns:jaxws="http://cxf.apache.org/jaxws" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://cxf.apache.org/core http://cxf.apache.org/schemas/core.xsd
http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd
http://cxf.apache.org/policy http://cxf.apache.org/schemas/policy.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd
http://cxf.apache.org/bindings/soap http://cxf.apache.org/schemas/configuration/soap.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
scan
base-package="frame.web.controller;frame.web.advice" />
"jstlViewResolver"
class="org.springframework.web.servlet.view.UrlBasedViewResolver">
"order" value="1" />
"viewClass"
value="org.springframework.web.servlet.view.JstlView" />
"prefix" value="/WEB-INF/jsp/" />
"conversionService"
class="org.springframework.format.support.FormattingConversionServiceFactoryBean">
Annotation DrivenBean Definition Parser 류 는 탭 을 분석 하 는 데 사 용 됩 니 다.다음은 Annotation DrivenBean Definition Parser 의 일부 소스 코드 입 니 다.
package org.springframework.web.servlet.config;
class AnnotationDrivenBeanDefinitionParser implements BeanDefinitionParser {
/**
*parse , annotation-drive , spring ioc 。
**/
@Override
public BeanDefinition parse(Element element, ParserContext parserContext) {
Object source = parserContext.extractSource(element);
XmlReaderContext readerContext = parserContext.getReaderContext();
CompositeComponentDefinition compDefinition = new CompositeComponentDefinition(element.getTagName(), source);
parserContext.pushContainingComponent(compDefinition);
RuntimeBeanReference contentNegotiationManager = getContentNegotiationManager(element, source, parserContext);
// RequestMappingHandlerMapping,
RootBeanDefinition handlerMappingDef = new RootBeanDefinition(RequestMappingHandlerMapping.class);
handlerMappingDef.setSource(source);
handlerMappingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerMappingDef.getPropertyValues().add("order", 0);
handlerMappingDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
if (element.hasAttribute("enable-matrix-variables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enable-matrix-variables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
else if (element.hasAttribute("enableMatrixVariables")) {
Boolean enableMatrixVariables = Boolean.valueOf(element.getAttribute("enableMatrixVariables"));
handlerMappingDef.getPropertyValues().add("removeSemicolonContent", !enableMatrixVariables);
}
configurePathMatchingProperties(handlerMappingDef, element, parserContext);
readerContext.getRegistry().registerBeanDefinition(HANDLER_MAPPING_BEAN_NAME , handlerMappingDef);
RuntimeBeanReference corsConfigurationsRef = MvcNamespaceUtils.registerCorsConfigurations(null, parserContext, source);
handlerMappingDef.getPropertyValues().add("corsConfigurations", corsConfigurationsRef);
// ConversionService json,xml Spring mvc
RuntimeBeanReference conversionService = getConversionService(element, source, parserContext);
RuntimeBeanReference validator = getValidator(element, source, parserContext);
RuntimeBeanReference messageCodesResolver = getMessageCodesResolver(element);
RootBeanDefinition bindingDef = new RootBeanDefinition(ConfigurableWebBindingInitializer.class);
bindingDef.setSource(source);
bindingDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
bindingDef.getPropertyValues().add("conversionService", conversionService);
bindingDef.getPropertyValues().add("validator", validator);
bindingDef.getPropertyValues().add("messageCodesResolver", messageCodesResolver);
ManagedList> messageConverters = getMessageConverters(element, source, parserContext);
ManagedList> argumentResolvers = getArgumentResolvers(element, parserContext);
ManagedList> returnValueHandlers = getReturnValueHandlers(element, parserContext);
String asyncTimeout = getAsyncTimeout(element);
RuntimeBeanReference asyncExecutor = getAsyncExecutor(element);
ManagedList> callableInterceptors = getCallableInterceptors(element, source, parserContext);
ManagedList> deferredResultInterceptors = getDeferredResultInterceptors(element, source, parserContext);
//RequestMappingHandlerAdapter
RootBeanDefinition handlerAdapterDef = new RootBeanDefinition(RequestMappingHandlerAdapter.class);
handlerAdapterDef.setSource(source);
handlerAdapterDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
handlerAdapterDef.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
handlerAdapterDef.getPropertyValues().add("webBindingInitializer", bindingDef);
handlerAdapterDef.getPropertyValues().add("messageConverters", messageConverters);
addRequestBodyAdvice(handlerAdapterDef);
addResponseBodyAdvice(handlerAdapterDef);
if (element.hasAttribute("ignore-default-model-on-redirect")) {
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignore-default-model-on-redirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
else if (element.hasAttribute("ignoreDefaultModelOnRedirect")) {
// "ignoreDefaultModelOnRedirect" spelling is deprecated
Boolean ignoreDefaultModel = Boolean.valueOf(element.getAttribute("ignoreDefaultModelOnRedirect"));
handlerAdapterDef.getPropertyValues().add("ignoreDefaultModelOnRedirect", ignoreDefaultModel);
}
if (argumentResolvers != null) {
handlerAdapterDef.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
handlerAdapterDef.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
if (asyncTimeout != null) {
handlerAdapterDef.getPropertyValues().add("asyncRequestTimeout", asyncTimeout);
}
if (asyncExecutor != null) {
handlerAdapterDef.getPropertyValues().add("taskExecutor", asyncExecutor);
}
handlerAdapterDef.getPropertyValues().add("callableInterceptors", callableInterceptors);
handlerAdapterDef.getPropertyValues().add("deferredResultInterceptors", deferredResultInterceptors);
readerContext.getRegistry().registerBeanDefinition(HANDLER_ADAPTER_BEAN_NAME , handlerAdapterDef);
String uriCompContribName = MvcUriComponentsBuilder.MVC_URI_COMPONENTS_CONTRIBUTOR_BEAN_NAME;
RootBeanDefinition uriCompContribDef = new RootBeanDefinition(CompositeUriComponentsContributorFactoryBean.class);
uriCompContribDef.setSource(source);
uriCompContribDef.getPropertyValues().addPropertyValue("handlerAdapter", handlerAdapterDef);
uriCompContribDef.getPropertyValues().addPropertyValue("conversionService", conversionService);
readerContext.getRegistry().registerBeanDefinition(uriCompContribName, uriCompContribDef);
RootBeanDefinition csInterceptorDef = new RootBeanDefinition(ConversionServiceExposingInterceptor.class);
csInterceptorDef.setSource(source);
csInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, conversionService);
RootBeanDefinition mappedCsInterceptorDef = new RootBeanDefinition(MappedInterceptor.class);
mappedCsInterceptorDef.setSource(source);
mappedCsInterceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(0, (Object) null);
mappedCsInterceptorDef.getConstructorArgumentValues().addIndexedArgumentValue(1, csInterceptorDef);
String mappedInterceptorName = readerContext.registerWithGeneratedName(mappedCsInterceptorDef);
// ExceptionHandlerExceptionResolver,
RootBeanDefinition exceptionHandlerExceptionResolver = new RootBeanDefinition(ExceptionHandlerExceptionResolver.class);
exceptionHandlerExceptionResolver.setSource(source);
exceptionHandlerExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
exceptionHandlerExceptionResolver.getPropertyValues().add("contentNegotiationManager", contentNegotiationManager);
exceptionHandlerExceptionResolver.getPropertyValues().add("messageConverters", messageConverters);
exceptionHandlerExceptionResolver.getPropertyValues().add("order", 0);
addResponseBodyAdvice(exceptionHandlerExceptionResolver);
if (argumentResolvers != null) {
exceptionHandlerExceptionResolver.getPropertyValues().add("customArgumentResolvers", argumentResolvers);
}
if (returnValueHandlers != null) {
exceptionHandlerExceptionResolver.getPropertyValues().add("customReturnValueHandlers", returnValueHandlers);
}
String methodExceptionResolverName = readerContext.registerWithGeneratedName(exceptionHandlerExceptionResolver);
RootBeanDefinition responseStatusExceptionResolver = new RootBeanDefinition(ResponseStatusExceptionResolver.class);
responseStatusExceptionResolver.setSource(source);
responseStatusExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
responseStatusExceptionResolver.getPropertyValues().add("order", 1);
String responseStatusExceptionResolverName =
readerContext.registerWithGeneratedName(responseStatusExceptionResolver);
RootBeanDefinition defaultExceptionResolver = new RootBeanDefinition(DefaultHandlerExceptionResolver.class);
defaultExceptionResolver.setSource(source);
defaultExceptionResolver.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
defaultExceptionResolver.getPropertyValues().add("order", 2);
String defaultExceptionResolverName =
readerContext.registerWithGeneratedName(defaultExceptionResolver);
parserContext.registerComponent(new BeanComponentDefinition(handlerMappingDef, HANDLER_MAPPING_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(handlerAdapterDef, HANDLER_ADAPTER_BEAN_NAME));
parserContext.registerComponent(new BeanComponentDefinition(uriCompContribDef, uriCompContribName));
parserContext.registerComponent(new BeanComponentDefinition(exceptionHandlerExceptionResolver, methodExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(responseStatusExceptionResolver, responseStatusExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(defaultExceptionResolver, defaultExceptionResolverName));
parserContext.registerComponent(new BeanComponentDefinition(mappedCsInterceptorDef, mappedInterceptorName));
// Ensure BeanNameUrlHandlerMapping (SPR-8289) and default HandlerAdapters are not "turned off"
MvcNamespaceUtils.registerDefaultComponents(parserContext, source);
parserContext.popAndRegisterContainingComponent();
return null;
}
}
위의 코드 분석 을 통 해 우 리 는 Exception Handler Exception Resolver 라 는 종 류 를 찾 아 Spring MVC 의 각종 이상 을 처리 할 수 있 습 니 다. 그러면 Exception Handler Exception Resolver 는 구체 적 으로 Controller Advice 와 함께 사용 하여 각종 이상 을 처리 합 니까?Exception Handler Exception Resolver 의 핵심 코드 를 살 펴 보 겠 습 니 다.
package org.springframework.web.servlet.mvc.method.annotation;
// InitializingBean, Bean afterPropertiesSet()
public class ExceptionHandlerExceptionResolver extends AbstractHandlerMethodExceptionResolver
implements ApplicationContextAware, InitializingBean {
// map ControllerAdviceBean ExceptionHandlerMethodResolver
private final Map exceptionHandlerAdviceCache =
new LinkedHashMap();
// spring
@Override
public void afterPropertiesSet() {
// Do this first, it may add ResponseBodyAdvice beans
// ExceptionHandler
initExceptionHandlerAdviceCache();
if (this.argumentResolvers == null) {
List resolvers = getDefaultArgumentResolvers();
this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers);
}
if (this.returnValueHandlers == null) {
List handlers = getDefaultReturnValueHandlers();
this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers);
}
}
/**
* spring ioc @ControllerAdvice , @ExceptionHandler ExceptionHandlerMethodResolver exceptionHandlerAdviceCache map 。
**/
private void initExceptionHandlerAdviceCache() {
if (getApplicationContext() == null) {
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Looking for exception mappings: " + getApplicationContext());
}
// @ControllerAdvice
List adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext());
AnnotationAwareOrderComparator.sort(adviceBeans);
for (ControllerAdviceBean adviceBean : adviceBeans) {
// ControllerAdvice @ExceptionHandler , ExceptionHandlerMethodResolver map。
// ExceptionHandlerMethodResolver @ExceptionHandler ExceptionHandlerMethodResolver map
//key @ExceptionHandler(value = ParamException.class) value, ParamException.class, @ExceptionHandler Method
ExceptionHandlerMethodResolver resolver = new ExceptionHandlerMethodResolver(adviceBean.getBeanType());
if (resolver.hasExceptionMappings()) {
// @ExceptionHandler ,
// @ControllerAdvice Bean, Bean Exception key,Method value Map ExceptionHandlerMethodResolver exceptionHandlerAdviceCache
this.exceptionHandlerAdviceCache.put(adviceBean, resolver);
if (logger.isInfoEnabled()) {
logger.info("Detected @ExceptionHandler methods in " + adviceBean);
}
}
if (ResponseBodyAdvice.class.isAssignableFrom(adviceBean.getBeanType())) {
this.responseBodyAdvice.add(adviceBean);
if (logger.isInfoEnabled()) {
logger.info("Detected ResponseBodyAdvice implementation in " + adviceBean);
}
}
}
}
/**
** exceptionHandlerAdviceCache ,
*/
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
Class> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);
if (handlerMethod != null) {
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
}
for (Entry entry : this.exceptionHandlerAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
ExceptionHandlerMethodResolver resolver = entry.getValue();
// ,
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
}
}
}
return null;
}
}
어디 보 자. ExceptionHandlerMethodResolver 이런 종류
public class ExceptionHandlerMethodResolver {
public static final MethodFilter EXCEPTION_HANDLER_METHODS = new MethodFilter() {
public boolean matches(Method method) {
return AnnotationUtils.findAnnotation(method, ExceptionHandler.class) != null;
}
};
private static final Method NO_METHOD_FOUND = ClassUtils.getMethodIfAvailable(System.class, "currentTimeMillis", new Class[0]);
// Map @ControllerAdvice @ExceptionHandler , @ExceptionHandler value Exception Key, Method
private final Map, Method> mappedMethods = new ConcurrentHashMap(16);
private final Map, Method> exceptionLookupCache = new ConcurrentHashMap(16);
public ExceptionHandlerMethodResolver(Class> handlerType) {
// Class , @ControllerAdvice
Iterator var2 = MethodIntrospector.selectMethods(handlerType, EXCEPTION_HANDLER_METHODS).iterator();
// , @ExceptionHandler
while(var2.hasNext()) {
Method method = (Method)var2.next();
// @ExceptionHandler @ExceptionHandler value
// {ArrayIndexOutOfBoundsException.class,ParamException.calss}
Iterator var4 = this.detectExceptionMappings(method).iterator();
while(var4.hasNext()) {
Class extends Throwable> exceptionType = (Class)var4.next();
// ArrayIndexOutOfBoundsException.class key,method value map
this.addExceptionMapping(exceptionType, method);
}
}
}
private List> detectExceptionMappings(Method method) {
List> result = new ArrayList();
// @ExceptionHandler @ExceptionHandler value
// {ArrayIndexOutOfBoundsException.class,ParamException.calss}
this.detectAnnotationExceptionMappings(method, result);
if (result.isEmpty()) {
Class[] var3 = method.getParameterTypes();
int var4 = var3.length;
for(int var5 = 0; var5 < var4; ++var5) {
Class> paramType = var3[var5];
if (Throwable.class.isAssignableFrom(paramType)) {
result.add(paramType);
}
}
}
Assert.notEmpty(result, "No exception types mapped to {" + method + "}");
return result;
}
protected void detectAnnotationExceptionMappings(Method method, List> result) {
// @ExceptionHandler, null, ExceptionHandler
ExceptionHandler ann = (ExceptionHandler)AnnotationUtils.findAnnotation(method, ExceptionHandler.class);
// ExceptionHandler , , , @ExceptionHandler({ArrayIndexOutOfBoundsException.class,ParamException.calss})
result.addAll(Arrays.asList(ann.value()));
}
}
이상 처리 원리
SpringMVC 는 어떻게 처 리 를 요청 하 는 과정 에서 이상 에 대한 통일 처 리 를 완성 합 니까?우 리 는 소스 코드 에서 깊이 해석 했다.
Dispatcher Servlet 로 돌아 가 는 doDispatcher 방법
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// Actually invoke the handler.
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);
요청 처리 의 핵심 처리 에 큰 try / catch 를 사용 하 는 것 을 볼 수 있 습 니 다. 이상 이 발생 하면 dispatchException 으로 일괄 적 으로 패키지 하여 processDispatchResult 방법 으로 처리 합 니 다.프로 세 스 Dispatch Result 방법 은 보기 로 돌아 가 는 것 을 조작 하 는 동시에 이상 도 통일 적 으로 처리 하 는 것 을 알 고 있 습 니 다.
processDispatch Result 에 서 는 우선 이상 을 판단 합 니 다.
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
특별한 Model AndView Defining Exception 이 아니라면 processHandler Exception 에서 작 동 합 니 다.
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
// ,
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
// ,
if (exMv != null) {
if (exMv.isEmpty()) {
request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
우 리 는 주로 이상 프로세서 의 이상 처리 에 관심 을 가지 고 있 습 니 다. SpringMVC 는 Handler Exception Resolver 의 resolveException 호출 을 통 해 실현 류 의 실제 실현 방법 doResolveException 을 호출 합 니 다.
추천 블 로그
프로그래머 가 코드 를 쓰 는 것 외 에 어떻게 임금 을 한 몫 더 벌 수 있 습 니까?
ExceptionHandlerExceptionResolver
ExceptionHandler ExceptionResolver 는 @ ExceptionHandler 주해 의 실현 을 지원 합 니 다.추상 적 인 기본 클래스 인 AbstractHandler MethodException Resolver 는 AbstractHandler Exception Resolver, doResolve Exception 방법 을 계승 하여 Exception Handler Exception Resolver 의 doResolve Handler MethodException 방법 을 실제 호출 합 니 다.
protected final ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
return this.doResolveHandlerMethodException(request, response, (HandlerMethod)handler, ex);
}
protected ModelAndView doResolveHandlerMethodException(HttpServletRequest request,
HttpServletResponse response, HandlerMethod handlerMethod, Exception exception) {
// HandlerMethod exception Method
ServletInvocableHandlerMethod exceptionHandlerMethod = getExceptionHandlerMethod(handlerMethod, exception);
if (exceptionHandlerMethod == null) {
return null;
}
//
exceptionHandlerMethod.setHandlerMethodArgumentResolvers(this.argumentResolvers);
exceptionHandlerMethod.setHandlerMethodReturnValueHandlers(this.returnValueHandlers);
ServletWebRequest webRequest = new ServletWebRequest(request, response);
ModelAndViewContainer mavContainer = new ModelAndViewContainer();
//
try {
if (logger.isDebugEnabled()) {
logger.debug("Invoking @ExceptionHandler method: " + exceptionHandlerMethod);
}
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("Failed to invoke @ExceptionHandler method: " + exceptionHandlerMethod, invocationEx);
}
// Continue with default processing of the original exception...
return null;
}
//
if (mavContainer.isRequestHandled()) {
return new ModelAndView();
}
else {
ModelMap model = mavContainer.getModel();
HttpStatus status = mavContainer.getStatus();
ModelAndView mav = new ModelAndView(mavContainer.getViewName(), model, status);
mav.setViewName(mavContainer.getViewName());
if (!mavContainer.isViewReference()) {
mav.setView((View) mavContainer.getView());
}
if (model instanceof RedirectAttributes) {
Map flashAttributes = ((RedirectAttributes) model).getFlashAttributes();
request = webRequest.getNativeRequest(HttpServletRequest.class);
RequestContextUtils.getOutputFlashMap(request).putAll(flashAttributes);
}
return mav;
}
}
우리 가 주로 주목 하 는 것 은 어떻게 이상 처리 방법 에 일치 하 는 지, 즉 Exception Handler Exception Resolver 의 getExceptionHandlerMethod
protected ServletInvocableHandlerMethod getExceptionHandlerMethod(HandlerMethod handlerMethod, Exception exception) {
Class> handlerType = (handlerMethod != null ? handlerMethod.getBeanType() : null);
// Controller Method,
if (handlerMethod != null) {
ExceptionHandlerMethodResolver resolver = this.exceptionHandlerCache.get(handlerType);
if (resolver == null) {
resolver = new ExceptionHandlerMethodResolver(handlerType);
this.exceptionHandlerCache.put(handlerType, resolver);
}
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(handlerMethod.getBean(), method);
}
}
// ControllerAdvice Method
// , @ControllerAdvice , @ExceptionHandler exceptionHandlerAdviceCache
// @ControllerAdvice
for (Entry entry : this.exceptionHandlerAdviceCache.entrySet()) {
if (entry.getKey().isApplicableToBeanType(handlerType)) {
// ExceptionHandlerMethodResolver, @ExceptionHandler Key Excpthion,value Method Map
ExceptionHandlerMethodResolver resolver = entry.getValue();
// ExceptionHandlerMethodResolver @ExceptionHandler
Method method = resolver.resolveMethod(exception);
if (method != null) {
return new ServletInvocableHandlerMethod(entry.getKey().resolveBean(), method);
}
}
}
return null;
}
public Method resolveMethod(Exception exception) {
Method method = this.resolveMethodByExceptionType(exception.getClass());
if (method == null) {
Throwable cause = exception.getCause();
if (cause != null) {
method = this.resolveMethodByExceptionType(cause.getClass());
}
}
return method;
}
public Method resolveMethodByExceptionType(Class extends Throwable> exceptionType) {
// exceptionLookupCache , , mappedMethods
Method method = (Method)this.exceptionLookupCache.get(exceptionType);
if (method == null) {
method = this.getMappedMethod(exceptionType);
this.exceptionLookupCache.put(exceptionType, method != null ? method : NO_METHOD_FOUND);
}
return method != NO_METHOD_FOUND ? method : null;
}
private Method getMappedMethod(Class extends Throwable> exceptionType) {
List> matches = new ArrayList();
// Key
Iterator var3 = this.mappedMethods.keySet().iterator();
while(var3.hasNext()) {
Class extends Throwable> mappedException = (Class)var3.next();
// exceptionType mappedException
if (mappedException.isAssignableFrom(exceptionType)) {
matches.add(mappedException);
}
}
if (!matches.isEmpty()) {
Collections.sort(matches, new ExceptionDepthComparator(exceptionType));
// Method
return (Method)this.mappedMethods.get(matches.get(0));
} else {
return null;
}
}
exception HandlerMethod 에 일치 하면 실행 방법 을 설정 한 다음 에 ServletInvocable HandlerMethod 의 invokeAndHandle 을 호출 하여 실행 합 니 다. 이 호출 과정 은 정상 적 인 요청 과 일치 합 니 다.
exceptionHandlerMethod.invokeAndHandle(webRequest, mavContainer, new Object[]{exception, cause, handlerMethod});
여 기 는 이상 처 리 를 호출 하 는 방법 입 니 다. 전체적으로 SpringMvc 가 시 작 될 때 이상 처 리 된 구성 요 소 를 초기 화 하 는 것 입 니 다. @Controller Advice 에 표 시 된 특수 클래스 와 @ ExceptionHandler 에 표 시 된 방법 을 캐 시 에 저장 합 니 다. 대상 Controller 에 이상 이 생 겼 을 때 던 진 이상 을 통 해 캐 시 에서 해당 하 는 처리 방법 을 찾 은 다음 해당 하 는 이상 처리 방법 을 호출 하면 됩 니 다.
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.