SpringMVC如何扩展以及原理

本文解决了一个在Spring Boot项目中常见的问题:配置MVC拦截器后静态资源失效。通过深入分析Spring Boot自动配置源码,提供了两种有效解决方案,帮助开发者避免此类陷阱。

自己踩到了一个很大的坑,这篇博客对这个大坑做一记录,希望对大家有所帮助。

1.静态资源失效问题

我在做登录的时候,实现了资源映射,MVC拦截器去拦截请求,但是配置之后,静态资源失效了。
代码如下:

@Configuration
public class MyMvcConfig extends WebMvcConfigurationSupport {
    @Override
    protected void addViewControllers(ViewControllerRegistry registry) {
        //super.addViewControllers(registry);
        //浏览器发送/pxj请求来到success页面
        //registry.addViewController("/pxj").setViewName("success");
        registry.addViewController("/").setViewName("login");
        registry.addViewController("/index.html").setViewName("login");
        registry.addViewController("/main.html").setViewName("index");
    }
    
    @Override
    protected void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index.html","/user/login","/","/**/*.css",
                "/**/*.js","/static/**");
    }    
}

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getSession().getAttribute("loginUser");
        if(user == null){
            //未登录,返回登录页
            System.out.println("没有权限请先登录");
            request.setAttribute("msg","没有权限请先登录");
            request.getRequestDispatcher("/index.html").forward(request,response);
            return false;
        }else{
            //已登录,放行请求
            return true;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

那么为什么会出现这种情况呢?一起看下SpringBoot自动配置源码:
WebMvcAutoConfiguration

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({Servlet.class,DispatcherServlet.class,WebMvcConfigurer.class })
@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE + 10)
@AutoConfigureAfter({ DispatcherServletAutoConfiguration.class, 
TaskExecutionAutoConfiguration.class,ValidationAutoConfiguration.class })
public class WebMvcAutoConfiguration {

上述代码中有@ConditionalOnMissingBean(WebMvcConfigurationSupport.class)这局,意思是:若是存在WebMvcConfigurationSupport类型的类,则自动配置失效。

========================================================

上面了解SpringMVC为何失效的原因,那么我们应该怎么进行扩展呢?
第一种方法:继承WebMvcConfigurationAdapter
WebMvcConfigurationAdapter 在spring boot 2.0被废弃以后,所以我们在这里就不用这个方法了。
第二种方法:实现WebMvcConfigurer

@Configuration
public class MvcConfig implements WebMvcConfigurer {
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/").setViewName("/login");
    }
	......
}

这样就不会影响自动配置了。那么为什么可以这样配置呢?再来看下WebMvcAutoConfiguration源码

以SpringBoot2.2.6来看下这样配置的原理。
1.springMVC自动配置类,若是WebMvcConfigurationSupport这个类型的类不存在,则自动配置生效。

@Configuration(proxyBeanMethods = false)
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
@ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
@AutoConfigureOrder(-2147483638)
@AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {

2.上面类中有如下配置类

@Configuration(proxyBeanMethods = false)
public static class EnableWebMvcConfiguration extends
DelegatingWebMvcConfiguration implements ResourceLoaderAware {

3.DelegatingWebMvcConfiguration 类型的类实现了这个WebMvcConfigurationSupport接口

@Configuration(proxyBeanMethods = false)
public class DelegatingWebMvcConfiguration extends WebMvcConfigurationSupport {

4.DelegatingWebMvcConfiguration类中有如下方法。注入所有的WebMvcConfigurer类型的类,将所有的WebMvcConfigurer相关的配置都来一起调用;

@Autowired(required = false)
public void setConfigurers(List<WebMvcConfigurer> configurers) {
    if (!CollectionUtils.isEmpty(configurers)) {
        this.configurers.addWebMvcConfigurers(configurers);
    }
}

5.WebMvcConfigurerComposite实现类这个类,依次调用所有的WebMvcConfigurer类型的类。

class WebMvcConfigurerComposite implements WebMvcConfigurer {
    public void addViewControllers(ViewControllerRegistry registry) {
    Iterator var2 = this.delegates.iterator();
    while(var2.hasNext()) {
        WebMvcConfigurer delegate = (WebMvcConfigurer)var2.next();
        delegate.addViewControllers(registry);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值