SpringBoot入门(八)异常处理/Servlet组件注入/定制化
发布日期:2022-03-04 11:48:26 浏览次数:10 分类:技术文章

本文共 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:定义错误页面中可以包含哪些数据

    • 容器中的组件:类型:BasicErrorController ---> id:basicErrorController(这个控制器就是用来响应json或者响应白页的   适配响应)
      • 处理默认/error路径的请求,页面响应new ModelAndView("error", model);

      • 容器中有组件View->id是error(响应默认错误页   白页)

      • 容器中放组件BeanNameViewResolver(视图解析器),按照返回的视图名error作为组件的id去容器中找View对象
        • 如果想要返回页面;就会找error视图【StaticView】(默认是一个白页)

    • 容器中的组件:类型:DefaultErrorViewResolver -> id:conventionErrorViewResolver
      • 如果发生错误,会以HTTP的状态码作为视图页地址(viewName),找到真正的页面
      • error/404、5xx.html

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 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:力扣题151反转字符串里的单词
下一篇:SpringBoot入门(十)单元测试

发表评论

最新留言

不错!
[***.144.177.141]2024年04月20日 18时07分45秒

关于作者

    喝酒易醉,品茶养心,人生如梦,品茶悟道,何以解忧?唯有杜康!
-- 愿君每日到此一游!

推荐文章