SpringBoot使用WebMvcCongigurer自动配置类实现视图控制器/拦截器/跨域,以及自动配置类的实现原理

457 阅读7分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第8天,点击查看活动详情

一、通过覆盖bean来实现自动配置

上篇文章主要介绍了springboot的自动配置类:WebMvcAutoConfiguration,里面设置了很多自动配置的相关信息。在其中标记了很多ConditionalOnMissingBean(xxxxx.class)(意思就是如果容器中没有,当前的@bean才会生效)。 只需要在自己的配置类中配置对应的一个@Bean就可以覆盖默认自动配置。 还得结合源码的实际功能进行定制。

举例来说明:比如在自动配置类中有如下配置

image.png 其中代码:@ConditionalOnMissingBean(HiddenHttpMethodFilter.class)表示如果在IOC容器中没有HiddenHttpMethodFilter.class才会执行这个方法,如果我们想覆盖这个方法就创建一个返回HiddenHttpMethodFilter的bean即可,简单演示一下

image.png

通过以上配置,就会走我们自己定义的Bean,不会在运行自动配置类中的Bean了。

二、通过WebMvcConfigurer进行扩展

通过WebMvcConfigurer,我们可以对视图控制器、拦截器、全局CORS进行扩展。 首先创建一个配置类,并实现WebMvcConfigurer接口,通过重写这个接口的方法,我们就可以对相关功能进行扩展,如下:
可以看到WebMvcConfigurer里面提供了如下的接口 image.png

2-1、视图控制器的处理

如下图,通过重写addViewControllers,就可以设置控制器的访问和视图,相当于在springmvc稳重中说的 <mvc:view-controller path="/myCon" view-name="hello" />

image.png

2-2、拦截器的处理

拦截器在实际项目中用处也颇多,比如记录一些请求响应数据,或者记录请求和响应时间方便后期代码优化等,下面我们就做一个简单的请求接口时间的计算。

2-2-1、创建拦截器

主要记录了程序运行的时长

package com.jony.interceptor;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.time.LocalDateTime;

public class TimeInterceptor implements HandlerInterceptor {
    LocalDateTime begin;
    Logger logger = LoggerFactory.getLogger(TimeInterceptor.class);

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 开始时间
        begin = LocalDateTime.now();
        logger.info("当前请求:" + request.getRequestURI() + ";开始时间:" + begin);
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        // 结束时间
        LocalDateTime end = LocalDateTime.now();
        // 计算2个时间差
        Duration between = Duration.between(begin, end);
        // 获得相差毫秒
        long l = between.toMillis();
        logger.info("当前请求:" + request.getRequestURI() + ";执行时间:" + l + "毫秒");
    }
}

2-2-2、使用配置类添加拦截器

需要注意的是,我们自己创建的自动配置类一定要添加@Configuration注解。首先拦截所有请求 image.png

访问一下查看日志:

image.png

访问静态资源看日志:

image.png

如果我们不想拦截静态资源,只需要在拦截器中进行配置即可
只需要配置一下excludePathPatterns然后设置排除拦截请求的路径即可。

image.png

如果拦截路径较多,我们可以对代码进行一下优化,如下:
添加了拦截路径的数组和不拦截路径的数组。

image.png

2-3、Cors请求的处理

在日常开发中,对于前后端分离的项目,经常设计到联调开发,前端在访问后端服务的时候就会设计到跨域请求,默认系统是不支持跨域请求的,处理跨域请求有很多解决方式,下面来介绍使用自动配置类,如何解决跨域。

2-3-1、通过给方法添加@CrossOrigin实现跨域

在springboot中,我们可以在控制器方法上添加@CrossOrigin实现跨域

image.png

进入这个注解,可以看到里面设置了默认的运行域、Header等相关信息,这样我们给方法直接添加这个注解,所有的域名和Header就都可以访问了。

image.png

2-3-2、springboot实现全局跨域设置

2-3-2-1、设置跨域url及Method

在自动配置类中重写跨域的方法,如下运行所有请求,GET/POST/PUT/DELETE跨域

/**
 * 处理跨域请求
 * @param registry
 */
@Override
public void addCorsMappings(CorsRegistry registry) {
    registry.addMapping("/**") //映射服务中哪些http运行跨域访问
            .allowedMethods("GET","POST","PUT","DELETE"); //配置运行跨域的请求方法。
}

2-3-2-2、设置跨域允许的origin

通过设置allowedOrigins设置固定origin跨域

image.png

2-3-2-3、跨域的其他设置

image.png

allowCredentials
响应头表示是否可以将对请求的响应暴露给页面。返回true则可以,其他值均不可以.出于安全原因,浏览器禁止Ajax调用驻留在当前原点之外的资源。例如,当你在一个标签中检查你的银行账户时,你可以在另一个选项卡上拥有EVILL网站。 来自EVILL的脚本不能够对你的银行API做出Ajax请求(从你的帐户中取出钱!)使用您的凭据。 allowCredentials="true"其实是配置了CrossOrigin中的DEFAULT_ALLOWED_HEADERS:允许跨域传输所有的header参数,将用于使用token放入header域做session共享的跨域请求

三、WebMvcConfigurer原理

3-1、自动配置类加载实现原理过程

实现WebMvcConfigurer接口可以扩展Mvc实现, 又既保留SpringBoot的自动配置

1.在WebMvcAutoConfiguration 也有一个实现了WebMvcConfigurer的配置类

image.png 2.WebMvcAutoConfigurationAdapter 它也是利用这种方式去进行扩展, 所以我们通过查看这个类我们发现它帮我们实现了其他不常用的方法,帮助我们进行自动配置,我们只需定制(拦截器、视图控制器、CORS 在开发中需要额外定制的定制的功能)

可以看到上面的类有一个注解:@Import(EnableWebMvcConfiguration.class),我们进入这个注解

image.png 这个类有实现了DelegatingWebMvcConfiguration,再次进入这个类

image.png 上图EnableWebMvcConfiguration它的父类上 setConfigurers 使用@Autowired

  • 它会去容器中将所有实现了WebMvcConfigurer 接口的Bean都自动注入进来, 添加到configurers 变量中(也就是上面我们自己写的自动配置类,里面设置了拦截器、跨域相关方法都会注入进来)

再次进入addWebMvcConfigurers这个方法,将配置类添加到delegates委派器中

image.png

比如我们举例拿拦截器来看一下

image.png

进入这个addInterceptors方法 image.png

实际上底层调用WebMvcConfigurer对应的方法时 就是去拿到之前注入到delegates的WebMvcConfigurer ,依次调用

通过上面这些我们可以知道:不论是我们自己创建的自动配置类还是系统内置的自动配置了,只要添加了@Configuration注解就会帮我们注入到IOC容器中,就会将实现了WebMvcConfigurer接口的的所有Bean,使用刚刚看到setConfigurers方法添加到deleates委派器中。然后循环这个委派器,依次调用

3-2、@EnableWebMvc的使用注意事项

另外需要注意的是,如果实现了WebMvcConfigurer,就不能添加@EnableWebMvc注解,可以看下官网中的说明

image.png

那为什么不能添加呢?
因为当添加了@EnableWebMvc就不会使用SpringMVC自动配置类的默认配置就失效了

首先我们先来测试一下,给我们创建的自动配置类添加上@EnableMvc这个注解

image.png 因为添加了@EnableMvc这个注解,那么springboot自己的默认配置局失效了,而我们创建的自动配置了,没有对静态资源进行配置,因此访问一下之前可以访问的静态资源,就可以证明了,如下静态资源已经不能访问,证明此时springboot里面自己的自动配置类失效了。

image.png

3-2-1、@EnableMvc注解的探究

首先进入这个注解

image.png

其他注解都比较熟悉,不再进行介绍,唯独import了DelegatingWebMvcConfiguration,再次进入这个类

image.png

可以看到这个类继承了WebMvcConfigurationSupport

再次回到springboot的自动配置类:WebMvcAutoConfiguration类

image.png 看到如上标记的内容了,它的意思就是如果在IOC容器中没有找到WebMvcConfigurationSupport.class,当前类才有效。

因此:

  • 在WebMvcAutoConfiguration中@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
    • 当容器中不存在WebMvcConfigurationSupport 这个Bean的时候当前自动配置类才会生效
    • 正因为通过@EnableWebMvc 导入了DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport 从而才使自动配置类失效