SpringBoot2(2)应用篇 web开发

SpringBoot2系列:

SpringBoot2(1) 基础篇

SpringBoot2(3)应用篇 数据访问 单元测试Junit5 指标监控Actuator 高级特性profile

 

1、SpringMVC自动配置概览

可以先复习SpringMVC:https://blog.csdn.net/chengqingshihuishui/article/details/112758387

SpringBoot已经帮我们自动配置好了SpringMVC的常用开发场景.(大多场景我们都无需自定义配置)

The auto-configuration adds the following features on top of Spring’s defaults:

(1)内容协商视图解析器(ContentNegotiatingViewResolver)和BeanName视图解析器(BeanNameViewResolver

(2)静态资源(包括webjars)

(3)自动注册 Converter,GenericConverter,Formatter

(4)支持 HttpMessageConverters (后来我们配合内容协商理解原理)

(5)自动注册 MessageCodesResolver (国际化用)

(6)静态index.html 页支持

(7)自定义 Favicon  

(8)自动使用 ConfigurableWebBindingInitializer ,(DataBinder负责将请求数据绑定到JavaBean上)

注意:

If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own @Configuration class of type WebMvcConfigurer but without @EnableWebMvc.

不用@EnableWebMvc注解。使用@Configuration+WebMvcConfigurer自定义规则

If you want to provide custom instances of RequestMappingHandlerMapping, RequestMappingHandlerAdapter, or ExceptionHandlerExceptionResolver, and still keep the Spring Boot MVC customizations, you can declare a bean of type WebMvcRegistrations and use it to provide custom instances of those components.

声明WebMvcRegistrations改变默认底层组件

If you want to take complete control of Spring MVC, you can add your own @Configuration annotated with @EnableWebMvc, or alternatively add your own @Configuration-annotated DelegatingWebMvcConfiguration as described in the Javadoc of @EnableWebMvc.

使用@EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC

2、简单功能分析

2.1 静态资源访问

(1)静态资源放置位置(开发中放置位置):

静态资源需要放在类路径下的:/static (or /public or /resources or /META-INF/resources

"classpath:/META-INF/resources/"

"classpath:/resources/"

"classpath:/static/"

"classpath:/public"

"/":当前项目的根路径

特别说明:resources本身就是类路径之一  resources/resources 就是指类路径下的resources

Web浏览器访问地址(部署后,web浏览器访问):当前项目根路径/ + 静态资源名

原理(见上图):静态映射/**(拦截了所有) 

请求进来,先去找Controller看能不能处理。不能处理的所有请求又都交给静态资源处理器。静态资源也找不到则响应404页面

注意:如果对默认静态资源路径不感兴趣,修改静态资源路径方法(yml语法)

spring:
  mvc:
    static-path-pattern: /res/**

  resources:
    static-locations: [classpath:/haha/]

(2)静态资源访问前缀(默认无前缀)

spring:
  mvc:
    static-path-pattern: /res/**

当前项目 + static-path-pattern + 静态资源名 = 静态资源文件夹下找

(3)webjar

webjars:以jar包的形式引入静态资源  比如引入jQuery,就可以直接下载jquery.jar来用了。

https://www.webjars.org/ 可以直接搜索想要的jar包,复制坐标,交给maven部署就是了。

这个方法下载的jar包,会被SpringBoot自动放置在SpringBoot要求的目录下(即自动对应SpringBoot资源映射)

exp:下载jquery(进入https://www.webjars.org/    找到jquery如下图所示,粘贴到项目的pom.xml中)

下载好后,会自动存到lib下的以下文件夹。

项目会自动映射/webjars/**  路径

所以:web浏览器访问地址:

http://localhost:8080/webjars/jquery-ui/1.12.1/jquery-ui.js   后面地址要按照依赖里面的包路径

2.2 主页(index.html)

将  index.html 放到前文提及的静态资源路径下,即可被正常访问

  • 可以配置静态资源路径
  • 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
spring:
#  mvc:
#    static-path-pattern: /res/**   这个会导致welcome page功能失效

  resources:
    static-locations: [classpath:/haha/]
  • controller能处理/index

2.3 自定义favicon

就是给项目网页上弄个小图标

favicon.ico 放在静态资源目录下即可。

3、请求参数处理

3.1 rest风格

① REST风格,就是一种风格的URL,用这种风格设计请求的url,正在越来越流行。REST理论认为网络上的所有试题都是资源(一张图片,一段文本,一种服务等等)。这样每种资源对应一个特定的URI。要获取资源,访问它的URI就可以了。即URI为每一个资源的独一无二的识别符

② 锁定好资源后,如何操作它呢,常规操作就是增删改查,根据请求方式不同(GET,POST,PUT,DELETE),代表要做不同的操作

总结:所谓REST风格,就是 锁定资源 + 操作方式(GET,POST,PUT,DELETE) 两部分组成

特别注意:由于浏览器表单只支持 GET 与 POST 请求,而DELETE、PUT 等 method 并不支持,Spring3.0 添加了一个过滤器HiddenHttpMethodFilter,可以将这些请求转换为标准的 http 方法,使得支持 GET、POST、PUT 与DELETE 请求。

springboot中的过滤器HiddenHttpMethodFilter注解版配置方法:

    @Bean
    @ConditionalOnMissingBean(HiddenHttpMethodFilter.class) //没有下面的自定义,才用这个
    @ConditionalOnProperty(prefix = "spring.mvc.hiddenmethod.filter", name = "enabled", matchIfMissing = false)
    public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
        return new OrderedHiddenHttpMethodFilter();
    }


//自定义filter
    @Bean
    public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
        HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
        methodFilter.setMethodParam("_m");
        return methodFilter;
    }
补充:@ConditionalOnMissingBean 注解(符合下面的属性,配置就生效):
  • prefix:application.properties配置的前缀
  • name:属性是从application.properties配置文件中读取属性值
  • havingValue:配置读取的属性值跟havingValue做比较,如果一样则返回true,否则返回false;如果返回值为false,则该configuration不生效;为true则生效
  • matchIfMissing = true:表示如果没有在application.properties设置该属性,则默认为条件符合
Springboot小工具,配置文件中:
spring:
  mvc:
    hiddenmethod:
      filter:
        enabled: true   #开启页面表单的Rest功能

这段代码直接开启Rest功能,无序转换器,要转换器再写第二处代码。

第二处代码相当于:

原xml版本的 HiddenHttpMethodFilter的配置方法,tomcat配置文件中web.xml中

<!--filter 标签用于配置一个Filter 过滤器-->
<filter>
        <!--给filter 起一个别名-->
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <!--配置filter 的全类名-->
        <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
 
 
 <filter-mapping>
        <filter-name>HiddenHttpMethodFilter</filter-name>
        <url-pattern>/*</url-pattern>
</filter-mapping>

转换原理:

Rest原理(表单提交要使用REST的时候)

Step1:表单提交会带上_method=PUT

Step2:请求过来被HiddenHttpMethodFilter拦截

      请求是否正常,并且是POST

           获取到_method的值(兼容以下请求;PUT.DELETE.PATCH)。

           原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。

           过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。

配置完成后,前端 和 服务端代码写法:

前端:

<body>
  <form action="springmvc/testRest/1" method="post">
    <input type="hidden" name="_method" value="PUT">
    <input type="submit" value="TestRest PUT">
  </form>
 
  <form action="springmvc/testRest/1" method="post">
    <input type="hidden" name="_method" value="DELETE">
    <input type="submit" value="TestRest DELETE">
  </form>
 
  <form action="springmvc/testRest" method="post">
    <input type="submit" value="TestRest POST">
  </form>
  <a href="springmvc/testRest/1">Test Rest Get</a>
</body>

服务端

package com.atguigu.springmvc.handlers
 
@RequestMapping("/springmvc")
@Controller
public class SpringMVCTest{
  private static final String SUCCESS="success";
 
  
  @RequestMapping(value="/testRest/{id}",method=RequestMethod.PUT)
  public String testRestPut(@PathVariable Integer id){
    System.out.println("testRest Put:"+id);
    return SUCCESS;
  }
 
  
  @RequestMapping(value="/testRest/{id}",method=RequestMethod.DELETE)
  public String testRestDelete(@PathVariable Integer id){
    System.out.println("testRest Delete:"+id);
    return SUCCESS;
  }
 
  @RequestMapping(value="/testRest",method=RequestMethod.POST)
  public String testRest(){
    System.out.println("testRest POST");
    return SUCCESS;
  }
 
  @RequestMapping(value="/testRest/{id}",method=RequestMethod.GET)
  public String testRest(@PathVariable Integer id){
    System.out.println("testRest GET:"+id);
    return SUCCESS;
  }
 
 
  @RequestMapping(value="/testPathVariable/{id}")
  public String testPathVariable(@PathVariable("id") Integer id){
    System.out.println("testParamAndHeaders"+id);
    return SUCCESS;
  }
}

 

3.2 请求映射原理  比如 @RequestMapping(value="/testRest/{id}",method=RequestMethod.PUT)  

Springboot里面的新注解@GetMapping = @RequestMapping(...method=RequestMethod.GET

RequestMappingHandlerMapping:保存了所有@RequestMapping 和handler的映射规则。

  • SpringBoot自动配置欢迎页的 WelcomePageHandlerMapping 。访问 /能访问到index.html;
  • SpringBoot自动配置了默认 的 RequestMappingHandlerMapping
  • 请求进来,挨个尝试所有的HandlerMapping看是否有请求信息。

               如果有就找到这个请求对应的handler

               如果没有就是下一个 HandlerMapping

  • 我们需要一些自定义的映射处理,我们也可以自己给容器中放HandlerMapping。自定义 HandlerMapping

4、普通参数与基本注解(获取前端传来的参数)

4.1 注解

@PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody

Http协议URL的格式是:http://www.   路径+请求参数  

@pathVariable 获取路径变量

@RequestHeader获取请求头

@RequestParam获取请求参数(一个属性有多个值可以使用 ?inters=bask&inters=game)

@RequestBody获取表单提交内容

@RequestAttribute获取request域属性(HttpServetRequest在request域中添加属性)

@RestController
public class ParameterTestController {


    //  car/2/owner/zhangsan
    @GetMapping("/car/{id}/owner/{username}")
    public Map<String,Object> getCar(@PathVariable("id") Integer id,
                                     @PathVariable("username") String name,
                                     @PathVariable Map<String,String> pv,
                                     @RequestHeader("User-Agent") String userAgent,
                                     @RequestHeader Map<String,String> header,
                                     @RequestParam("age") Integer age,
                                     @RequestParam("inters") List<String> inters,
                                     @RequestParam Map<String,String> params,
                                     @CookieValue("_ga") String _ga,
                                     @CookieValue("_ga") Cookie cookie){


        Map<String,Object> map = new HashMap<>();

//        map.put("id",id);
//        map.put("name",name);
//        map.put("pv",pv);
//        map.put("userAgent",userAgent);
//        map.put("headers",header);
        map.put("age",age);
        map.put("inters",inters);
        map.put("params",params);
        map.put("_ga",_ga);
        System.out.println(cookie.getName()+"===>"+cookie.getValue());
        return map;
    }


    @PostMapping("/save")
    public Map postMethod(@RequestBody String content){
        Map<String,Object> map = new HashMap<>();
        map.put("content",content);
        return map;
    }


    //1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd
    //2、SpringBoot默认是禁用了矩阵变量的功能
    //      手动开启:原理。对于路径的处理。UrlPathHelper进行解析。
    //              removeSemicolonContent(移除分号内容)支持矩阵变量的
    //3、矩阵变量必须有url路径变量才能被解析
    @GetMapping("/cars/{path}")
    public Map carsSell(@MatrixVariable("low") Integer low,
                        @MatrixVariable("brand") List<String> brand,
                        @PathVariable("path") String path){
        Map<String,Object> map = new HashMap<>();

        map.put("low",low);
        map.put("brand",brand);
        map.put("path",path);
        return map;
    }

    // /boss/1;age=20/2;age=10

    @GetMapping("/boss/{bossId}/{empId}")
    public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
                    @MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
        Map<String,Object> map = new HashMap<>();

        map.put("bossAge",bossAge);
        map.put("empAge",empAge);
        return map;
    }
}

4.2 POJO对象封装    https://blog.csdn.net/chengqingshihuishui/article/details/112758387   5.4

4.3 使用 Servlet 原生API 作为spring容器函数调入参数  https://blog.csdn.net/chengqingshihuishui/article/details/112758387   6

4.4 参数处理原理(原理看看就是了,不是猛人就不用深究了)

  • HandlerMapping中找到能处理请求的Handler(Controller.method())
  • 为当前Handler 找一个适配器 HandlerAdapter; RequestMappingHandlerAdapter
  • 适配器执行目标方法并确定方法参数的每一个值
  • 处理派发结果

5、数据响应与内容协商:

5.1 响应JSON的解析器配置

5.1.1 jackson.jar+@ResponseBody

即可给前端自动返回json

配置方法:

Step1: maven引入依赖(都已经自动有了,不晓得为啥还有这么一节)

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
</dependency>
web场景自动引入了json场景
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-json</artifactId>
      <version>2.3.4.RELEASE</version>
      <scope>compile</scope>
</dependency>

5.1.2 返回值解析器 和 原理(原理看看就好,非猛人不用管)

(1)返回值处理器判断是否支持这种类型返回值 supportsReturnType

(2)返回值处理器调用 handleReturnValue 进行处理

(3)RequestResponseBodyMethodProcessor 可以处理返回值标了@ResponseBody 注解的。

 

    (3.1)利用 MessageConverters 进行处理 将数据写为json

        (3.1.1)内容协商(浏览器默认会以请求头的方式告诉服务器他能接受什么样的内容类型)

        (3.1.2)服务器最终根据自己自身的能力,决定服务器能生产出什么样内容类型的数据,

        (3.1.3)SpringMVC会挨个遍历所有容器底层的 HttpMessageConverter ,看谁能处理?

               ① 得到MappingJackson2HttpMessageConverter可以将对象写为json

               ② 利用MappingJackson2HttpMessageConverter将对象转为json再写出去。

5.1.3 SpringMVC支持的返回值

ModelAndView
Model
View
ResponseEntity 
ResponseBodyEmitter
StreamingResponseBody
HttpEntity
HttpHeaders
Callable
DeferredResult
ListenableFuture
CompletionStage
WebAsyncTask
有 @ModelAttribute 且为对象类型的
@ResponseBody 注解 ---> RequestResponseBodyMethodProcessor;

5.2 内容协商dataformat

根据客户端接收能力不同,返回不同媒体类型的数据。

5.2.1 引入maven

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-xml</artifactId>
</dependency>
  • 开启内容协商功能(配置文件yml写法)
spring:
    contentnegotiation:
      favor-parameter: true  #开启请求参数内容协商模式

浏览器分别发请求:

http://localhost:8080/test/person?format=json

http://localhost:8080/test/person?format=xml

5.2.2 postman分别测试返回json和xml

只需要改变请求头中Accept字段。Http协议中规定的,告诉服务器本客户端可以接收的数据类型。

确定客户端接收什么样的内容类型;

(1)Parameter策略优先确定是要返回json数据(获取请求头中的format的值)

(2)最终进行内容协商返回给客户端json即可。

5.2.3 内容协商原理(原理随便看看即可)

Step1、判断当前响应头中是否已经有确定的媒体类型。MediaType

Step2、获取客户端(PostMan、浏览器)支持接收的内容类型。(获取客户端Accept请求头字段)【application/xml】

          contentNegotiationManager 内容协商管理器 默认使用基于请求头的策略

          HeaderContentNegotiationStrategy  确定客户端可以接收的内容类型

Step3、遍历循环所有当前系统的 MessageConverter,看谁支持操作这个对象(Person)

Step4、找到支持操作Person的converter,把converter支持的媒体类型统计出来。

Step5、客户端需要【application/xml】。服务端能力【10种、json、xml】

Step6、进行内容协商的最佳匹配媒体类型

Step7、用 支持 将对象转为 最佳匹配媒体类型 的converter。调用它进行转化 。

5.2.5 自定义MessageConverter(对以上转换我都不满意,要自己转的话,非高手慎用)

主要目的:实现多协议数据兼容。json、xml、x-guigu(初学者一般不得用的)

Step1 @ResponseBody 响应数据出去 调用 RequestResponseBodyMethodProcessor 处理

Step2 Processor 处理方法返回值。通过 MessageConverter 处理

Step3 所有 MessageConverter 合起来可以支持各种媒体类型数据的操作(读、写)

Step4 内容协商找到最终的 messageConverter;

6、视图解析器与模板引擎  

6.1 视图解析

复习springmvc视图  https://blog.csdn.net/chengqingshihuishui/article/details/112858295

区别springmvc,Springboot默认不支持JSP,需要引入第三方模板引擎技术实现页面渲染

视图解析:

  • 返回值以 forward: 开始: new InternalResourceView(forwardUrl); -->  转发request.getRequestDispatcher(path).forward(request, response);
  • 返回值以redirect: 开始:new RedirectView() --》 render就是重定向
  • 返回值是普通字符串: new ThymeleafView()--->

6.2 模板引擎 Thymeleaf

https://blog.csdn.net/chengqingshihuishui/article/details/111406057  3 模板引擎Thymeleaf

主要会查手册。到时候用啥查啥,没必要去背

6.2.1 使用方法

通过maven引入thymeleaf的starter,会同时自动引入thymeleaf的自动配置类,并配置好

7、拦截器

7.1 HandlerInterceptor接口

一定先复习SpringMVC拦截器https://blog.csdn.net/chengqingshihuishui/article/details/113241501  4拦截器

/**
 * 登录检查
 * 1、配置好拦截器要拦截哪些请求
 * 2、把这些配置放在容器中
 */
@Slf4j
public class LoginInterceptor implements HandlerInterceptor {

    /**
     * 目标方法执行之前
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        String requestURI = request.getRequestURI();
        log.info("preHandle拦截的请求路径是{}",requestURI);

        //登录检查逻辑
        HttpSession session = request.getSession();

        Object loginUser = session.getAttribute("loginUser");

        if(loginUser != null){
            //放行
            return true;
        }

        //拦截住。未登录。跳转到登录页
        request.setAttribute("msg","请先登录");
//        re.sendRedirect("/");
        request.getRequestDispatcher("/").forward(request,response);
        return false;
    }

    /**
     * 目标方法执行完成以后
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        log.info("postHandle执行{}",modelAndView);
    }

    /**
     * 页面渲染以后
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        log.info("afterCompletion执行异常{}",ex);
    }
}

7.2 拦截器配置: SpringBoot和SpringMVC的主要差异,就是写配置

SpringMVC是xml中配置拦截器

Springboot直接写配置类

package com.atguigu.admin.config;

/**
 * 1、编写一个拦截器实现HandlerInterceptor接口
 * 2、拦截器注册到容器中(实现WebMvcConfigurer的addInterceptors)
 * 3、指定拦截规则【如果是拦截所有,静态资源也会被拦截】
 *
 * @EnableWebMvc:全面接管
 *      1、静态资源?视图解析器?欢迎页.....全部失效
 */
//@EnableWebMvc
@Configuration
public class AdminWebConfig implements WebMvcConfigurer{


    /**
     * Filter、Interceptor 几乎拥有相同的功能?
     * 1、Filter是Servlet定义的原生组件。好处,脱离Spring应用也能使用
     * 2、Interceptor是Spring定义的接口。可以使用Spring的自动装配等功能
     *
     */


    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/**")  //所有请求都被拦截包括静态资源
                .excludePathPatterns("/","/login","/css/**","/fonts/**","/images/**",
                        "/js/**","/aa/**"); //放行的请求
    }
}

7.3 拦截器原理(老样子,原理随便看看就好)

(1)根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】

(2)先来顺序执行 所有拦截器的 preHandle方法

  • 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
  • 2、如果当前拦截器返回为false。直接    倒序执行所有已经执行了的拦截器的  afterCompletion;

(3)如果任何一个拦截器返回false。直接跳出不执行目标方法

(4)所有拦截器都返回True。执行目标方法

(5)倒序执行所有拦截器的postHandle方法。

(6)前面的步骤有任何异常都会直接倒序触发 afterCompletion

(7)页面成功渲染完成以后,也会倒序触发 afterCompletion

8、文件上传

复习SpringMVC  https://blog.csdn.net/chengqingshihuishui/article/details/113241501  3、文件上传

8.1 文件上传代码

    /**
     * MultipartFile 自动封装上传过来的文件
     * @param email
     * @param username
     * @param headerImg
     * @param photos
     * @return
     */
    @PostMapping("/upload")
    public String upload(@RequestParam("email") String email,
                         @RequestParam("username") String username,
                         @RequestPart("headerImg") MultipartFile headerImg,
                         @RequestPart("photos") MultipartFile[] photos) throws IOException {

        log.info("上传的信息:email={},username={},headerImg={},photos={}",
                email,username,headerImg.getSize(),photos.length);

        if(!headerImg.isEmpty()){
            //保存到文件服务器,OSS服务器
            String originalFilename = headerImg.getOriginalFilename();
            headerImg.transferTo(new File("H:\\cache\\"+originalFilename));
        }

        if(photos.length > 0){
            for (MultipartFile photo : photos) {
                if(!photo.isEmpty()){
                    String originalFilename = photo.getOriginalFilename();
                    photo.transferTo(new File("H:\\cache\\"+originalFilename));
                }
            }
        }
        return "main";
    }

8.2 在SpringMVC中需要自己配置好组件,来实现上传功能

同样,Springboot中,又被自动配置好了。

  • 自动配置原理

文件上传自动配置类-MultipartAutoConfiguration-MultipartProperties:

(1)自动配置好了 StandardServletMultipartResolver   【文件上传解析器】

(2)原理步骤

① 请求进来使用文件上传解析器判断(isMultipart)并封装(resolveMultipart,返回MultipartHttpServletRequest)文件上传请求

② 参数解析器来解析请求中的文件内容封装成MultipartFile

③ 将request中文件信息封装为一个Map;MultiValueMap<String, MultipartFile>

FileCopyUtils。实现文件流的拷贝

9、异常处理:

先复习SpringMVC https://blog.csdn.net/chengqingshihuishui/article/details/113241501 5、异常处理

Springboot厉害的地方在于,又自动配置好了

9.1 默认处理方式

  • 默认情况下,Spring Boot提供/error处理所有错误的映射
  • 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据,显示分别如下图:

9.2 自定义错误处理逻辑

(1)要对其进行自定义,添加View解析error

(2)要完全替换默认行为,可以实现 ErrorController并注册该类型的Bean定义,或添加ErrorAttributes类型的组件以使用现有机制但替换其内容。

(3)error/下的4xx,5xx页面会被自动解析;

(4)自定义错误页

         error/404.html   error/5xx.html;有精确的错误状态码页面就匹配精确,没有就找 4xx.html;如果都没有就触发白页

(5)@ControllerAdvice+@ExceptionHandler处理全局异常;底层是 ExceptionHandlerExceptionResolver 支持的

(6)ResponseStatus+自定义异常 ;底层是 ResponseStatusExceptionResolver ,把responsestatus注解的信息底层调用response.sendError(statusCode, resolvedReason);tomcat发送的/error

(7)Spring底层的异常,如 参数类型转换异常;DefaultHandlerExceptionResolver 处理框架底层的异常。

        response.sendError(HttpServletResponse.SC_BAD_REQUEST, ex.getMessage());

(8)自定义实现 HandlerExceptionResolver 处理异常;可以作为默认的全局异常处理规则

(9)ErrorViewResolver  实现自定义处理异常;

  • response.sendError 。error请求就会转给controller
  • 你的异常没有任何人能处理。tomcat底层 response.sendError。error请求就会转给controller
  • basicErrorController 要去的页面地址是ErrorViewResolver  ;

9.3 异常处理自动配置原理 (原理就不说了,反正看着也烧脑子,暂时又没用)

9.4 异常处理步骤

(1)执行目标方法,目标方法运行期间有任何异常都会被catch、而且标志当前请求结束;并且用 dispatchException

(2)进入视图解析流程(页面渲染?)

           processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);

(3)mv = processHandlerException;处理handler发生的异常,处理完成返回ModelAndView;

    (3.1)遍历所有的 handlerExceptionResolvers,看谁能处理当前异常【HandlerExceptionResolver处理器异常解析器】

    (3.2)系统默认的  异常解析器DefaultErrorAttributes先来处理异常。把异常信息保存到request域,并且返回null;默认没有任何人能处理异常,所以异常会被抛出

           ① 如果没有任何人能处理最终底层就会发送 /error 请求。会被底层的BasicErrorController处理

           ② 解析错误视图;遍历所有的  ErrorViewResolver  看谁能解析。

           ③ 默认的 DefaultErrorViewResolver ,作用是把响应状态码作为错误页的地址,error/500.html

           ④  模板引擎最终响应这个页面 error/500.html

10、Tomcat原生三大组件注入(Servlet、Filter、Listener)

SpringMVC接管后可以用SpringMVC的强大功能开发web,但也不影响用原生Tomcat的组件

10.1 三大组件注入

三大组件有两种方式进行注入: API方式(推荐) 和 RegistrationBean方式

10.1.1 使用ServletAPI注入组件

@ServletComponentScan(basePackages = "com.atguigu.admin") :指定原生Servlet组件都放在那里

@WebServlet(urlPatterns = "/my"):效果:直接响应,(原生Servlet由filter进行过滤,不通过spring拦截器)

@WebFilter(urlPatterns={"/css/*","/images/*"})

@WebListener

多个Servlet都能处理到同一层路径,精确优选原则

A: /my/

B: /my/1  (这个更精确)

10.1.2 使用RegistrationBean注入组件(还是注解方式舒服)

ServletRegistrationBean, FilterRegistrationBean, and ServletListenerRegistrationBean

@Configuration
public class MyRegistConfig {

    @Bean
    public ServletRegistrationBean myServlet(){
        MyServlet myServlet = new MyServlet();

        return new ServletRegistrationBean(myServlet,"/my","/my02");
    }


    @Bean
    public FilterRegistrationBean myFilter(){

        MyFilter myFilter = new MyFilter();
//        return new FilterRegistrationBean(myFilter,myServlet());
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean(myFilter);
        filterRegistrationBean.setUrlPatterns(Arrays.asList("/my","/css/*"));
        return filterRegistrationBean;
    }

    @Bean
    public ServletListenerRegistrationBean myListener(){
        MySwervletContextListener mySwervletContextListener = new MySwervletContextListener();
        return new ServletListenerRegistrationBean(mySwervletContextListener);
    }
}

 

 

 

 

 

 

 


版权声明:本文为chengqingshihuishui原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。