本文共 9063 字,大约阅读时间需要 30 分钟。
目录
第七章 Web开发
7.9 错误处理
7.9.1 默认规则
参考:
- 默认情况下,SpringBoot提供一个映射/error,处理所有错误
-
对于机器客户端,它将生成JSON响应,其中包含错误、HTTP状态和异常消息的详细信息,将这些信息显示在页面上
-
对于浏览器客户端,响应一个"whitelabel(白页)"错误视图,以HTML格式呈现相同的数据
-
要进行自定义,可以添加View解析为error
-
要完全替换默认行为,可以实现ErrorController,并注册该类型的Bean定义,或者添加ErrorAttributes类型的组件,以实现使用现有机制替换其内容
-
public/templates下的error文件夹下的4xx、5xx页面会被自动解析
-
7.9.2 定制错误处理逻辑
- 自定义错误页
- error/404.html error/5xx.html:有精确的错误状态码页面就匹配精确,没有就找 4xx.html,如果都没有就触发白页
- @ControllerAdvice+@ExceptionHandler处理全局异常,底层是处理器异常解析器ExceptionHandlerExceptionResolver支持的
/** * 处理整个Web controller的异常 */@Slf4j@ControllerAdvicepublic class GlobalExceptionHandler { @ExceptionHandler({ArithmeticException.class,NullPointerException.class}) //处理异常 public String handleArithException(Exception e){ log.error("异常是:{}",e); return "login";//返回一个视图地址 }}
- @ResponseStatus+自定义异常:底层是ResponseStatusExceptionResolver,把responsestatus注解的信息底层调用response.sendError(statusCode, resolvedReason);tomcat发送的/error
@ResponseStatus(value = HttpStatus.FORBIDDEN,reason = "用户数量太多") //可以传入Http状态码、错误原因public class UserTooMuchException extends RuntimeException{//自定义异常类 public UserTooMuchException() { } public UserTooMuchException(String message) { super(message); }}
-
Spring底层的异常,如参数类型转换异常、参数不存在异常。由处理器异常解析器DefaultHandlerExceptionResolver处理框架底层的异常
- response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());
- 自定义实现HandlerExceptionResolver处理异常,实现HandlerExceptionResolver接口,修改优先级后可以作为默认的全局异常处理规则
@Order(value = Ordered.HIGHEST_PRECEDENCE)//给当前异常解析器一个最高优先级,超过之前的DefaultHandlerExceptionResolver@Componentpublic class CustomerHandlerExceptionResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { try{ response.sendError(511,"自定义的错误状态码");//也会被5xx.html页面处理 } catch (IOException e) { e.printStackTrace(); } return new ModelAndView(); }}
- ErrorViewResolver,实现自定义处理异常(最底层的一种异常处理)
- response.sendError,error请求就会转给controller
- 你的异常没有其他任何人能处理,tomcat底层response.sendError,error请求就会转给controller
-
basicErrorController要去的页面地址是ErrorViewResolver
7.9.3 异常处理自动配置原理
-
ErrorMvcAutoConfiguration:自动配置异常处理规则
-
- 容器中的组件:类型:DefaultErrorAttributes ---> id:errorAttributes
- public class DefaultErrorAttributes implements ErrorAttributes, HandlerExceptionResolver
- DefaultErrorAttributes:定义错误页面中可以包含哪些数据
- 容器中的组件:类型:DefaultErrorAttributes ---> id:errorAttributes
-
- 容器中的组件:类型:BasicErrorController ---> id:basicErrorController(这个控制器就是用来响应json或者响应白页的 适配响应)
- 处理默认/error路径的请求,页面响应new ModelAndView("error", model);
- 容器中的组件:类型:BasicErrorController ---> id:basicErrorController(这个控制器就是用来响应json或者响应白页的 适配响应)
-
-
- 容器中有组件View->id是error(响应默认错误页 白页)
-
-
-
- 容器中放组件BeanNameViewResolver(视图解析器),按照返回的视图名error作为组件的id去容器中找View对象
- 如果想要返回页面;就会找error视图【StaticView】(默认是一个白页)
- 容器中放组件BeanNameViewResolver(视图解析器),按照返回的视图名error作为组件的id去容器中找View对象
-
-
- 容器中的组件:类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
- 如果发生错误,会以HTTP的状态码作为视图页地址(viewName),找到真正的页面
- error/404、5xx.html
- 容器中的组件:类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
7.9.4 异常处理步骤流程
- 执行目标方法,目标方法运行期间有任何异常都会被catch,而且标志当前请求结束,并且用 dispatchException进行封装
-
进入视图解析流程(页面渲染) processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
-
mv = processHandlerException(); 处理handler发生的异常,处理完成返回ModelAndView
-
-
遍历所有的handlerExceptionResolvers,看谁能处理当前异常【HandlerExceptionResolver处理器异常解析器】
-
-
-
系统默认的异常解析器
-
-
-
-
DefaultErrorAttributes先来处理异常,把异常信息保存到request域,并且返回null
-
默认没有任何人能处理异常,所以异常会被抛出
-
如果没有任何人能处理最终底层就会发送/error请求,会被底层的BasicErrorController处理
-
解析错误视图:遍历所有的ErrorViewResolver看谁能解析。
-
-
-
-
-
-
-
默认的DefaultErrorViewResolver,作用是把响应状态码作为错误页的地址,比如error/500.html
-
模板引擎最终响应这个页面error/500.html
-
-
-
7.10 Web原生组件注入(Servlet、Filter、Listener)
7.10.1 使用Servlet API
- @ServletComponentScan+@WebServlet:前者注解在主配置类上,后者注解在自定义Servelt类上
@ServletComponentScan(basePackages = "com.xxy.springbootproject")//指定原生Servlet组件都放在哪里@SpringBootApplicationpublic class SpringbootProjectApplication { public static void main(String[] args) { SpringApplication.run(SpringbootProjectApplication.class, args); }}
@WebServlet(urlPatterns = "/my")public class MyServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.getWriter().write("66666"); }}
- @WebFilter
@Slf4j@WebFilter(urlPatterns = {"/css/*","/images/*"})public class MyFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { log.info("MyFilter初始化完成"); } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { log.info("MyFilter工作"); filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { log.info("MyFilter销毁"); }}
- @WebListener
@Slf4j@WebListenerpublic class MyServletContextListener implements ServletContextListener { @Override public void contextInitialized(ServletContextEvent sce) { log.info("MyServletContextListener监听到项目初始化完成"); } @Override public void contextDestroyed(ServletContextEvent sce) { log.info("MyServletContextListener监听到项目销毁"); }}
7.10.2 使用RegistrationBean
除了使用上述的注解的方式,还可以使用下面这种方式:
- ServletRegistrationBean
- FilterRegistrationBean
- ServletListenerRegistrationBean
@Configuration(proxyBeanMethods = true)//保证依赖的组件始终是单实例的public class MyRegistConfig { @Bean public ServletRegistrationBean myServlet(){ MyServlet myServlet = new MyServlet(); //第一个参数传入自定义Servlet return new ServletRegistrationBean(myServlet,"/my","/my02");//可以多个路径 } @Bean public FilterRegistrationBean myFilter(){ MyFilter myFilter = new MyFilter();// return new FilterRegistrationBean(myFilter,myServlet()); //相当于myServlet()拦截哪些路径,整个过滤器就拦截哪些路径 FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter); filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*")); return filterRegistrationBean; } @Bean public ServletListenerRegistrationBean myListener(){ MyServletContextListener myServletContextListener = new MyServletContextListener(); return new ServletListenerRegistrationBean(myServletContextListener); }}
7.10.3 一个小问题
自己定义的Servlet的响应是/my,当直接访问/my时,会发现不会经过之前定义Spring的拦截器
MyServlet ---> /my
DispatcherServler ---> /
扩展:DispatchServlet如何实现注册的。
-
容器中自动配置了DispatcherServlet,属性绑定到WebMvcProperties,对应的配置文件配置项是spring.mvc
spring.mvc.servlet.path=/mvc/ 修改DispatcherServlet的映射路径
-
通过ServletRegistrationBean<DispatcherServlet>把DispatcherServlet配置进来
-
默认映射的是/路径
Tomcat-Servlet:MyServlet
多个Servlet都能处理到同一层路径,精确优先原则(最长前缀匹配原则)
7.11 嵌入式Web容器
7.11.1 切换嵌入式Servlet容器
- 默认支持的webServer
- Tomcat、Jetty、Undertow
- ServletWebServerApplicationContext容器启动寻找ServletWebServerFactory并引导创建服务器
- 切换服务器
- 想要使用什么服务器,就取出它的场景启动器
<dependency>
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <exclusions> <exclusion><!--排除web自动导入的tomcat场景-->
<groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency><!--再另外加入其他服务器的场景依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
- 原理
- SpringBoot应用启动发现当前是Web应用,web场景包导入tomcat
-
web应用会创建一个web版的ioc容器ServletWebServerApplicationContext
-
ServletWebServerApplicationContext启动的时候寻找ServletWebServerFactory(Servlet的web服务器工厂 ---> 生产Servlet的web服务器)
-
SpringBoot底层默认有很多的WebServer工厂
-
TomcatServletWebServerFactory
-
JettyServletWebServerFactory
-
UndertowServletWebServerFactory
-
-
底层直接会有一个自定配置类,ServletWebServerFactoryAutoConfiguration
-
ServletWebServerFactoryAutoConfiguration导入ServletWebServerFactoryConfiguration配置类
-
ServletWebServerFactoryConfiguration配置类动态判断到底导入了哪个web服务器的包,默认是web-starter场景导入tomcat包,容器中就有TomcatServletWebServerFactory
-
TomcatServletWebServerFactory创建出Tomcat服务器并启动,TomcatWebServer的构造器拥有初始化方法initialize---this.tomcat.start();
-
内嵌服务器,其实就是手动把启动服务器的代码调用(前提是服务器tomcat的核心jar包要存在)
7.11.2 定制Servlet容器
下面几种方法可以定制服务器,如端口号等等。
-
实现WebServerFactoryCustomizer<ConfigurableServletWebServerFactory>
- 把配置文件的值和ServletWebServerFactory进行绑定
- 修改配置文件server.xxx(推荐)
- 直接自定义ConfigurableServletWebServerFactory
import org.springframework.boot.web.server.WebServerFactoryCustomizer;import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory;import org.springframework.stereotype.Component;@Componentpublic class CustomizationBean implements WebServerFactoryCustomizer{ @Override public void customize(ConfigurableServletWebServerFactory server) { server.setPort(9000); }}
xxxxxCustomizer:定制化器,可以改变xxxx的默认规则
7.12 定制化原理
7.12.1 定制化的常见方式
-
修改配置文件
-
xxxxxCustomizer:定制化器
- 编写自定义的配置类xxxConfiguration+@Bean,替换或增加容器中默认组件
- Web应用 编写一个配置类实现WebMvcConfigurer,即可定制化web功能+@Bean给容器中再扩展一些组件
@Configurationpublic class AdminWebConfig implements WebMvcConfigurer
- @EnableWebMvc+配置类WebMvcConfigurer+@Bean可以全面接管SpringMVC,所有规则全部自己重新配置, 实现定制和扩展功能
- @EnableWebMvc会使WebMvcAutoConfiguration没有生效
7.12.2 原理分析套路
场景starter - xxxxAutoConfiguration(自动配置) - 导入xxx组件(@Bean) - 绑定xxxProperties(组件的默认属性) - 绑定配置文件项
PS:根据尚硅谷课程整理,如有侵权,联系删除
转载地址:https://blog.csdn.net/xxyneymar/article/details/122764273 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!