SpringMVC-尚硅谷笔记+自己的一些了解【下】
异常处理
HandlerExceptionResolver
ExceptionHandlerExceptionResolver
例子:
结果
解决:
/**
* 1. 在 @ExceptionHandler 方法的入参中可以加入 Exception 类型的参数, 该参数即对应发生的异常对象
* 2. @ExceptionHandler 方法的入参中不能传入 Map. 若希望把异常信息传导页面上, 需要使用 ModelAndView 作为返回值
* 3. @ExceptionHandler 方法标记的异常有优先级的问题.
* 4. @ControllerAdvice: 如果在当前 Handler 中找不到 @ExceptionHandler 方法来出来当前方法出现的异常,
* 则将去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常.
*/
@ExceptionHandler({ArithmeticException.class})
public ModelAndView handleArithmeticException(Exception ex){
System.out.println("出异常了: " + ex);
ModelAndView mv = new ModelAndView("error");
mv.addObject("exception", ex);
return mv;
}
结果:
此为局部异常,
如何定义一个全局异常?
@ControllerAdvice: 如果在当前 Handler 中找不到 @ExceptionHandler 方法来出来当前方法出现的异常, 则将去 @ControllerAdvice 标记的类中查找 @ExceptionHandler 标记的方法来处理异常.
ResponseStatusExceptionResolver
@ResponseStatus在类上时
结果:
在方法上的时候
结果:如果i==13,那么报的就是403用户名和密码不匹配的错误,如果正常,那么就会运行成功,报出404的错误。
ResponseStatusExceptionResolver源码解析
作用:
ResponseStatusExceptionResolver是HandleExceptionResolver的实现类,它使用@ResponseStatus注解把excetptions映射为HTTP的状态码
package org.springframework.web.servlet.mvc.annotation;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver;
public class ResponseStatusExceptionResolver extends AbstractHandlerExceptionResolver implements MessageSourceAware {
private MessageSource messageSource;
public ResponseStatusExceptionResolver() {
}
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
//获取ResponseStatus 的注解
ResponseStatus responseStatus = (ResponseStatus)AnnotationUtils.findAnnotation(ex.getClass(), ResponseStatus.class);
if (responseStatus != null) {
try {
//如果有这个注解,那么就调用resolveResponseStatus方法,就在下一个方法
return this.resolveResponseStatus(responseStatus, request, response, handler, ex);
} catch (Exception var7) {
this.logger.warn("Handling of @ResponseStatus resulted in Exception", var7);
}
}
return null;
}
protected ModelAndView resolveResponseStatus(ResponseStatus responseStatus, HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
int statusCode = responseStatus.value().value();//得到注解汇总的属性值,相当于注解中的value属性
String reason = responseStatus.reason();//得到注解汇总的属性值,相当于注解中的reason值
if (this.messageSource != null) {
reason = this.messageSource.getMessage(reason, (Object[])null, reason, LocaleContextHolder.getLocale());
}
if (!StringUtils.hasLength(reason)) {
response.sendError(statusCode);//如果原因reason为空,那么只放状态码
} else {
response.sendError(statusCode, reason);//不为空则全放
}
return new ModelAndView();
}
}
原理大概就是如果有@ResponseStatus注解,那么springmvc中的ResponseStatusExceptionResolver就会解析它,并将值放入客户端。
DefaultHandlerExceptionResolver
源码解析
作用:
DefaultHandlerExceptionResolver是HandleExceptionResolver的实现类,它可以处理spring的一些异常同时把这些异常转化成为HTTP的状态码
:
//只展示部分代码,doResolveException主要是显示了它能处理的异常
protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
try {
if (ex instanceof NoSuchRequestHandlingMethodException) {
return this.handleNoSuchRequestHandlingMethod((NoSuchRequestHandlingMethodException)ex, request, response, handler);
}
if (ex instanceof HttpRequestMethodNotSupportedException) {
return this.handleHttpRequestMethodNotSupported((HttpRequestMethodNotSupportedException)ex, request, response, handler);
}
if (ex instanceof HttpMediaTypeNotSupportedException) {
return this.handleHttpMediaTypeNotSupported((HttpMediaTypeNotSupportedException)ex, request, response, handler);
}
if (ex instanceof HttpMediaTypeNotAcceptableException) {
return this.handleHttpMediaTypeNotAcceptable((HttpMediaTypeNotAcceptableException)ex, request, response, handler);
}
if (ex instanceof MissingServletRequestParameterException) {
return this.handleMissingServletRequestParameter((MissingServletRequestParameterException)ex, request, response, handler);
}
if (ex instanceof ServletRequestBindingException) {
return this.handleServletRequestBindingException((ServletRequestBindingException)ex, request, response, handler);
}
if (ex instanceof ConversionNotSupportedException) {
return this.handleConversionNotSupported((ConversionNotSupportedException)ex, request, response, handler);
}
if (ex instanceof TypeMismatchException) {
return this.handleTypeMismatch((TypeMismatchException)ex, request, response, handler);
}
if (ex instanceof HttpMessageNotReadableException) {
return this.handleHttpMessageNotReadable((HttpMessageNotReadableException)ex, request, response, handler);
}
if (ex instanceof HttpMessageNotWritableException) {
return this.handleHttpMessageNotWritable((HttpMessageNotWritableException)ex, request, response, handler);
}
if (ex instanceof MethodArgumentNotValidException) {
return this.handleMethodArgumentNotValidException((MethodArgumentNotValidException)ex, request, response, handler);
}
if (ex instanceof MissingServletRequestPartException) {
return this.handleMissingServletRequestPartException((MissingServletRequestPartException)ex, request, response, handler);
}
if (ex instanceof BindException) {
return this.handleBindException((BindException)ex, request, response, handler);
}
if (ex instanceof NoHandlerFoundException) {
return this.handleNoHandlerFoundException((NoHandlerFoundException)ex, request, response, handler);
}
} catch (Exception var6) {
this.logger.warn("Handling of [" + ex.getClass().getName() + "] resulted in Exception", var6);
}
return null;
}
SimpleMappingExceptionResolver
该异常解析器会主动把异常放入request域中,这样就可以不用像@ExceptionHandler一样要使用ModelAndView来传递异常值。
源码如下
注意:this.exceptionAttribute的默认值是
如果需要修改可以在springmvc.xml中添加
SpringMVC运行流程(面试重点)
spring整合springmvc
若需要则要求spring整合springmvc
步骤如下:
1、创建UserService
@Service
public class UserService {
@Autowired
private HelloWorld helloWorld;
public UserService() {
System.out.println("UserService Constructor...");
}
}
2、添加HelloWorld的controller
@Controller
public class HelloWorld {
@Autowired
private UserService userService;
public HelloWorld() {
System.out.println("HelloWorld Constructor...");
}
@RequestMapping("/helloworld")
public String hello(){
System.out.println("success");
System.out.println(userService);
return "success";
}
}
3、修改web.xml
<!-- 配置启动 Spring IOC 容器的 Listener -->
<!-- needed for ContextLoaderListener -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:beans.xml</param-value>
</context-param>
<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
4、添加beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
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-4.0.xsd">
<context:component-scan base-package="com.atguigu.springmvc">
</context:component-scan>
<!-- 配置数据源, 整合其他框架, 事务等. -->
</beans>
结果,可以成功的访问,但是控制台输出两次结果
因为springmvc的配置文件springmvc.xml和spring的配置文件beans.xml都设置<context:component-scan base-package="com.atguigu.springmvc">
所以对UserService,HelloWorld里边的handler和service,bean每个容器都初始化一遍
问题: 若 Spring 的 IOC 容器和 SpringMVC 的 IOC 容器扫描的包有重合的部分, 就会导致有的 bean 会被创建 2 次.
解决:
1. 使 Spring 的 IOC 容器扫描的包和 SpringMVC 的 IOC 容器扫描的包没有重合的部分.
2. 使用 exclude-filter 和 include-filter 子节点来规定只能扫描的注解
第二种方法(使用 exclude-filter 和 include-filter 子节点来规定只能扫描的注解)
下方两个注解都是springmvc要扫描的
//在springmvc.xml里边添加
<context:component-scan base-package="com.atguigu.springmvc" use-default-filters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
//在beans.xml中添加
<context:component-scan base-package="com.atguigu.springmvc">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation"
expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
结果:
成功
Bean 被创建两次?
面试题:我们在Service层能不能注入Controller的Bean?
springboot可以,ssm不行,除非手动配置,但是不推荐,比较约定大于配置